Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Beginning CSharp Game Programming (2005) [eng]

.pdf
Скачиваний:
160
Добавлен:
16.08.2013
Размер:
4.97 Mб
Скачать

270 Chapter 10 Putting Together a Game

} // end ship collisions

}// end ship/projectile, ship/ship collisions

The previous code segment is mostly the same as the ship-to-projectile collision checking code, except that the code checks each ship with other ships this time, rather than projectiles.

Finally, the last piece of code checks to see if the player collides with any powerups:

// check powerups if( s == player )

{

foreach( Powerup p in powerups )

{

// only check non-destroyed powerups if( !p.Destroyed )

{

if( s.Collide( p ) )

{

// we have a collision! Collide( s, p );

}

}

}

} // end powerup checking

}

}

That’s an awful lot of code, actually; the reason for this is all the special case checking. In a truly flexible collision system no special cases would exist, and things would know how to collide with one another, so you could simply have one loop that checked every object with every other object. Instead, you have three loops for each of the special cases. The choice of system is really up to you; I think for this game, a special-case system is better than handling collisions between objects that don’t need to collide.

Colliding Ships and Projectiles

If a collision does occur, one of three Collision functions is called. The first is the ship-to- projectile collision function:

void Collide( Spaceship s, Projectile p )

{

// hit the ship s.Energy -= p.Damage;

Generic Space Shooter 3000

271

//destroy the projectile; it can only hit one ship p.Destroyed = true;

//if ship is destroyed, add points to whoever fired projectile if( s.Destroyed )

p.Owner.Score += s.Score;

}

The ship’s energy is reduced by the amount of damage the projectile can deal out, and the projectile is destroyed.

If the ship is destroyed, then the score of the ship that fired the projectile (p.Owner) is increased by the score of the destroyed ship.

Colliding Ships

Ship-to-ship collisions add an element of strategy to the game. Rather than killing ships by staying back and shooting lasers at them, you could instead become a kamikaze and ram into ships if you have enough energy.

This is the function that is called when two ships collide:

void Collide( Spaceship s1, Spaceship s2 )

{

//the amount of damage done is the same as the amount of

//energy a ship has left times 2.

float e1 = s1.Energy * 2; float e2 = s2.Energy * 2; s2.Energy -= e1; s1.Energy -= e2;

// modify points if either ship is destroyed. int score1 = s1.Score;

int score2 = s2.Score; if( s1.Destroyed )

s2.Score += score1; if( s2.Destroyed )

s1.Score += score2;

}

The general rule for colliding ships is that the damage done to one is twice the current energy level of the other. So if I have 100 energy and I hit a ship with 10 energy, I’ll whack him for 200 energy points, and he’ll whack me for 20. If either ship dies, the score of the surviving ship is increased by the score of the dead ship.

272 Chapter 10 Putting Together a Game

Colliding Ships and Powerups

This collision function that allows for ships to collide with powerups is the easiest:

void Collide( Spaceship s, Powerup p )

{

//powerup the spaceship p.DoPowerup( s );

//destroy the powerup p.Destroyed = true;

}

The powerup is told to power up the spaceship and is then destroyed.

Need Input!

Johnny 5 is alive! Er...

Okay, the next part of the game state handles gathering input. Luckily for you, it’s not all that hard.

The Keyboard

The keyboard is the default control of the game. I actually prefer using a joystick for this game, but alas, you can’t expect your players to have a joystick, so you need to support the keyboard. Here’s the KeyboardDown function:

protected override void KeyboardDown( DI.Key key )

{

switch( key )

{

case DI.Key.Left:

kl = true; break; case DI.Key.Right:

kr = true; break; case DI.Key.Up:

ku = true; break; case DI.Key.Down:

kd = true; break; case DI.Key.Space:

firing = true; break; case DI.Key.LeftControl:

PreviousWeapon(); break; case DI.Key.RightControl:

NextWeapon(); break;

}

}

Generic Space Shooter 3000

273

For the arrow keys, the kl, kr, ku, and kd variables are set to true when those keys are pressed down. You’ll see how this works a bit later; for now, all you need to know is that those Booleans are true when a key is down, and false when the key is up.

Pressing the left Ctrl or right Ctrl keys switches the current weapon of the player.

Next is the complement of KeyboardDown, KeyboardUp:

protected override void KeyboardUp( DI.Key key )

{

switch( key )

{

case DI.Key.Escape: done = true; break;

case DI.Key.P:

Paused = !Paused; break; case DI.Key.F1:

help = true; break; case DI.Key.Left:

kl = false; break; case DI.Key.Right:

kr = false; break; case DI.Key.Up:

ku = false; break; case DI.Key.Down:

kd = false; break; case DI.Key.Space:

firing = false; break;

}

}

