|
|
|
|
Flight kinematics – Keyboard input through DirectInput |
Last chapter we actually made our airplane fly over our city, but without being able to control its direction, it’s not so much fun. So let’s first do something about that!
Let’s first start by reading keyboard input, exactly as we did in Series 1. So go on and add a reference to Microsoft.DirectX.DirectInput, or you may get errors like ‘The type or namespace name 'DirectInput' does not exist in the namespace 'Microsoft.DirectX' (are you missing an assembly reference?)’ when you put these lines at the bottom of your using-block:
using Microsoft.DirectX.DirectInput; using DI = Microsoft.DirectX.DirectInput;
Next create the variable to hold the link to the keyboard:
private DI.Device keyb;
And create the InitializeInputDevices method, exactly as we did in Series 1:
private void InitializeInputDevices() { keyb = new DI.Device(SystemGuid.Keyboard); keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive); keyb.Acquire(); }
Don’t forget to call this method from your Main method:
our_directx_form.InitializeInputDevices();
OK, with our keyboard set up, we can create a method that reads the keyboard input and, if needed, changes the angles of our airplane. Let’s first have a look how we will define the angles of our airplane:

Let’s explain the meaning of these angles. In an airplane, when you move to the left, your flaps will be adjusted so the plane rotates around the X axis displayed above. Then, when you pull the joystick to you, the nose of the plane will be lifted up, which corresponds to a rotation among the Y axis. If your plane already was tilted around the X axis, lifting the nose up will also result in a rotation around the Z axis.
The UpdatePosition method will read keyboard input and change the values of the angles accordingly:
private void ReadUserInput(float speed) { KeyboardState keys = keyb.GetCurrentKeyboardState(); if (keys[Key.Right]) { spacemeshangles.X += 2.5f * speed; } if (keys[Key.Left]) { spacemeshangles.X -= 2.5f * speed; } }
All this does, is rotating the plane around its Z axis when you push the left or right button. You’ll notice that this variation again depends on a speed variable. To try this, make sure this method is called every time your screen is updated, so put this line as the first line of your OnPaint method:
ReadUserInput(gamespeed);
Now run the program! Once again, you’ll have your plane flying in a straight line, but when you push the left or right arrow button on your keyboard, the plane will spin around its X axis.
Without being able to pull up the nose of the airplane, it’s still pretty hard not to crash of course. So let’s add this code to the ReadUserInput method:
if (keys[Key.Down]) { spacemeshangles.Z -= (float)(speed * Math.Sin(spacemeshangles.X)); spacemeshangles.Y -= (float)(speed * Math.Cos(spacemeshangles.X)); } if (keys[Key.Up]) { spacemeshangles.Z += (float)(speed * Math.Sin(spacemeshangles.X)); spacemeshangles.Y += (float)(speed * Math.Cos(spacemeshangles.X)); }
Pressing the down arrow will result in pulling up the nose of the airplane. If the rotation around the X axis is 0, this will only change the rotation around the Y axis, because sin(0) = 0. If, however, the rotation aroun the X axis is not 0, the rotation around the Z axis will also be changed, which makes your airplane actually ‘turn’.
Although these maths are the basics flight simulators rely on, it’s not 100% complete. For example, looping will cause the Math.Sin to switch from positive to negative, making your airplane fly backwards. Although 4 additional mathematical if-checks would do the trick, learning maths is not the goal of this Series. For anyone with a decent math background, it should be rather easy to construct these checks.
That’s it! Running this code should enable you to fligh around your 3D city, as demonstrated in the image below:

