Free Web Hosting Provider - Web Hosting - E-commerce - High Speed Internet - Free Web Page
Search the Web

vbProgramming | Tutorials | DirectX | Matrices and Transformations
  vbProgramming 
Tutorials | DirectX - Matrices and Transformations
 
     

vbProgramming Home :: vbProgramming Forums :: Tutorials :: Contact :: Links 

 

 

This tutorial will teach you how to move stuff in Direct3D by way of Matrices and Transformations.

Introduction
The title is scary huh^^? This tutorial will start to confuse you a lot :D. Don't worry about it, I'll try to make this tutorial as easy as possible for you guys. Just please try to keep an open mind and a positive attitude (i.e. don't say "I give up" right when I give you an explanation).

Heh, lets start with a few Q and A to get the ball rolling.

Q&A
The obvious question:

"How do you move things in Direct3D??"

The n00b responses (which I expect from some of you at this stage):

"Well, the first thing to do is recreate the vertices - its kinda simple, if you want to move a square right, all you gotta do is this:
vertex(0) = new customvertex.positionTextured(<1 unit to the right>)
and do the same with the other vertices"


Sorry, wrong answer...  Direct3D doesn't allow this... why? Because you're changing data in the vertex buffer. The vertex buffer is loaded and ready to go (there is a way around this but this is not for now), and you can't change it!

Another n00b comes up with a response to that:

"Well all you gotta do is recreate the vertex buffer and load in a new set of vertices"

Well... yes that could work... but isn't that a TOTAL waste of memory? You're creating a TOTALLY new vertex buffer and you're filling it with ANOTHER set of vertices = lag fest & flicker mania. So .... that's wrong ;p.

And then a geek comes up with a response:

"Well, its rather simple actually, Direct3D has its own built-in functions for doing this, it involves a complex system of matrices. The world gets translated in the direction which you want your objects to move and the result is a mass-movement of all objects in that specified direction."

Uhh... yeah that's the correct answer..... In English please?? Here it is:

-Every object is in the WORLD. Just like humans are on the earth.
-These objects can't move on their own (by changing the vertices or recreating the vertex buffer..etc) because it would be too slow.... imagine moving a 3D Character this way...do you realize how many vertices we'd have to move in order to move him? Even if this was just 1 pixel to the right.. its a CRAZY way of doing this - again - this would lead to lag fest and LOTS of flicker ;p.

So, the ingenious Microsoft came up with a way of doing this:

Move the world to move an object.

Scary huh? Again, keep an optimistic outlook and an open mind towards this concept, it might seem weird but everything has its reasons.

If an object wants to move left... what do you do? Make the world move left! :-P. The same goes for the other directions.. whichever direction the object wants to go in, the world goes in.

So to move that high polygon 3D character model, you move the world rather than move each of the vertices over... you sort of understand what I mean?

How do we move the world?

Well... here's where it gets a bit confusing,
Microsoft stores the world in a Matrix which is stored in your Device. The process of moving the world is called Translating... there are other transformations as well, such as Rotation and Scaling, but Translating is just moving it in a direction.

Well here's how to translate the world 1 unit to the right.. assuming d3ddev is your device:

d3ddev.Transform.World = Matrix.Translation(1,0,0).
The 3 arguments are X, Y, and Z respectively. That 1 indicates that the world is being moved 1 unit to the right.

Now that our character has moved 1 unit to the right, how do we move him back to his original position?

d3ddev.Transform.World = Matrix.Translation(0,0,0)

Lets try experimenting with this.


The Programming Part

Open up the previous project (Rendering a Sprite)... Go to GameClass and then go to the RenderScene sub.

Right before Alex.Render, type in

d3ddev.transform.World = Matrix.Translation(30,0,0)

Run it...
edit: Before you run it, do 1 thing, it is an error on my part in the previous tutorial, in the Initialize sub in GameClass, please change
D3Ddev.Transform.View = Matrix.OrthoOffCenterLH(0, DP.Width, DP.Height, 0,
0, 10)
to
D3Ddev.Transform.View = Matrix.OrthoOffCenterLH(0, DP.Width, DP.Height, 0,
1, 10)

Due to some mathematical reason, the zNearPlane argument must be 1 (don't worry if you don't understand this line anyways, I will explain later in the tutorial.

The character just shifted 30 units to the right! Be sure to realize one thing, remember that the top left of the character is position (0,0,0), which is the top left of the screen? Well now, the point (0,0,0) has now shifted 30 units to the right, it is now where point (30,0,0) used to be.. the character's top left is still at point (0,0,0) ... and the top left of the screen is now -30,0,0 :) ... starting to understand a bit?

Now let's try something different.

How do we translate the world 1 unit to the right each frame [each time the object is rendered]?

Well, first of all, we would have d3ddev.transform.world = matrix.translation(1,0,0) right? How would we make it move 1 unit to the right of its previous position, meaning how do we make it keep moving right 1 unit??

We'd simply do this [replace this line with the translation(30,0,0) line]:

d3ddev.transform.world = Matrix.Multiply(d3ddev.transform.world, matrix.translation(1,0,0))

Don't worry, the line may seem tricky, but just replace that line with your other translation line and run it... So now the guy moves REALLY quickly across the screen. For those of you with fast computers, I'd suggest that you change the 1 into a 0.1 or a 0.01. Depending on the amount of FPS (Frames Per Second) you get, the faster he'll go.

So Multiply simply translates the world from the PREVIOUS position.. and without Multiply, it would translate the world from position (0,0,0)... got all that? If you don't - play around with it for a while and it might sink in :D.

Basic Controlled Movement using Matrix.Multiply and Matrix.Translation
Now that you've understood that (I don't expect you to understand it completely, but get the general gist of what it does), lets discuss a bit on how to move him.

The first response that people come up with is usually

"Simple, use the Multiply along with Translation to animate the character - except use variables... for example:

d3ddev.transform.world = Matrix.Multiply(d3ddev.transform.world, matrix.Translation(x,y,0)) since Z doesn't matter in a 2d game set it to 0. When the character presses (for example) right, set x = 1 so he can start animating, when there is no key pressed, set x = 0 so he can STOP animating"


- I'll tell you right now that this is the wrong way, but.. I like to teach people why we do things as opposed to simply how we do things... let's experiment:

In GameClass:
Public X As Single
Public Y As Single

Replace the translation line (the one before Alex.Render - you should only have 1 "Translation line") with:

d3ddev.transform.world = Matrix.Multiply(d3ddev.transform.world, matrix.Translation(x,y,0))

Now go back to form1.. add the following code

If e.KeyCode = Keys.Right Then
  Game.X = 0.1
End If
 
If
e.KeyCode = Keys.Left Then
  Game.X = -0.1
End If
 
If
e.KeyCode = Keys.Up Then
  Game.Y = -0.1
End If
 
If
e.KeyCode = Keys.Down Then
  Game.Y = 0.1
End If


And in form1_keyup:
game.X = 0
game.Y = 0

Now test out the code :p...


You see that it works right? Here's my reasoning behind why we shouldn't do this... the Matrix.Multiply thing is based on time (FPS) because it moves the world based on frame count.. So basically, the slower the computer, the slower the guy will move :(.

Here's how it works... You move it .1 in whatever direction, lets say the Frames Per Second is 1 (REALLY bad lol), that means that it moves the world by .1 (in whatever direction) every second. Based on the FPS only you'll know how much you're actually moving the world by - if the FPS jumps to 2 (woohoo, a really small increase!) then he'll move twice as fast and you wouldn't be able to keep track of his position :(.

This leads to confusion and is something you probably want to avoid if you want to keep track of the character's position

So there's another technique which does not use multiply and translation, it just uses translation < I bet you guys now know what it is >

 

Basic Controlled Movement using Matrix.Translation
The other technique is really simple - We simply use Translation and use variables.

It works something like this [ replace the Multiply line with this ]
d3ddev.transform.world = matrix.Translation(x,y,0)

Now, the translation is not based on FRAME RATE, its simply based on the amount that X and Y is.

Go back to form 1 and revise the code in form1_keydown:


If e.KeyCode = Keys.Right Then
  Game.X += 1
End If
 
If e.KeyCode = Keys.Left Then
  Game.X -= 1
End If
 
If e.KeyCode = Keys.Up Then
  Game.Y -= 1
End If
 
If e.KeyCode = Keys.Down Then
  Game.Y += 1
End If


Its pretty simple, since its not based on frame rate, we can use "normal sized" amounts as opposed to using .01 or .1.

Oh yeah, delete the code in form1_keyup http://www.dotnetforums.net/images/smilies/wink.gif.

Now run the thing http://www.dotnetforums.net/images/smilies/biggrin.gif, it works. As a matter of fact, you might actually have to increase the 1 to maybe 5 or 10.


Well, here comes another question which people will ask:
Remember, you have to move the world to move an object...

"What if you had Two objects??? What if object1 wanted to go right and object2 wanted to go left? Oh no! is this a limitation of Direct3D????"

Of course its not ;P. This is a confusing explanation, and I gotta explain this through experimentation (and you'll probably understand it better that way).

Moving Multiple Objects using Matrix.Translation
Ok, let's get started on this confusing concept :).

In GameClass,
Private Alex2 As clsSprite

at the end of Initialize,
Alex2 = new clsSprite(d3ddev, "down1.bmp", new point(5,5), 32,32)

and in RenderScene after Alex.Render:
Alex2.Render

Run it, as expected, you should see both Alex and Alex2 moving together in the same direction because you're moving the WORLD...

Well, how do you move 2 objects at once? You translate the world again before rendering the 2nd object!

In GameClass,
Dim X2 as Single
Dim Y2 As Single

In form1_keydown, add this to your previously existing code:

If
e.KeyCode = Keys.W Then
 
Game.y2 -= 1
End If
 
If
e.KeyCode = Keys.S Then
 
Game.y2 += 1
End If
 
If
e.KeyCode = Keys.A Then
 
Game.x2 -= 1
End If
 
If
e.KeyCode = Keys.D Then
 
Game.x2 += 1
End
If

Ok, now before alex2.Render:
d3ddev.Transform.World = Matrix.Translation(x2,y2,0)

Now run it (don't worry if you don't understand the code yet, I'll explain soon), push W S A D and UP DOWN LEFT RIGHT to move the guys... they move SEPARATELY :).
----

Now its time for an explanation :D.

Here's how the program "reads" at the code

Translates the world to Alex's position
Renders Alex

Translates the world to Alex2's position
Renders Alex2

Its a weird concept to sink in :s, its really hard to explain.. but here's 1 other thing you should realize,

After you render Alex, any more translating after that won't really do anything to Alex's position... he's already been rendered, and he "stays" there :), so when you translate the world to alex2's position - you're not affecting Alex's position.

Experiment a bit with this, and please try to understand it before moving on, it took me a month to understand this concept (I learned DirectX right when it came out, I had no books and there was virtually no internet resources lol, I learned it with a few other people through trial and error... and then I picked up .NET Game Programming with DirectX 9 and it explained it all beautifully :D)

The View Matrix
Here is a question which I said I would answer in the previous tutorial (Rendering a Sprite).

I asked you earlier to type in the line D3Ddev.Transform.View = Matrix.OrthoOffCenterLH(0, DP.Width, DP.Height, 0, 1, 10) near the end of GameClass, and I told you I would explain it in the next tutorial.

The World matrix stores the objects, and the View matrix stores what you can see IN the world.

Here are arguments of the function
In this OrthoOffCenterLH View matrix, we define the left of the screen to have an x of 0, we define the right of the screen to be DP.Width (ex: if it was 800x600, the right of the screen would be 800), we define the bottom of the screen to be DP.Height (600 in our case), we define the Top of the screen to have a y of 0.

The next 2 arguments tell us zNear plane and zFar plane. Here's what those things do (its not important in 2d but it certainly is important in 3d):
-It basically says "If any object has a Z which is less than 1, don't render it, If any object has a Z greater than 10, don't render it". Its sort of a memory manager so that you don't render your ENTIRE world (imagine a HUGE 3d world), at the same time - you render it as you approach there.

If we HADN'T speficied the View matrix, it would be by default:
Left: -1
Right: 1
Top: -1
Bottom: 1
ZNearPlane: 1
ZFarPlane: <im not too sure, I think it may be 2>

This means that point (0,0,0) is the CENTER of the screen, we certainly don't want that to happen. This also means that we can only see a tiny section of the world (2 by 2)! I recommend that your View Matrix "matches" with your resolution (the OrthoOffCenterLH matrix defined in your program "matches" with your resolution)

Now to explain what OrthoOffCenterLH means, let's dissect it :D;

Ortho: No matter what Z value an object has, it's not going to matter - ideal for 2d, bad for 3d... the Z value of an object only does 1 thing: determines what object renders on top of the other object. If and object had a z value of 1 and an other object had a z value of 5, then the object with a zvalue of 1 would be rendered on top.

OffCenter: This means that the center of the screen isn't 0,0,0

LH: Left handed coordinate system, this means that the more Z an object has, the farther away it is. If we used RH, then the more Z an object has, the closer it is to the screen - this gets confusing because it's better to have a "Start Point" for where the beginning of the scene is - imagine taking the years in BC and switching it with the years in AD.... We would be counting DOWN :-P, so RH is a little weird, I prefer LH.

The reason why I said replace the 0 with a 1 < look way above, I'm talking about the ZNearPlane argument> was for mathematical reasons. Never have a zNear of 0 - I honestly don't know why/how, and I don't think it will affect this 2d program, but - always use good programming practices and fix your problems early rather than wondering why something was not working when you do it later.

That's all for this tutorial. Mess around with some stuff, and learn more - It took me a while to understand this concept and I learned it by messing around.

The Source Code for this tutorial is located here: You can also locate this by logging in to vbProgramming Forums and going to:
Tutorials > Tutorial Source Code > Source Code