In addition to setting the arrow Booleans to false when the arrow keys are released, this also takes care of the Escape key, P key, and F1 key. Escape causes the game to exit by setting done to true, P toggles the pause status, and F1 turns on the help mode.

The Joystick

Joystick input is simple, as well:

protected override void JoyMoveX( int delta )

{

jx = delta;

}

protected override void JoyMoveY( int delta )

{

jy = delta;

}

274Chapter 10 Putting Together a Game

The axis movement functions simply tell the jx and jy variables the current value of the joystick position. I’ll get to this in just a bit.

The next piece of code handles button presses:

protected override void JoyButtonDown( int button )

{

switch( button )

{

case 0:

firing = true; break; case 1:

NextWeapon(); break; case 2:

PreviousWeapon(); break;

}

}

protected override void JoyButtonUp( int button )

{

if( button == 0 ) firing = false;

}

The joystick buttons simply toggle firing (Button 0), and switching weapons (Buttons 1 and 2).

Processing Input

In the previous game state classes, there was no need to alter the GameState.ProcessInput function. The function simply called the input checkers, which in turn directly handled the input by way of the keyboard/mouse/joystick up/down/axis functions. You can’t do that in this case, however, because the input hasn’t really been handled. For example, when the user presses the Left Arrow key, the ship is supposed to move left, but that doesn’t happen. Instead, a Boolean kl is set to true, and nothing else happens. The game is supposed to handle the player movement calculations later on.

The ProcessInput function, once it has gathered all the input, must actually process the input, which is why I override it:

public override void ProcessInput()

{

// do the default input processing

base.ProcessInput();

Generic Space Shooter 3000

275

The first thing I do is call the base ProcessInput function, which simply tells the mouse, keyboard, and joystick input handlers to gather input. At this point, the kl, kr, ku, kd, jx, and jy variables will hold the states of the arrow keys and the joystick axis. Now it’s time to actually do something with those values, such as calculate the X and Y velocity of the player’s ship:

float vx = 0; float vy = 0;

if( kl

)

 

vx -= player.Speed;

 

if( kr

)

 

vx += player.Speed;

 

if( ku

)

 

vy -= player.Speed;

 

if( kd

)

 

vy += player.Speed;

 

// now

add joystick values

 

vx += (jx/10000.0f)

player.Speed;

vy += (jy/10000.0f)

player.Speed;

If the Left Arrow key is held down, then the player’s speed is subtracted from the player’s X velocity. This means that if only the Left Arrow key is down and the ship can move 200 pixels per second, then vx will be -200. The same thing is done with the other three arrow keys.

The next step is to calculate the joystick input. The function takes jx and jy and divides them by 10,000 (the range of the joystick). If jx is at 5000, then you’ll end up with 0.5, which means that the stick is halfway to the right. In this case, you multiply that value with the player’s speed, and if you use the example from above, you’ll end up with a vx component of +100 pixels per second. Same goes for the Y axis.

So now you’ve calculated the speed of the ship in the X and Y axes, but there’s a problem: As it stands now, the player can “cheat” and make his ship go up to twice as fast as it should go! If the player is holding down the Left Arrow key and has the joystick at the -10,000 x position, this will calculate a speed of -400 pixels per second, which is twice as fast as the 200 pixels per second ship should be moving! Oops! (Yes, it’s difficult to actually play using the arrow keys and a joystick at the same time, but someone will manage it eventually.)

So the last step is to make sure the values never exceed the ship’s speed, and then adjust the speed accordingly:

//make sure player isn’t getting a speed boost

//by using keyboard and joystick at the same time

276 Chapter 10 Putting Together a Game

if( vx > player.Speed ) vx = player.Speed; if( vx < -player.Speed ) vx = -player.Speed;

if( vy > player.Speed ) vy = player.Speed; if( vy < -player.Speed ) vy = -player.Speed;

player.VX = vx; player.VY = vy;

}

Now your players can’t cheat!

Rendering

Rendering is fairly simple. First let me show you the CustomRender function:

protected override void CustomRender()