Click here to go to the forum on this chapter!
Or click on one of the topics on this chapter to go there: Flight cinematics Well, i spent around 4 hours and I did it.
I did ...At last fully controllable ship Using sin cos I had lots of problems, I started fl...advanced physics I am new to directx -- started it as a hobby, but ...Flying is all wrong :( Hi, first I like to say thank you for taking the t...4 if-checks Please could you post here those 4 if-checks you m...
Now you can fly your plane, maybe it’s to do some collision detection, because flying through walls really isn’t a natural thing to happen.
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using D3D = Microsoft.DirectX.Direct3D;
using Microsoft.DirectX.DirectInput; using DI = Microsoft.DirectX.DirectInput;
namespace DirectX_Tutorial { public class WinForm : System.Windows.Forms.Form { private int[,] int_Floorplan; private int WIDTH; private int HEIGHT; private int differentbuildings = 5; private float gamespeed = 0.04f; private int[] buildingheights = new int[] { 0, 10, 1, 3, 2, 5 }; private System.ComponentModel.Container components = null; private D3D.Device device; private Texture scenerytexture; private Material material; private CustomVertex.PositionNormalTextured[] verticesarray; ArrayList verticeslist = new ArrayList(); private Mesh spacemesh; private Material[] spacemeshmaterials; private Texture[] spacemeshtextures; private float spacemeshradius; private float scaling = 0.0005f; private Vector3 spacemeshposition = new Vector3(8, 2, 1); private Vector3 spacemeshangles = new Vector3(0, 0, 0);
private DI.Device keyb;
public WinForm() { InitializeComponent(); this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true); } public void InitializeDevice() { PresentParameters presentParams = new PresentParameters(); presentParams.Windowed = true; presentParams.SwapEffect = SwapEffect.Discard; presentParams.AutoDepthStencilFormat = DepthFormat.D16; presentParams.EnableAutoDepthStencil = true; device = new D3D.Device(0, D3D.DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams); device.RenderState.Lighting = true; device.Lights[0].Type = LightType.Directional; device.Lights[0].Diffuse = Color.White; device.Lights[0].Direction = new Vector3(1, 1, -1); device.Lights[0].Update(); device.Lights[0].Enabled = true; device.Lights[1].Type = LightType.Directional; device.Lights[1].Diffuse = Color.White; device.Lights[1].Direction = new Vector3(-1, -1, -1); device.Lights[1].Update(); device.Lights[1].Enabled = true; } protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
ReadUserInput(gamespeed);
UpdatePosition(ref spacemeshposition, spacemeshangles, gamespeed); device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.DarkSlateBlue, 1.0f, 0); device.BeginScene(); device.RenderState.Ambient = Color.DarkGray; device.Transform.World = Matrix.Translation(-(float)spacemeshposition.X, -(float)spacemeshposition.Y, -(float)spacemeshposition.Z) * Matrix.RotationYawPitchRoll((float)spacemeshangles.X, (float)spacemeshangles.Y, (float)spacemeshangles.Z); device.VertexFormat = CustomVertex.PositionNormalTextured.Format; device.SetTexture(0, scenerytexture); device.DrawUserPrimitives(PrimitiveType.TriangleList, verticeslist.Count / 3, verticesarray); device.Transform.World = Matrix.Scaling(scaling, scaling, scaling) * Matrix.RotationX((float)Math.PI / 2); DrawMesh(spacemesh, spacemeshmaterials, spacemeshtextures); device.EndScene(); device.Present(); this.Invalidate(); } private void SetUpCamera() { device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4, (float)this.Width / (float)this.Height, 0.3f, 500f); device.Transform.View = Matrix.LookAtLH(new Vector3(0, -1f, 0.2f), new Vector3(0, 0, 0), new Vector3(0, 0, 1)); } private void LoadTexturesAndMaterials() { material = new Material(); material.Diffuse = Color.White; material.Ambient = Color.White; device.Material = material; scenerytexture = TextureLoader.FromFile(device, "texturemap.jpg"); } private void LoadFloorplan() { WIDTH = 20; HEIGHT = 15; int_Floorplan = new int[,] { {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,1,1,0,0,0,1,1,0,0,1,0,1}, {1,0,0,1,1,0,0,0,1,0,0,0,1,0,1}, {1,0,0,0,1,1,0,1,1,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,1,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,1,1,0,0,0,1,0,0,0,0,0,0,1}, {1,0,1,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,1,0,0,0,0,0,0,0,0,1}, {1,0,0,0,0,1,0,0,0,1,0,0,0,0,1}, {1,0,1,0,0,0,0,0,0,1,0,0,0,0,1}, {1,0,1,1,0,0,0,0,1,1,0,0,0,1,1}, {1,0,0,0,0,0,0,0,1,1,0,0,0,1,1}, {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, }; Random random = new Random(); for (int x = 0; x < WIDTH; x++) { for (int y = 0; y < HEIGHT; y++) { if (int_Floorplan[x, y] == 1) { int_Floorplan[x, y] = random.Next(differentbuildings) + 1; } } } } private void LoadMesh(string filename, ref Mesh mesh, ref Material[] meshmaterials, ref Texture[] meshtextures, ref float meshradius) { ExtendedMaterial[] materialarray; mesh = Mesh.FromFile(filename, MeshFlags.Managed, device, out materialarray); if ((materialarray != null) && (materialarray.Length > 0)) { meshmaterials = new Material[materialarray.Length]; meshtextures = new Texture[materialarray.Length]; for (int i = 0; i < materialarray.Length; i++) { meshmaterials[i] = materialarray[i].Material3D; meshmaterials[i].Ambient = meshmaterials[i].Diffuse; if ((materialarray[i].TextureFilename != null) && (materialarray[i].TextureFilename != string.Empty)) { meshtextures[i] = TextureLoader.FromFile(device, materialarray[i].TextureFilename); } } } mesh = mesh.Clone(mesh.Options.Value, CustomVertex.PositionNormalTextured.Format, device); mesh.ComputeNormals(); VertexBuffer vertices = mesh.VertexBuffer; GraphicsStream stream = vertices.Lock(0, 0, LockFlags.None); Vector3 meshcenter; meshradius = Geometry.ComputeBoundingSphere(stream, mesh.NumberVertices, mesh.VertexFormat, out meshcenter) * scaling; } private void LoadMeshes() { LoadMesh("xwing.x", ref spacemesh, ref spacemeshmaterials, ref spacemeshtextures, ref spacemeshradius); } private void DrawMesh(Mesh mesh, Material[] meshmaterials, Texture[] meshtextures) { for (int i = 0; i < meshmaterials.Length; i++) { device.Material = meshmaterials[i]; device.SetTexture(0, meshtextures[i]); mesh.DrawSubset(i); } } private void UpdatePosition(ref Vector3 position, Vector3 angles, float speed) { Vector3 addvector = new Vector3(); addvector.X += (float)(Math.Sin(angles.Z)); addvector.Y += (float)(Math.Cos(angles.Z)); addvector.Z -= (float)(Math.Tan(angles.Y)); addvector.Normalize(); position += addvector * speed; } private void InitializeInputDevices() { keyb = new DI.Device(SystemGuid.Keyboard); keyb.SetCooperativeLevel(this, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive); keyb.Acquire(); }
private void ReadUserInput(float speed) { KeyboardState keys = keyb.GetCurrentKeyboardState(); if (keys[Key.Right]) { spacemeshangles.X += 2.5f * speed; } if (keys[Key.Left]) { spacemeshangles.X -= 2.5f * speed; } if (keys[Key.Down]) { spacemeshangles.Z -= (float)(speed * Math.Sin(spacemeshangles.X)); spacemeshangles.Y -= (float)(speed * Math.Cos(spacemeshangles.X)); } if (keys[Key.Up]) { spacemeshangles.Z += (float)(speed * Math.Sin(spacemeshangles.X)); spacemeshangles.Y += (float)(speed * Math.Cos(spacemeshangles.X)); } }
private void VertexDeclaration() { float imagesintexture = 1 + differentbuildings * 2; for (int x = 0; x < WIDTH; x++) { for (int y = 0; y < HEIGHT; y++) { int currentbuilding = int_Floorplan[x, y]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(0, 0, 1), (currentbuilding * 2 + 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, 0, 1), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y + 1, buildingheights[currentbuilding]), new Vector3(0, 0, 1), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y + 1, buildingheights[currentbuilding]), new Vector3(0, 0, 1), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, buildingheights[currentbuilding]), new Vector3(0, 0, 1), (currentbuilding * 2 + 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(0, 0, 1), (currentbuilding * 2 + 1) / imagesintexture, 1)); if (y > 0) { if (int_Floorplan[x, y - 1] != int_Floorplan[x, y]) { if (int_Floorplan[x, y - 1] > 0) { currentbuilding = int_Floorplan[x, y - 1]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(0, 1, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, 1, 0), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, 0f), new Vector3(0, 1, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, 1, 0), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(0, 1, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(0, 1, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); } if (int_Floorplan[x, y] > 0) { currentbuilding = int_Floorplan[x, y]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(0, -1, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, 0f), new Vector3(0, -1, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, -1, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(0, -1, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x + 1, y, buildingheights[currentbuilding]), new Vector3(0, -1, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(0, -1, 0), currentbuilding * 2 / imagesintexture, 0)); } } } if (x > 0) { if (int_Floorplan[x - 1, y] != int_Floorplan[x, y]) { if (int_Floorplan[x - 1, y] > 0) { currentbuilding = int_Floorplan[x - 1, y]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(1, 0, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, 0f), new Vector3(1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, buildingheights[currentbuilding]), new Vector3(1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, buildingheights[currentbuilding]), new Vector3(1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(1, 0, 0), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(1, 0, 0), currentbuilding * 2 / imagesintexture, 1)); } if (int_Floorplan[x, y] > 0) { currentbuilding = int_Floorplan[x, y]; verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, 0f), new Vector3(-1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(-1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, 0f), new Vector3(-1, 0, 0), currentbuilding * 2 / imagesintexture, 1)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y, buildingheights[currentbuilding]), new Vector3(-1, 0, 0), (currentbuilding * 2 - 1) / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, buildingheights[currentbuilding]), new Vector3(-1, 0, 0), currentbuilding * 2 / imagesintexture, 0)); verticeslist.Add(new CustomVertex.PositionNormalTextured(new Vector3(x, y + 1, 0f), new Vector3(-1, 0, 0), currentbuilding * 2 / imagesintexture, 1)); } } } } } verticesarray = (CustomVertex.PositionNormalTextured[])verticeslist.ToArray(typeof(CustomVertex.PositionNormalTextured)); } protected override void Dispose(bool disposing) { if (disposing) { if (components != null) { components.Dispose(); } } base.Dispose(disposing); } private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.Size = new System.Drawing.Size(500, 500); this.Text = "Riemer's DirectX Tutorial using C# -- Season 2"; } static void Main() { using (WinForm our_directx_form = new WinForm()) { our_directx_form.InitializeDevice(); our_directx_form.SetUpCamera(); our_directx_form.LoadFloorplan(); our_directx_form.VertexDeclaration(); our_directx_form.LoadTexturesAndMaterials(); our_directx_form.LoadMeshes(); our_directx_form.InitializeInputDevices(); Application.Run(our_directx_form); } } } }
- Website design & XNA + DirectX code : Riemer Grootjans - ©2003 - 2011 Riemer Grootjans
|
|
|
|
|