|
Introduction
If you've been following along my
Direct3D tutorials you'll know that we loaded a cube in the previous
tutorial. Now let's start moving the cube. I highly suggest you
download the source code for the previous tutorial (see link at the
end of the page) and follow along, because that's just how I've
written this tutorial. If you've got a good imagination, then go
right ahead and follow this tutorial without .NET open, this might
save you time but you won't learn as much.
Let's
get started - open up the source code from the previous tutorial
Background
Information
We know that there are 3
matrices in Direct3D that the device uses:
World - This matrix contains information about the world, the
locations of all objects, etc.
View - This matrix tells Direct3D the location and orientation
of the camera
Projection - This matrix is basically how you see the
world. Think of it as tweaking a camera lens. For example: by
increasing the Field of View angle you can see more objects "around"
you.
3rd Person Pseudocode
Here's an example of how a 3rd person game might go. Just note you
don't need to type in any code (yet)
The character would be moved using the World matrix (ex: if you
wanted him to move forward, increase the Z value).
dxDevice.Transform.World =
Matrix.Translation(offsetX, offsetY, offsetZ)
In this case, if the character pushed Left then offsetX would have
to be decreased by a certain amount. Same goes with OffsetZ. In this
case, the offsetY would be the height of the ground (because the
character is always on top of the ground).
The camera would be moved using the View matrix, and would look at
the character at all times (the CameraTarget value of the LookAtLH
view matrix would be set to the character's position), and the
camera would be a certain distance away from the sprite.
dxDevice.Transform.View =
Matrix.LookAtLH(cameraPosition, characterPosition, New
Vector3(0,1,0))
Pretty much self explanatory, but we'll talk about calculating the
cameraPosition (which is based on the characterPosition) in a
different tutorial. The "New Vector3(0,1,0)" just symbolizes that Y
is the 'up' direction (See note below).
Note: Most 3D Modeling programs
use Z as the 'up' direction. You'll notice this when all 2d objects
are located on the XY plane, and anything 3D has a "Z" value. Most
DirectXers use Y as the Up direction. So if you were planning on
moving a character back and forth, you'd adjust the Z value. If you
were planning on going left and right, you'd adjust the X value. If
you were planning on jumping, you'd adjust the Y value.
The typical projection matrix looks like this:
dxDevice.Transform.Projection =
Matrix.PerspectiveFovLH(Math.PI / 4, DP.Width / DP.Height, 1.0F,
300.0F)
This was discussed in detail in a different tutorial. Just note the
arguments in order: Field of view, aspect ratio, zNear, zFar. DP is
the DisplayMode object.
1st Person Grounded
Pseudocode
First person is slightly
more confusing than 3rd person, but in a way it's a little simpler.
Here we're discussing first person "grounded" (meaning the character
is always on the ground).
In a first person game, YOU are
the camera. The camera position is the character position. The
camera target is determined by the left/right/up/down movements you
make on your mouse (we'll discuss this later, some trig is required
for this).
Basically what you want to do is translate the the world.
dxDevice.Transform.World =
Matrix.Translation(offsetX, offsetY, offsetZ)
For the view you figure out the forward direction (which
requires trig, as mentioned before, and will be discussed later)
dxDevice.Transform.View = Matrix.LookAtLH(characterPosition,
APointInFrontOfTheCharacter, new Vector3(0,1,0)) 'Pseudocode
And the projection is the same thing
D3Ddev.Transform.Projection = Matrix.PerspectiveFovLH(Math.PI / 4,
DP.Width / DP.Height, 1.0F, 300.0F)
1st Person Free-Fly
Pseudocode
Free-Fly is a bit more
confusing :).
In free fly, you move the mouse up down left right, but you can move
wherever you want. If you move the mouse up so that the camera is
facing 45 degrees up, and push "up" on your keyboard (or "W" or
whatever key you assigned), then the character will be moving
45-degrees forward.
Basically what you're doing here is adjusting your position based on
the direction you're facing (we'll call this the Forward direction).
What you want to do is, based on the left-right angle and the
up-down angle you want to calculate the direction you face (this is
the hard part), and move in that direction.
This
will definitely be discussed in-depth in a elsewhere, as this topic
requires a bit of math. Also the CameraUp won't always be (0,1,0)
because we'll be able to do a "backflip" and make it (0,-1,0)....and
other values as well!
Einstein's
Theory of Relativity
What you'll need is the universal constant:
Lambda,
and you'll need to make your world-view calculations based on this
constant, remembering that the universe is ever expandi---
Nah.
Just tried to scare you a bit.
You know when you're in a car, and the car next to you goes forward
but you feel like you're moving backwards? You'll need a grasp of
that concept.
The point I'm trying to get across is that you can't really design a
camera class without having anything to reference to. Say you were
making a 3rd person camera class, and the only object on the screen
was the character....since the camera always follows the character
and both the camera and the character move forward at the same time,
you won't feel like you're moving at all. Now if you were
walking on the ground, then you would feel as though you're actually
moving forward.
We'll need a reference object: a platform.
-------------
Click Here to download platform.x
and roadtexture.jpg
and save them into your Bin folder
(We're still using the previous
tutorial's source code)*
*I modeled the platform (which is why it looks messed up), and the
roadtexture.jpg came from a Microsoft sample.
*I also modeled the box that we used in the previous tutorial. You
can't really mess up a box, so I did well on that.... but I did
mess it up. I hate to say this, but I accidentally made the left
edge of the box at (0,0,0) instead of the center of the box being at
(0,0,0). So as you'll see in this tutorial, the box will look like
it's shifted to the right a bit.
-------------
Let's just mess around with code
for some time before we get to the real stuff :).
-Private Platform As clsMesh ' In Gameclass globals
-Platform = New clsMesh(D3Ddev, "platform.x", "roadtexture.jpg")
'Instantiate this right after you instantiate Car in Initialize()
-Platform.Render() 'Render this right after you do Car.Render in
RenderScene()
In the previous tutorial I do believe I added a "bonus" for
this tutorial that manipulated the world using the YawPitchAndRoll
Matrix:
"D3Ddev.Transform.World = Matrix.Multiply(D3Ddev.Transform.World,
Matrix.RotationYawPitchRoll(Math.PI / 10000, Math.PI / 10000,
Math.PI / 10000))"
Delete that line for now, we don't need it now.
Run the code, you should see a box on top of the platform.
Removing the Pixilated-ness
As you can tell, the texture
is stretched to fit the platform's size, so the texture looks
pixelated.
In the Initialize sub, where you set up the device, type in the
following lines:
D3Ddev.SamplerState(0).MinFilter = TextureFilter.Point
D3Ddev.SamplerState(0).MagFilter = TextureFilter.Linear
How does this work? I'm not sure
exactly, but I can give you a general idea. MinFilter is the
filtering which Direct3D uses when a texture is close to you.
Setting it to Point gives you distinct clarity. MagFilter is the
oppposite, when a texture is far from you. Setting it to Linear
would "blend" the adjacent pixels with each other, but setting it to
Point would make each pixel stand out, making it pixilated. The only
way I figured this out was by experimentation. Try it out,
I felt that these 2 lines of
code deserved to be in a separate section .... because they're might
come in handy :).
Getting a feel for the 3D World: Simple 3rd
Person Camera (Without Trig)
This section is very light -
we'll just mess around getting around in the world for now before we
delve into trignometry.
In GameClass globals, declare
Public
CharacterPosition As Vector3
Public CameraPosition As New Vector3(0,3,-5) 'We just want a good
starting position for the camera
We'll make a very basic 3rd person camera.
Let's pseudocode!
-Pushing up/down/left/right will adjust CharacterPosition
-Pushing w/s/a/d will adjust the CameraPosition (I told you, this is
a very basic camera class, we won't be doing "following" yet)
-There will be no rotation (Again, basic camera class)
-Because the camera is 3rd person, the camera target value in
LookAtLH will be CharacterPosition
-The platform will stay in the same place ... obviously :). So
before we render the platform we'll set the world to Identity
Easy enough.
Form1's keydown should look like this:
Select Case e.KeyCode
'Up Down Left Right move the character position
'W S A D move the camera position
'Escape quits
Case Keys.Up
Game.CharacterPosition.Z += 0.05
Case Keys.Down
Game.CharacterPosition.Z -= 0.05
Case Keys.Left
Game.CharacterPosition.X -= 0.05
Case Keys.Right
Game.CharacterPosition.X += 0.05
Case Keys.W
Game.CameraPosition.Z += 0.05
Case Keys.S
Game.CameraPosition.Z -= 0.05
Case Keys.A
Game.CameraPosition.X -= 0.05
Case Keys.D
Game.CameraPosition.X += 0.05
Case Keys.Escape
Game.GameOver = True
End Select
Self explanatory :).
There should be a line in Gameclass.Initailize() that looks like
this:
D3Ddev.Transform.View = Matrix.LookAtLH(...........) 'Arguments
culled out
Just replace it with:
D3Ddev.Transform.View =
Matrix.LookAtLH(CameraPosition, CharacterPosition, New Vector3(0, 1,
0))
Now in the RenderScene() sub,
right after BeginScene(), type in the same line:
D3Ddev.Transform.View =
Matrix.LookAtLH(CameraPosition, CharacterPosition, New Vector3(0, 1,
0))
Right before Car.Render():
D3Ddev.Transform.World =
Matrix.Translation(CharacterPosition)
Right before Platform.Render():
D3Ddev.Transform.World = Matrix.Identity
'Because the platform doesn't move, the car does!
Well? Run it,
First press Up: You'll notice that the character moves forward, and
the camera stays in the same place and angles up so that it keeps
looking at the character
Same thing goes for left, right, and down.
Now press W. You'll notice that the camera moves forward, angling
down to keep looking at the character.
Now for Quirks!
Now press A. You'll almost get the impression that the camera is
rotating around the character. It's not. All it's doing is going
straight left, but still looking at the character. Keep on holding
A.... the platform will start to fade away but, the platform
will get distorted and you'll see these little jagged edges
everywhere. I heard that you need to enable AA/AF to get rid of
these "jaggies" but I suppose that's for another tutorial (as I
don't know how to do this yet)
Another quirk:
Restart the application (just to start fresh), and press W. You'll
notice that the character is kept on the center of the screen
(actually, the left edge is, because as I said before I messed up
modeling the box :P). Keep holding W.... WOAH the camera flips over
the box. Why? Because it went past the box, but it still has
to look at the box, hence the flipping over.
Making the Camera Automatically Follow the
Box
This is rather easy, because our "Game" doesn't handle rotation
yet..
What we're basically going to do is get the Z position of the
camera. Again, this is very simple.
Add this into GameClass:
Public DistanceFromSprite as Single = 5
Private Sub CalculateCameraPosition(ByVal DistanceFromCharacter As
Single)
CameraPosition.Z =
CharacterPosition.Z - DistanceFromCharacter
End Sub
CalculateCameraPosition(DistanceFromSprite)
right before Platform.Render()
Form1_Keydown should look like this now:
Select Case e.KeyCode
'Up Down Left Right move the character position
'W S A D move the camera position
'+ or - is to adjust camera distance from sprite
'Escape quits
Case Keys.Up
Game.CharacterPosition.Z += 0.05
Case Keys.Down
Game.CharacterPosition.Z -= 0.05
Case Keys.Left
Game.CharacterPosition.X -= 0.05
Case Keys.Right
Game.CharacterPosition.X += 0.05
Case Keys.W
Game.CameraPosition.Y += 0.05
Case Keys.S
Game.CameraPosition.Y -= 0.05
Case Keys.A
Game.CameraPosition.X -= 0.05
Case Keys.D
Game.CameraPosition.X += 0.05
Case Keys.Add
Game.DistanceFromSprite -= .1
Case Keys.Subtract
Game.DistanceFromSprite += .1
Case Keys.Escape
Game.GameOver = True
End Select
There
you go. Run it, enjoy.
There's a few quirks in there (especially when you push Left and
Right and the camera doesn't move forward) - but we'll fix all this
in a "proper" 3rd person camera tutorial (this is just an example,
and just a place to get you started).
What Next?
This tutorial was basically
the first of several 3D Transformation tutorials.
In the next tutorial we'll discuss Trigonometry, and from then on
I'll provide you with examples and samples of 1st and 3rd person
classes.
There will definitely be a DirectInput tutorial soon, because
what's a good 3D game without mouse input and proper keyboard input?
I loathe and detest the regular windows keyboard input, especially
with that annoying pause when you hold down a key at first.
For those of you who absolutely hate waiting, I've released source
code for a 3rd person camera in vbProgramming forums. Please see the
Mathematics section, there will a post called FPS Camera (I just now
realized I posted a 3rd person camera when he asked for a first
person camera....haha).
Final Word(s)
Bye.
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
|