{

Game.devices.Graphics.Clear( D3D.ClearFlags.Target,

System.Drawing.Color.Black, 1.0f, 0 );

Game.devices.Graphics.BeginScene();

Game.devices.Sprite.Begin();

foreach( Sprite s in ships )

s.Draw( Game.devices.Sprite, camera ); foreach( Sprite s in projectiles )

s.Draw( Game.devices.Sprite, camera ); foreach( Sprite s in powerups )

s.Draw( Game.devices.Sprite, camera );

The same old D3D initialization code makes up the first three lines of the function, and then the sprite drawing kicks in. The game simply loops through all three object arrays and draws them. Remember that as GameObjects are Sprites, you can draw them quite easily.

The next step is to draw the user interface (UI) and some miscellaneous text strings:

DrawUI();

if( paused )

{

Game.bigfont.DrawText(

“PAUSED”,

Generic Space Shooter 3000

277

new System.Drawing.Rectangle( 0, 0,

GSS3KConstants.Width, GSS3KConstants.Height ),

D3D.DrawTextFormat.Center |

D3D.DrawTextFormat.VerticalCenter,

System.Drawing.Color.White );

}

Game.smallfont.DrawText(

“Press F1 for help”,

new System.Drawing.Rectangle( 0, 0,

GSS3KConstants.Width, GSS3KConstants.Height ),

0, System.Drawing.Color.FromArgb( 127, 255, 255, 255 ) );

Game.devices.Sprite.End();

Game.devices.Graphics.EndScene();

Game.devices.Graphics.Present();

}

If the game is paused, then PAUSED is printed out to the screen, and a string telling the user “Press F1 for help” is printed at the upper-left of the screen. I made the text halftranslucent so it’s not a big nuisance.

The user interface is drawn inside the DrawUI function, but I’m not going to show that to you. You’re probably sick of all this graphics code by now anyway. If you’re interested, check it out on the CD, as usual.

Playing GSS3K

Playing the game is pretty simple, as you’ve already seen all the control code. The Arrow keys move your ship, Spacebar fires, the Control keys switch weapons, P pauses the game, F1 takes you to the Help screen, and Escape quits. In addition to that, you can choose to use a joystick (make sure you select it from the setup screen), in which case Button 0 fires, and Buttons 1 and 2 are used to switch weapons.

Figures 10.3 and 10.4 show screenshots of the game in action.

The game is very simple, as you’ll see after about, say, 10 seconds of playing it. But it’s a start! Besides, this simple game is already 10 times more complex than most of the original space shooters.

278 Chapter 10 Putting Together a Game

Figure 10.3 Oh no! There’s too many of them!

Figure 10.4 Hah! The Annihilator got most of them! Suckers!

You’ll notice some quirks in the game—things I didn’t have time to get to. For starters, the game just continues playing after you die. This is probably a bad idea, but the code is structured enough so that nothing fails, and it continues running with no problem. In the future, you may consider adding a “YOU LOSE!” screen to the game.

As it stands now, the game is endless; there is no final boss, there are no levels. You have an endless array of ships swarming around you. This is another situation that you may want to address in a real version of the game.

The Future

279

Another major problem is the lack of detail—this is a space game, yet there are no space objects, such as stars, asteroids, and so on. Including such details would make the game much more immersive. As it stands now, the game actually looks kind of boring—just a black screen with spaceships on it.

Anyway, Demo 10.2 is a decent demonstration of something that could go on to become a great arcade-style game. Feel free to modify it for your own purposes.

The Future

Writing a book on general game programming is a very difficult thing to do. The problem is that there are literally thousands of different game genres out there, and one book cannot possibly cover a substantial amount of information even on just one of them!

This book covers a lot of topics that are seen in almost every game out there, so this should have given you a good start. Most games use graphics, sound, input, sprites, collision detection, and so on. There’s also a ton of other concepts that games use, but I just didn’t have room to get to them.

3D Worlds

Most games these days are 3D, and for a good reason! 3D games look so much more realistic than 2D, and have much more flexibility. Not only that, but 3D hardware is dirtcheap. You should be able to find a video card that has coloring hardware for less than $20, and a card that supports hardware vertex transformations and lighting for less than $50.

Advanced Collision Detection

Obviously, the collision detection in GSS3K is simplistic; it basically puts a theoretical square around your objects and checks to see if those squares intersect. But not everything in the real world is square-shaped! The problem gets even worse in 3D because not everything is cube-shaped; so there are tons of different ways to check if objects collide in a game world.

Artificial Intelligence

GSS3K had no artificial intelligence of any kind in it. The enemy ships simply flew forward and shot at you. Pretty silly, if you ask me. Game companies put lots and lots of research put into AI topics because the more advanced your AI is, the more realistic the game is. Nothing ruins the element of reality worse than having a computer-controlled object do something really stupid, like get stuck in a wall or something. Entire books can are written on the topic of AI. It’s that complex.