Building the Game Engine - Tornado Design



Games for the Game Engine

[pic][pic]

[pic][pic]

Software Development / Games Development Course

Christopher Lewis

Grantham College

Document Version 1.0

Introduction 4

How this booklet works 4

Setting up a Game Project 5

Setting up the Form 9

Changes to the Properties 9

Changes to the Code 10

Other Considerations for the Project 12

Pong 13

Summary 13

Graphic Files for Game 13

Level File Information 13

Form Setup 13

Headers to the File 13

Global Variables 13

Constructor 13

Redrawing the Screen 13

Timers 13

Key Control 13

Mouse Control 13

Other Methods 13

Full Code - Pong 13

Breakout 19

Summary 19

Graphic Files for Game 19

Level File Information 19

Form Setup 19

Headers to the File 19

Global Variables 19

Constructor 19

Redrawing the Screen 19

Timers 19

Key Control 19

Mouse Control 19

Other Methods 19

Full Code - Breakout 19

Space Invaders 28

Summary 28

Graphic Files for Game 28

Level File Information 28

Form Setup 28

Headers to the File 28

Global Variables 28

Constructor 28

Redrawing the Screen 28

Timers 28

Key Control 28

Mouse Control 28

Other Methods 28

Full Code 28

Legend of Nelda 29

Summary 29

Graphic Files for Game 29

Level File Information 29

Form Setup 29

Headers to the File 29

Global Variables 29

Constructor 29

Redrawing the Screen 29

Timers 29

Key Control 29

Mouse Control 29

Other Methods 29

Full Code 29

Introduction

Once you have got your game engine up and running, it is time to start looking at how we can actually use this within the game we want to make. To help you through this process, I have created a series of different types of games for you to look at and build yourself, so that you can see the game engine in action. Each game is fully listed here along with the Level file and any graphics required to make the game work. For copies of the graphics, see me.and I can email them through to you.

How this booklet works

This booklet will take you through the process of creating various different games, step by step. Each game starts with a summary, then a list of the graphics and level file required. Then, each section of the program is explained, before a full code listing is given at the end.

Every piece of code in the game has been tested and works – to an extent. I have not FULLY bug tested the software, and there WILL be errors. The games will run and play, but will probably have glitches in them. Wherever possible I have listed what the glitches are and you can use this information to try and fix the code if you so wish. If you do find an error just email me at clewis@grantham.ac.uk and I may incorporate your fix into the next version of this booklet.

Although you don’t need to know any programming before starting on this booklet, an understanding on the basics of software design and C# programming in a Visual Studio 2010 environment would definitely be beneficial.

Setting up a Game Project

Every game project you create should be in the same Solution file as your game engine, to make it easier for you to debug your code.

Start by opening your “GameEngine” project.

[pic]

Right mouse click on where it says “Solution GameEngine (1 Project)” and click “Add ( New Project”.

[pic]

Select a Windows Form Project, and give it a name to match the game you are creating, for example “Pong”:

[pic]

Then click OK. On the new project that you have just created, right click on its name and select “Set as Startup Project”:

[pic]

This means it will be the project that runs when you click the green “Play” button. Then, right click on the “References” folder in this new project and select “Add Reference…”

[pic]

It should automatically come up with the Tab “Projects”, and “GameEngine” should be highlighted – if it hasn’t, set it as below and click OK.

[pic]

Now finally right-click on the form in the main window, click “View Code”, and at the top of the screen add “Using GameEngine”:

[pic]

Setting up the Form

Most games that are listed here require you to set up your project in the same way. To save having to write this out for every project, this section covers basically everything you need to do.

Changes to the Form Design Page

First of all, right-click on the grey area of the form and click “Properties”. You need to change the following:

• FormBorderStyle - None

• WindowState – Maximised

[pic]

Click on the “Lightning bolt” on the properties window to show all the events. Double click on the following:

• KeyDown

• KeyUp

• Paint

From the Toolbox on the left, double click on a Timer. All games need at least one timer, some will need two! This first timer should have the following properties set (you may need to change from the lightning bolt to the bullet list next to it on the properties window):

• Enabled – True

• Interval – 20

Click on the Lightning bolt again, and double click on the “Tick”.

Changes to the Code Page

Within the global variable area, we are going to need a game world, so we should set one up. Within the Initialiser for the form, we should tell the computer that we want the screen to be smooth and not jerky when drawing the graphics (Set Style), Load the level (Which will be Level1.txt generally) and we need two event handlers – colliding with the wall and collisions with objects:

public partial class Form1 : Form

{

// Set up a Game World:

GameWorld Game = new GameWorld();

public Form1()

{

InitializeComponent();

SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);

// Set up the game - load in the level:

Game.LoadLevel("Levels\\Level1.txt");

// set up the two event handlers - Object and Wall collisions:

Game.ItemObjectCollision += new CollideObject(Game_ItemObjectCollision);

Game.ItemWallCollision += new CollideWall(Game_ItemWallCollision);

}

If you press “Tab” twice after the “+=” when adding the event handlers, the following will be put in for you - if not, you will need to do it! Anything between the curly brackets put in by Visual Studio by itself should be removed.

void Game_ItemWallCollision(ref GameObject Object, WallCollision Wall)

{

}

void Game_ItemObjectCollision(ref GameObject Object1, ref GameObject Object2)

{

}

Next, let’s look at the timer_tick – as a standard we need to move on to the next frame of animation on all our objects, move the items, check for any collisions, load these onto the camera, then refresh the screen:

private void timer1_Tick(object sender, EventArgs e)

{

// Move on to the next frame of animation:

Game.AnimateItems();

// Move the items:

Game.MoveItems();

// Check for collisions:

Game.CheckCollisions();

// Draw:

Game.Frame();

// and Refresh the screen:

this.Refresh();

}

Next we need to deal with drawing the camera to the screen. To make things better, we are going to stretch the camera over the whole form at all times:

private void Form1_Paint(object sender, PaintEventArgs e)

{

// Draw the game to the screen:

e.Graphics.DrawImage(((Camera)Game.WorldCameras[0]).CameraScreen, 0, 0, this.Width, this.Height);

}

We need to be able to close the game down as well, so we’ll get the “KeyDown” event working with clicking the “Escape” key closing the program:

private void Form1_KeyDown(object sender, KeyEventArgs e)

{

switch (e.KeyCode)

{

case Keys.Escape:

Application.Exit();

break;

}

}

Other Considerations for the Project

If you want to create a folder for storing levels or graphics (which you need to do for at least an “images” directory, which is where the engine expects to find all the graphics for the objects) you need to Right click on the project in solution explorer, select “Add -> new folder” then name it what you want to name it.

[pic]

If you need to add files into the folder, which again you will need to do with your graphics and possibly your level file, the procedure you should follow is as follows:

• Create the file in the folder you want to use it in.

• On Visual Studio, on the main menu under “Project” click “Show All Files”. You will see a “ghost” file of what you have created in your folder.

[pic]

[pic]

(in my case, this is “Test.bmp”

• Right click on the file and select “Include in Project”

[pic]

• Right click on the file again and select properties. Change the “Copy to output directory” from “Never” to “Copy To newer”

[pic]

• On the Project menu, switch off “Show All Files” again.

This should be enough now to get you ready to roll with the games!

Pong

Summary

Pong is probably the first game that was created for a computer. It involves two players with bats hitting a ball between each other, with the aim being to get the ball past the opponent. This makes it a nice simple game that is ideal to start with.

This version of Pong only supports two players (you can't play against the computer) and is set up to simply keep scoring forever. To stop the game, press escape on the keyboard. To make things a little harder, every 5 seconds the ball gets faster and the bats get smaller. When the bats reach a size of zero, the game is over.

To make this game as simple as possible, I have done the following:

• You can only move your bats up and down

• There is no animation (each object only has one frame of animation)

• The background is a single tile that is 640x480 in size

• The level is 640x480

• The camera resolution is 640x480

• The form blows up the camera to be the same size as the screen (Which means it looks a little squashed on a widescreen monitor)

Known Glitches

At present, the game doesn't actually ever finish - the "Game over" system doesn't work, as for some reason the game actually allows a zero-height bat, then starts using a negative value for the height, effectively making the bat grow! That doesn't really matter though, it still serves as a gentle introduction to the world of game programming!

Another quite problematic glitch is that the ball can bounce around inside the bat if it hits the top or bottom of it rather than the side. The solution to this i problem is actually shown in the next game example, but to keep things simple I have not fixed it here.

Graphic Files for Game

The background is a simple textured rectangle drawn in Paint, 640x480 in size:

[pic]

The Items are very simple too:

[pic]

Player 1 bat is located at 0,0 and is 25x80 pixels in size. Player 2 bat is located at 0,80 and is also 25x80 pixels. The ball is located at 25,0 and is 20x20 pixels in size.

Level File Information

The level file in its entirety is shown below:

640,480,1

Objects,Items.png,255,255,255

ENDGRAPHICS

Player1Bat,Objects,0,0,25,80,1,N

Player2Bat,Objects,0,80,25,80,1,N

Ball,Objects,25,0,20,20,1,N

ENDANIMATIONS

Background,images\Bgnd.png,640,480,640,480,0,0,0,0

00

ENDBACKGROUNDS

640,480,0

Player1,Player1Bat,25,0,0,0,0,0,N

Player2,Player2Bat, 590,0,0,0,0,0,N

Ball,Ball, 308,228,0,5,5,0,N

ENDOBJECTS

640,480,0,0,640,480,N

ENDCAMERAS

This means:

• The Level is 640x480 in size

• The only graphic object is my Items.png, with a transparency colour of White (255,255,255) and given the name “Objects”

• There are 3 “Animations” – Player1Bat, Player2Bat and Ball, and all three come from the “Objects” graphic. The Offsets are given first, then the sizes, and all of them are just one frame of animation. “N” signifies the animation frames would be horizontal if there were any.

• The image to use for the background is “Bgnd.png”, it is 640x480 in size, the tile size is also 640x480, it’s Z-Level is 0 and the transparency colour is Black (0,0,0)

• The tile map is therefore just “00” meaning just the one tile, top coordinate

• The bump map has a grid size of 640x480, and is a “0” (No bump map)

• The first object, “Player1”, uses the Player1Bat animation, starts at location 25,0,0, has a speed of 0,0,0 and is not a Ghost

• The next object, “Player2”, uses the Player2Bat animation, starts at location 590,0,0, has a speed of 0,0,0 and is not a Ghost

• The next object, “Ball”, uses the Ball animation, starts at location 308,228,0, has a speed of 5,5,0 and is not a Ghost

• There is one camera with a resolution of 640x480, which is also the draw size. It is a Normal camera rather than a 3D one.

Form Setup

The form uses the standard form setup as described earlier in this booklet, but then add a second timer. The properties of this second timer should be Enabled = true and Interval = 5000. Click on the lightning bolt to activate the timer2_tick event handler.

Headers to the File

The form uses the standard headers - No extra items are needed in the “using”’s at the top of the code!

Global Variables

As well as the game world, you need the following variables:

// Set the scores to zero:

int Player1Score = 0;

int Player2Score = 0;

// Create an enumerated type for up and down, used to know if a key has been pressed:

enum Direction { Up, Down}

bool[,] BatMovement = new bool[2, 2] { { false, false }, { false, false } };

// Create a font to use for the scores:

Font Scores = new Font("Calibri", 28);

The most important variable here is the BatMovement, 2 dimensional array. The first column is for Player 1’s movement, and the second column is for Player 2’s movement:

|Player 1 Up |Player 2 Up |

|Player1 Down |Player 2 Down |

If the Boolean is set to true, it means move in that direction, otherwise don’t!

Constructor

This is the standard constructor as already listed in this booklet – use Set Style and setup “Level1.txt”.

Redrawing the Screen

As well as drawing the game camera, we are going to draw the two scores up on the screen as well:

private void Form1_Paint(object sender, PaintEventArgs e)

{

// Draw the game to the screen:

e.Graphics.DrawImage(((Camera)Game.WorldCameras[0]).CameraScreen, 0, 0, this.Width, this.Height);

// Show the Scores:

e.Graphics.DrawString(Player1Score.ToString(), Scores, new SolidBrush(Color.White), new PointF(100, 10));

e.Graphics.DrawString(Player2Score.ToString(), Scores, new SolidBrush(Color.White), new PointF(this.Width-150, 10));

}

Timers

On Timer 1, we start with the standard timer 1 as set out previously in this document. Remove the line of code for “AnimateItems” as there are no animations. We are then going to add in setting/changing the speed of the Player bats. You will end up with a timer1_tick that appears as follows:

private void timer1_Tick(object sender, EventArgs e)

{ // main timer - go through all the objects, setting the speed for player 1 / 2:

foreach (GameObject item in Game.WorldObjects)

{

if (item.ObjectType == "Player1")

{

if (BatMovement[0, (int)Direction.Up] == true)

item.Speed.Y = -15;

if (BatMovement[0, (int)Direction.Down] == true)

item.Speed.Y = 15;

else if ((BatMovement[0, (int)Direction.Up] == false) && (BatMovement[0, (int)Direction.Down] == false))

item.Speed.Y = 0;

}

else if (item.ObjectType == "Player2")

{

if (BatMovement[1, (int)Direction.Up] == true)

item.Speed.Y = -15;

if (BatMovement[1, (int)Direction.Down] == true)

item.Speed.Y = 15;

else if ((BatMovement[1, (int)Direction.Up] == false) && (BatMovement[1, (int)Direction.Down] == false))

item.Speed.Y = 0;

}

}

// Move the items:

Game.MoveItems();

// Check for collisions:

Game.CheckCollisions();

// Draw:

Game.Frame();

// and Refresh the screen:

this.Refresh();

}

For the second timer, every 5 seconds we are going to make the ball move faster and the bats smaller!

private void timer2_Tick(object sender, EventArgs e)

{

// Make the ball move faster and bats smaller!

foreach (GameObject item in Game.WorldObjects)

{

if (item.ObjectType == "Ball")

{

if (item.Speed.Y > 0)

item.Speed.Y += 1;

else

item.Speed.Y -= 1;

if (item.Speed.X > 0)

item.Speed.X += 1;

else

item.Speed.X -= 1;

}

else

{

item.ObjectSize.Height -= 5;

if (item.ObjectSize.Height 0)

item.Speed.Y += 1;

else

item.Speed.Y -= 1;

if (item.Speed.X > 0)

item.Speed.X += 1;

else

item.Speed.X -= 1;

}

else

{

item.ObjectSize.Height -= 5;

if (item.ObjectSize.Height Object1.Location.X))

{

Object1.Speed.Y = -Object1.Speed.Y;

Object1.Location.Y = Object2.Location.Y - 20;

}

// Otherwise, just ignore!

}

else

{

// Ball above the bat - bounce it!

if ((Object2.Location.Y < Object1.Location.Y) && (Object1.Location.X < Object2.Location.X) && (Object1.Location.X + Object1.ObjectSize.Width > Object2.Location.X))

{

Object2.Speed.Y = -Object2.Speed.Y;

Object2.Location.Y = Object1.Location.Y - 20;

}

// Otherwise, just ignore!

}

}

else if (ItemsHit(ref Object1, ref Object2, "Player1", "Powerup"))

{

// Randomly select what the powerup does:

int Value = rnd.Next(0, 5);

// Remove it:

if (Object1.ObjectType == "Powerup") Object1.Remove = true;

else if (Object2.ObjectType == "Powerup") Object2.Remove = true;

switch (Value)

{

// Grow Bat:

case 0:

if (Object1.ObjectType == "Player1") Object1.ObjectSize.Width += 10;

else Object2.ObjectSize.Width += 10;

break;

// Shrink Bat:

case 1:

if (Object1.ObjectType == "Player1") Object1.ObjectSize.Width -= 10;

else Object2.ObjectSize.Width -= 10;

break;

// New Ball:

case 2:

GameObject PowerUp = new GameObject("Ball", "Ball",

"320", "240", "0", "-6", "-6", "0", "N");

Game.WorldObjects.Add(PowerUp);

break;

// Extra Life:

case 3:

PlayerLives += 1;

break;

// 1000 points

case 4:

PlayerScore += 1000;

break;

}

}

else if (ItemsHit(ref Object1, ref Object2, "Tile", "Ball"))

{

// Only do this if the ball hasn't already changed direction!

if (BallChange == false)

{

BallChange = true;

Rectangle Ball;

Rectangle Tile;

GameObject BallObject;

GameObject TileObject;

// Ball hit Tile:

if (Object1.ObjectType == "Ball")

{

Ball = new Rectangle(Object1.Location.X + (Object1.ObjectSize.Width / 2), Object1.Location.Y + (Object1.ObjectSize.Height / 2), 1, 1);

Tile = new Rectangle(Object2.Location.X, Object2.Location.Y, Object2.ObjectSize.Width, Object2.ObjectSize.Height);

BallObject = Object1;

TileObject = Object2;

}

else

{

Ball = new Rectangle(Object2.Location.X + (Object2.ObjectSize.Width / 2), Object2.Location.Y + (Object2.ObjectSize.Height / 2), 1, 1);

Tile = new Rectangle(Object1.Location.X, Object1.Location.Y, Object1.ObjectSize.Width, Object1.ObjectSize.Height);

BallObject = Object2;

TileObject = Object1;

}

if (Ball.Y > Tile.Y + Tile.Height)

{

// The ball is under the tile:

BallObject.Speed.Y = 6;

}

else if (Ball.Y < Tile.Y)

{

// The ball is above the tile:

BallObject.Speed.Y = -6;

}

if (Ball.X < Tile.X)

{

// The ball is to the left of the tile:

BallObject.Speed.X = -6;

}

else if (Ball.X > Tile.X)

{

// The ball is to the right of the tile

BallObject.Speed.X = 6;

}

// Kill the tile:

TileObject.Remove = true;

// If the tile is a blue bonus tile, release a power-up!

if (TileObject.CurrentObject == "Tile4")

{

GameObject PowerUp = new GameObject("Powerup", "Powerup",

TileObject.Location.X.ToString(), TileObject.Location.Y.ToString(), TileObject.Location.Z.ToString(), "0", "8", "0", "N");

Game.WorldObjects.Add(PowerUp);

}

// Add points:

PlayerScore += 10;

if (Object1.ObjectType == "Ball")

{

Object1 = BallObject;

Object2 = TileObject;

}

else

{

Object2 = BallObject;

Object1 = TileObject;

}

}

}

}

Let us start with the first interaction – Bat and Ball. If the ball hits the bat while it is still above it, we bounce it off the bat, otherwise we let it fall. This is how we fix the problem with the ball bouncing around inside the bat that we got with Pong. We need to runthe code both ways round (With ball being Object1 and Object2) as we don’t know which way the interaction will happen.

The next interaction is between the Player and the powerup. This involves removing the crystal from the game engine, creating a random number of either 0,1,2,3, or 4, then depending on what this value is depends on what we do – the comments should be pretty clear through that switch statement.

The final interaction is between the tile and the ball. If there has already been some form of interaction, ballchange is set to true and therefore this piece of code is ignored, otherwise it has to check to see whereabouts on the tile it has touched, and based on this information we decide how to deflect the ball from the tile. We also need to check to see if the tile bounced on is the blue bonus tile, and if so we release a power up crystal (Create a new object and put it into the game world).

Other Methods

The only other method we need to include is the one we have referenced in our object collision event handler – ItemsHit, that returns a Boolean - true if the two objects, either way round match what has been asked of it, and false if not:

protected bool ItemsHit(ref GameObject Object1, ref GameObject Object2, string Name1, string Name2)

{

// Check both ways around when a collision between two items:

if ((Object1.ObjectType == Name1) && (Object2.ObjectType == Name2))

return true;

else if ((Object1.ObjectType == Name2) && (Object2.ObjectType == Name1))

return true;

else

return false;

}

[pic]

Full Code - Breakout

using System;

using System.Collections.Generic;

using ponentModel;

using System.Data;

using System.Drawing;

using System.Linq;

using System.Text;

using System.Windows.Forms;

using GameEngine;

namespace Breakout

{

public partial class Form1 : Form

{

// Set up a Game World:

GameWorld Game = new GameWorld();

// Set the score:

int PlayerScore = 0;

int PlayerLives = 3;

Random rnd = new Random(System.DateTime.Now.Millisecond);

// Create an enumerated type for left and right, used to know if a key has been pressed:

enum Direction { Left, Right}

bool[] BatMovement = new bool[2] { false, false };

// Create a font to use for the scores:

Font Scores = new Font("Calibri", 28);

bool BallChange = false;

public Form1()

{

InitializeComponent();

SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);

// Set up the game - load in the level:

Game.LoadLevel("Levels\\Level1.txt");

// set up the two event handlers - Object and Wall collisions:

Game.ItemObjectCollision += new CollideObject(Game_ItemObjectCollision);

Game.ItemWallCollision += new CollideWall(Game_ItemWallCollision);

}

void Game_ItemWallCollision(ref GameObject Object, WallCollision Wall)

{

// If the ball collides with the wall:

if (Object.ObjectType == "Ball")

{

// Top or side walls, bounce off:

if (Wall == )

Object.Speed.Y = -Object.Speed.Y;

else if ((Wall == WallCollision.Left) || (Wall == WallCollision.Right))

Object.Speed.X = -Object.Speed.X;

// Bottom wall - lose a life:

else

{

// See how many balls we have:

int counter = 0;

foreach (GameObject balls in Game.WorldObjects)

{

if (balls.ObjectType == "Ball")

counter++;

}

if (counter == 1)

{

PlayerLives--;

if (PlayerLives == 0)

{

timer1.Enabled = false;

MessageBox.Show("Game Over");

Application.Exit();

}

Object.Location.X = 320 - 8;

Object.Location.Y = 240 - 8;

Object.Speed.Y = -6;

}

else

{

// remove the extra ball:

Object.Remove = true;

}

}

}

else if (Object.ObjectType == "Powerup")

{

// Remove it:

Object.Remove = true;

}

else if (Object.ObjectType == "Player1")

{

// If it is a bat colliding with the wall - stop it from moving:

Object.Speed.X = 0;

// Now set the position to be just off the top of the wall if it hit the top wall:

if (Wall == WallCollision.Left)

{

Object.Location.X = 5;

}

else

{

// Just off the right of the wall if it hit the right wall

Object.Location.X = 640 - (Object.ObjectSize.Width + 5);

}

}

}

void Game_ItemObjectCollision(ref GameObject Object1, ref GameObject Object2)

{

if (ItemsHit(ref Object1, ref Object2, "Player1", "Ball"))

{

// Ball hit bat:

if (Object1.ObjectType == "Ball")

{

// Ball above the bat - bounce it!

if ((Object1.Location.Y < Object2.Location.Y) && (Object2.Location.X < Object1.Location.X) && (Object2.Location.X + Object2.ObjectSize.Width > Object1.Location.X))

{

Object1.Speed.Y = -Object1.Speed.Y;

Object1.Location.Y = Object2.Location.Y - 20;

}

// Otherwise, just ignore!

}

else

{

// Ball above the bat - bounce it!

if ((Object2.Location.Y < Object1.Location.Y) && (Object1.Location.X < Object2.Location.X) && (Object1.Location.X + Object1.ObjectSize.Width > Object2.Location.X))

{

Object2.Speed.Y = -Object2.Speed.Y;

Object2.Location.Y = Object1.Location.Y - 20;

}

// Otherwise, just ignore!

}

}

else if (ItemsHit(ref Object1, ref Object2, "Player1", "Powerup"))

{

// Randomly select what the powerup does:

int Value = rnd.Next(0, 5);

// Remove it:

if (Object1.ObjectType == "Powerup")

Object1.Remove = true;

else if (Object2.ObjectType == "Powerup")

Object2.Remove = true;

switch (Value)

{

// Grow Bat:

case 0:

if (Object1.ObjectType == "Player1") Object1.ObjectSize.Width += 10;

else Object2.ObjectSize.Width += 10;

break;

// Shrink Bat:

case 1:

if (Object1.ObjectType == "Player1") Object1.ObjectSize.Width -= 10;

else Object2.ObjectSize.Width -= 10;

break;

// New Ball:

case 2:

GameObject PowerUp = new GameObject("Ball", "Ball",

"320", "240", "0", "-6", "-6", "0", "N");

Game.WorldObjects.Add(PowerUp);

break;

// Extra Life:

case 3:

PlayerLives += 1;

break;

// 1000 points

case 4:

PlayerScore += 1000;

break;

}

}

else if (ItemsHit(ref Object1, ref Object2, "Tile", "Ball"))

{

// Only do this if the ball hasn't already changed direction!

if (BallChange == false)

{

BallChange = true;

Rectangle Ball;

Rectangle Tile;

GameObject BallObject;

GameObject TileObject;

// Ball hit Tile:

if (Object1.ObjectType == "Ball")

{

Ball = new Rectangle(Object1.Location.X + (Object1.ObjectSize.Width / 2), Object1.Location.Y + (Object1.ObjectSize.Height / 2), 1, 1);

Tile = new Rectangle(Object2.Location.X, Object2.Location.Y, Object2.ObjectSize.Width, Object2.ObjectSize.Height);

BallObject = Object1;

TileObject = Object2;

}

else

{

Ball = new Rectangle(Object2.Location.X + (Object2.ObjectSize.Width / 2), Object2.Location.Y + (Object2.ObjectSize.Height / 2), 1, 1);

Tile = new Rectangle(Object1.Location.X, Object1.Location.Y, Object1.ObjectSize.Width, Object1.ObjectSize.Height);

BallObject = Object2;

TileObject = Object1;

}

if (Ball.Y > Tile.Y + Tile.Height)

{

// The ball is under the tile:

BallObject.Speed.Y = 6;

}

else if (Ball.Y < Tile.Y)

{

// The ball is above the tile:

BallObject.Speed.Y = -6;

}

if (Ball.X < Tile.X)

{

// The ball is to the left of the tile:

BallObject.Speed.X = -6;

}

else if (Ball.X > Tile.X)

{

// The ball is to the right of the tile

BallObject.Speed.X = 6;

}

// Kill the tile:

TileObject.Remove = true;

// If the tile is a blue bonus tile, release a power-up!

if (TileObject.CurrentObject == "Tile4")

{

GameObject PowerUp = new GameObject("Powerup", "Powerup",

TileObject.Location.X.ToString(), TileObject.Location.Y.ToString(), TileObject.Location.Z.ToString(), "0", "8", "0", "N");

Game.WorldObjects.Add(PowerUp);

}

// Add points:

PlayerScore += 10;

if (Object1.ObjectType == "Ball")

{

Object1 = BallObject;

Object2 = TileObject;

}

else

{

Object2 = BallObject;

Object1 = TileObject;

}

}

}

}

protected bool ItemsHit(ref GameObject Object1, ref GameObject Object2, string Name1, string Name2)

{

// Check both ways around when a collision between two items:

if ((Object1.ObjectType == Name1) && (Object2.ObjectType == Name2))

return true;

else if ((Object1.ObjectType == Name2) && (Object2.ObjectType == Name1))

return true;

else

return false;

}

private void Form1_Paint(object sender, PaintEventArgs e)

{

// Draw the game to the screen:

BallChange = false;

e.Graphics.DrawImage(((Camera)Game.WorldCameras[0]).CameraScreen, 0, 0, this.Width, this.Height);

e.Graphics.DrawString("Score:"+PlayerScore.ToString(), Scores, new SolidBrush(Color.White), new PointF(100, 10));

e.Graphics.DrawString("Lives:"+PlayerLives.ToString(), Scores, new SolidBrush(Color.White), new PointF(this.Width - 150, 10));

}

private void Form1_KeyDown(object sender, KeyEventArgs e)

{

// When you click a key, set the value to true:

switch (e.KeyCode)

{

case Keys.Escape:

Application.Exit();

break;

case Keys.Left:

BatMovement[(int)Direction.Left] = true;

break;

case Keys.Right:

BatMovement[(int)Direction.Right] = true;

break;

}

}

private void Form1_KeyUp(object sender, KeyEventArgs e)

{

// When you let go of a key, set the value to false:

switch (e.KeyCode)

{

case Keys.Left:

BatMovement[(int)Direction.Left] = false;

break;

case Keys.Right:

BatMovement[(int)Direction.Right] = false;

break;

}

}

private void timer1_Tick(object sender, EventArgs e)

{

// main timer - go through all the objects, setting the speed for player 1 / 2:

foreach (GameObject item in Game.WorldObjects)

{

if (item.ObjectType == "Player1")

{

if (BatMovement[(int)Direction.Left] == true)

item.Speed.X = -15;

else if (BatMovement[(int)Direction.Right] == true)

item.Speed.X = 15;

else

item.Speed.X = 0;

}

}

// Move on to the next frame of animation:

Game.AnimateItems();

// Move the items:

Game.MoveItems();

// Check for collisions:

Game.CheckCollisions();

// Draw:

Game.Frame();

// and Refresh the screen:

this.Refresh();

}

}

}

Alien Breed

Summary

Alien Breed was a game on the Amiga that came out in 1992 and has remained a classic to this day – versions are available on iOS, Android, PC, and other devices. It is a top-down shooter which places your character in a space station trying to defeat a horde of aliens. What is interesting about this game is that there was even a version for a graphical calculator, which led to the development of a black and white version for which you can see a full level editor for below:

[pic]

( for More information)

This particular level is 60 x 48 blocks in size, which looks like this when running on a calculator:

[pic]

The iOS version is a bit more interesting and colourful to look at:

[pic]

So could we make a game like this? Looking at the graphics, it is obviously a tiled background, with simple walls and a level file that looks worked out for us (on a calculator, at least) so we could possibly put together something similar…

Analysis of the image above and the game has led to the following information:

• Floor tiles are 48x48 pixels in size for a 1024x768 screen resolution

• There are various different floor tiles to make up the main background

• A black tile is used to signify an area which is not part of the level

• There are angled “shadow” floor tiles and darker tiles to give the impression of a light source illuminating the walls

• There is a double-thickness wall for around the outside of the level

• There are floortiles with different patterns on to break up the structure.

• The wall tiles needed are:

o 4x End (Up, Down, Left, Right)

o Vertical

o Horizontal

o 4x T-junction (up, down, left, right)

o 4x Corner (up, down, left, right)

• The Floor tiles needed are:

o In the light

o In the shadow

o 3x corner shadow

o Black tile

• There is a player object, and he can move in 8 directions.

• When you stop moving, you stay facing the way you were.

• There is one main type of enemy, an alien, although later levels do have some others

• There are doors that need to be opened with keys

• Keys are distributed around the level

• You earn money (normally from killing aliens) which you can use to buy weapon upgrades

Because this is a more complex game than the previous ones created here, I am going to create it step by step, and share with you the stages that lead to a fully working level for the game! So where do we start…?

Well, my plan of action will be as follows:

1) Create a level map for the game based on the one for the calculator level shown above, but only using a 1-layer thickness of wall at all locations

2) Create the character object, unanimated, with all 8 directions

3) Scroll the camera around with the character so you can go all around the level file

4) Replace the “Black” tile on the level layout with a “transparent” colour

5) Add a star background underneath the level file with parallax scrolling, so that you get a great effect around the level file

6) Add in the key card object and allow you to collect them

7) Add in the door object, so that you have to have a key to open a door

8) Add in the enemies

9) Add in the exit object (to change level / complete the game)

Alien Breed - Stage 1

Graphics for the Background

First of all, I used the screenshot above to steal borrow some of the background tiles. To make things easier for myself, I haven’t used lots of the different variant tiles for the walls and backgrounds, of which there are some, so my version will look a little bit more, well, amateur, but it will be clear enough and look good enough for a quick game or three:

[pic]

I will add to this as I make the map more complex, but this is more than enough to start with.

Building the Level Map

To build something as complex as the level map above, I needed to use a level editor. I have created one that you can also use when you want to create your own level files. My map is 2880 x 2304 pixels in size, with a block size of 48x48.

[pic]

To start with, although I have many different “Wall” tile pieces, I decided to just use one to build up the general structure, so I used the black tile where black should be and a side tile for the walls:

[pic]

Then once the map is finished, I can go back over all the walls and make sure I use the correct pattern for the position of the wall:

[pic]

So you can build the level file up in stages. After the walls, we need to add the shadows:

[pic]

Once the level is made, I clicked “Edit bump map” and added the bump map to wherever there was a wall:

[pic]

The Level File – So Far

We need the level to be 2880x2304 pixels, and have got our tile map and bump map produced :-

2880,2304,1

ENDGRAPHICS

ENDANIMATIONS

Background,images\Tiles.png,2880,2304,48,48,255,255,255,0

E04040404040404040404040404040404040404040404040404040404040404040404040404041313131313131313131E0404040404040404040404190707070707070707070707070707070707070707070707070707070707070707070707070709031313131313131313190707070707070707070709090700000000000000000000000000000000000000000C060E040304050602040304030405060903131313131313131E061700000000000000000009090700020404040404040404040404040304040404040A070903190707070427090319070707032404131E04040404061707000000000000000000090907000427070707070707070707070709031313131319070903190700000000090319070000042709031907070707070000000000000000000000090907000000000000000000000000000003240404040406170903190700000000090319070000000009031907000E04041600000000000000000000090907000000000000000000000000000004270707070707070903132404160E040613132404160E04061319070009031324160000000000000000000909070000000000000000000E04030404040404040404160009031313190709031313131319070903131319070009031319070000000000000000000909070000000000000000000903190707070707070709070009031E040617032404131E040617032404131907000903131907000000000000000000090324040404040404040404061319070000000000000907000903190707070427090319070707042709031907000903131324040404040404040404061313131313131313131313131319070000000000000907000903190700000000090319070000000009031907000903131313131313131313131313131313131313131E04040404040406170000000000000907000903132404160E040613132404160E04061319070009031313131313131313131313131313131313131319070707070707070700000E0404040617000903131319070903131313131907090313131907000903131313131313131313131313131313131313131907000000000000000000090707070707000524040406170324040404040617032404040617000903131313131313131313131313131E04040404040A07000000000000000000090700000000000907070707070427070707070707042707070707000903131313131313131313131313131907070707070907020304040404041600032404040416000907000000000000000000000000000000000000000903131313131313131313131313131907000000000907070903131313190700042707070907000907000E040404030404040404040404040404040406131313131313131313131313131319070C0600000907020A031313131524040404040406170009070009070707090313131313131313131313131313131313131313131313131313131319070907000009070709031313131907070707070707070009070009070000090313131313131313131313131313131E0404040413131313131313131907090700000907020A03131313132404040404040404040404170907000009031313131E04040413131313131313190707070903131313131313131907090700000907070903131313131313131313131313131319070907000009031313131907070903131313131313190700000324040404040404041907090700000907020A0313131313131313131313131313131907090700000903131313190700090313131E040404061700000427070707070707090907090700000B07070903131313131313131313131313131E0617090700000903131313190700090313131907070707070000000000000000000009090705250600042700032404040404040404040404040404061707032404040D040404040617000324040404040404050600000000000000000000090907052507000000000427070707070707070707070707070705100427070707070707070705100427070707070707070700000000000000000000090907052404040404040404040404040404040404040404040410000000000000000000000000000E04040404040404050600000000000000000000090907090313131313131313131313131313131313131313131900000E04040404040404040404040613131313131907070700000000000000000000090907032404040413131E040404040404040404040404040406100009031313131313131313131313131313131319070000000000000000000000000909070707070709031319070707070707070707070707070707000009031313131313131313131313131313131319070000000000000E04040404040619070000000009031319070E040404040404040404040404040404061313131313131313131313131313131313132404040404040406131313131313132404040416090313190709031313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131907090313190709031E040404040404131E040404040404131E040404040404131E040404040404131313131313131313131313131313131E0404040617090313190709031907070707070903190707070707090319070707070709031907070707070903131313131E0404040404040404040419070707070709031319070903132404160E040613132404160E040613132404160E040613132404170E040613131313131907070707070707070709090700000000090313190709031313190709031313131319070903131313131907090313131313190709031313131313131907000000000000000009090700000000090313190709031E040617032404131E040617032404131E040617032404131E040617032404131313131319070000000000000000090907000000000903131907090319070707042709031907070704270903190707070427090319070707042709031E04040406170000000000000000090907000000000903131907090319070000000009031907000000000903190700000000090319070000000009031907070707070000000000000000090907020404040D04040A070903132404160E040613132404160E040613132404160E040613132404160E04061319070E040417000000000000000009090704270707070707090709031313190709031313131319070903131313131907090313131313190709031313190709031907000000000000000009090700000000000000090709031E040617032404131E040617032404131E040617032404131E0406170324041319070903190700000000000000000909070000000000000009070903190707070427090319070707042709031907070704270903190707070427090319070903190700000000000000000909070000000000000009070903190700000000090319070000000009031907000000000903190700000000090319070903132404040404040404040619070000000000000009070903132404160E040613132404160E040613132404160E040613132404160E0406131907090313131313131313131313131907000000000000000907090313131907090313131313190709031313131319070903131313131907090313131907090313131313131313131313131907000000000000000907032404040617032404040404061703240404040406170324040404040617032404040617090313131313131313131313131907000000000000000907070707070707070707070707070707070707070707070707070707070707070707070707090313131313131313131313131324040404040404040D04040404040404040404040404040404040404040404040404040404040404040404040404061313131313131313131313131

ENDBACKGROUNDS

48,48,111111111111111111111111111111111111111000000000111111111111100000000000000000000000000000000000001000000000100000000001100000000000000000000010111110111111101000000001100000000001100111111111111111111110101000001010001110111111000000000001100000000000000010000010101000001010000010100000000000000001100000000000000011111110101000001010000010100111000000000001100000000000000000000000101110111011101110100101100000000001100000000001111111111100100010100000101000100100100000000001100000000001010000000100101110111011101110100100100000000001111111111111010000000100101000001010000010100100111111111111000000000000010000000100101000001010000010100100000000000000000000111111110000000100101110111011101110100100000000000000000000100000000001111100100010100000101000100100000000000000000000100000000001000000111110111111101111100100000000000000111111100000000001000000100000000000000000000100000000000000100000101111111001111100100000000000000000000100000000000000100000100100001000000100100111111111111111111100000000000000101000101100001111111100100100010000000000000000000000000000101000100100001000000000100100010000000000000001111100000000101000101100001111111111110100010000111100000001000100000000101000100100000000000000010100010000100100000001000111111111101000101100000000000000010100010000100100011111000000000001101000100100000000000000110100010000100100010000000000000001101100000111111111111111100111111111100111111111000000000001101100000000000000000000000000000000000000000000000000000001101111111111111111111111100000000000000111111111000000000001101000000000000000000000100111111111111100000100000000000001101111100111111111111111100100000000000000000100000000000001100000100100000000000000000100000000000000000100000001111111100000100101111111111111111100000000000000000111111111000000111110100101000000000000000000000000000000000000000000000000000010100101011111110111111101111111011111110000000000000000111110100101010000010100000101000001010000010000011111111111100000100101011101110111011101110111011101110000010000000001100000100101000101000001010000010100000101000000010000000001100000100101011101110111011101110111011101110000010000000001100000100101010000010100000101000001010000010111110000000001100000100101010000010100000101000001010000010100000000000001101111111101011101110111011101110111011101110101110000000001100000000101000101000001010000010100000101000101010000000001100000000101011101110111011101110111011101110101010000000001100000000101010000010100000101000001010000010101010000000001100000000101010000010100000101000001010000010101011111111111100000000101011101110111011101110111011101110101000000000000100000000101000101000001010000010100000101000101000000000000100000000101111111111111011111110111111101111101000000000000100000000100000000000000000000000000000000000001000000000000111111111111111111111111111111111111111111111111000000000000

ENDOBJECTS

640,480,0,0,640,480,N

ENDCAMERAS

Alien Breed – Stage 2: Creating the Character

The character information has also been taken from the above screenshot. I took the image from the screenshot and rotated it:

[pic]

The image is a 64x64 image, but the map needs the figure to be smaller than that – 32 x 32. Actually, this is beneficial for how you should create graphics for games. As you can see, the edge of the character is a bit blocky. All objects should really be created at twice the size you want to use them at, then when you put them into the program you reduce the size down by 50%. We can therefore update the level file to include this information as well. For size on the page, I’m ignoring the bump map and tile map on this copy!

Level File Information

2880,2304,1

Objects,Character.png,255,255,255

ENDGRAPHICS

PlayerUp,Objects,0,0,64,64,1,N

PlayerDown,Objects,64,0,64,64,1,N

PlayerRight,Objects,128,0,64,64,1,N

PlayerLeft,Objects,192,0,64,64,1,N

PlayerUpRight,Objects,258,0,64,64,1,N

PlayerUpLeft,Objects,320,0,64,64,1,N

PlayerDownRight,Objects,384,0,64,64,1,N

PlayerDownLeft,Objects,452,0,64,64,1,N

ENDANIMATIONS

Background,images\Tiles.png,2880,2304,48,48,255,255,255,0

TILEMAP

ENDBACKGROUNDS

48,48,BUMPMAP

Player1,PlayerUpLeft,PlayerUp,PlayerUpRight,PlayerLeft,PlayerUp,PlayerRight,PlayerDownLeft,PlayerDown,PlayerDownRight,64,64,0,0,0,0,N

ENDOBJECTS

640,480,0,0,640,480,N

ENDCAMERAS

Form Setup

As per the standard form setup, at the moment I only need a single timer and have set the form up as per normal. The only extra line I have put in as a constant is the “speed” for the player – set to 8 at the top of the global variables so that I can alter it easily without having to go through all the code.

In the initialisation of the form, I also need to add in all my event handlers and rescale all the objects down from 64x64 pixels to 32x32.

public partial class Form1 : Form

{

// Set up a Game World:

GameWorld Game = new GameWorld();

const int Speed = 8;

public Form1()

{

InitializeComponent();

SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw, true);

// Set up the game - load in the level:

Game.LoadLevel("Levels\\Level1.txt");

// set up the two event handlers - Object and Wall collisions:

Game.ItemObjectCollision += new CollideObject(Game_ItemObjectCollision);

Game.ItemWallCollision += new CollideWall(Game_ItemWallCollision);

Game.TouchedBumpMap += new ItemComplete(Game_TouchedBumpMap);

Game.ReachedTarget += new ItemComplete(Game_ReachedTarget);

foreach (GameObject item in Game.WorldObjects)

{

item.ObjectSize.Height = 32;

item.ObjectSize.Width = 32;

item.AutoSizeWithAnimation = false;

}

this.Refresh();

}

Drawing the screen at this stage is easy – just draw the camera to the form:

private void Form1_Paint(object sender, PaintEventArgs e)

{ // Draw the game to the screen:

e.Graphics.DrawImage(((Camera)Game.WorldCameras[0]).CameraScreen, 0, 0, this.Width, this.Height);

}

And our timer is going to need to do the following :-

• Set the Player’s middle animation (Standing still) to whatever the current animation object is

• Animate all the items in the environment

• Move the items in the environment

• Change the location of the camera so that if the player gets too close to the edge of what is visible on the screen it scrolls with them

• Check for any collisions between objects

• Redraw the frame of animation

• Refresh the screen.

In code, this looks as follows:

private void timer1_Tick(object sender, EventArgs e)

{

((GameObject)Game.WorldObjects[0]).BaseObject[1, 1] = ((GameObject)Game.WorldObjects[0]).CurrentObject;

// Move on to the next frame of animation:

Game.AnimateItems();

// Move the items:

Game.MoveItems();

// Move the camera:

if (((GameObject)Game.WorldObjects[0]).Location.X-((Camera)Game.WorldCameras[0]).Offset.X > 540-64)

((Camera)Game.WorldCameras[0]).MoveCameraRelative(new Point3D(Speed,0,0));

else if (((GameObject)Game.WorldObjects[0]).Location.X-((Camera)Game.WorldCameras[0]).Offset.X < 100)

((Camera)Game.WorldCameras[0]).MoveCameraRelative(new Point3D(-Speed, 0, 0));

if (((GameObject)Game.WorldObjects[0]).Location.Y - ((Camera)Game.WorldCameras[0]).Offset.Y > 380-64)

((Camera)Game.WorldCameras[0]).MoveCameraRelative(new Point3D(0, Speed, 0));

else if (((GameObject)Game.WorldObjects[0]).Location.Y - ((Camera)Game.WorldCameras[0]).Offset.Y < 100)

((Camera)Game.WorldCameras[0]).MoveCameraRelative(new Point3D(0, -Speed, 0));

// Check for collisions:

Game.CheckCollisions();

// Draw:

Game.Frame();

// and Refresh the screen:

this.Refresh();

}

Key Control

By controlling the speed with Key down and key up, you are already doing indirect control so don’t need to have any arrays like we did with breakout and pong (They were actually unnecessary, but I want to introduce things stage by stage!) I can set the speed of the player to be the constant variable “Speed” as set at the top of the form:

private void Form1_KeyDown(object sender, KeyEventArgs e)

{ // When you click a key, set the value to true:

switch (e.KeyCode)

{

case Keys.Escape:

Application.Exit(); break;

case Keys.Up:

((GameObject)Game.WorldObjects[0]).Speed.Y = -Speed;

break;

case Keys.Down:

((GameObject)Game.WorldObjects[0]).Speed.Y = Speed;

break;

case Keys.Left:

((GameObject)Game.WorldObjects[0]).Speed.X = -Speed;

break;

case Keys.Right:

((GameObject)Game.WorldObjects[0]).Speed.X = Speed;

break;

}

}

private void Form1_KeyUp(object sender, KeyEventArgs e)

{

// When you let go of a key, set the value to false:

switch (e.KeyCode)

{

case Keys.Up:

((GameObject)Game.WorldObjects[0]).Speed.Y = 0;

break;

case Keys.Down:

((GameObject)Game.WorldObjects[0]).Speed.Y = 0;

break;

case Keys.Left:

((GameObject)Game.WorldObjects[0]).Speed.X = 0;

break;

case Keys.Right:

((GameObject)Game.WorldObjects[0]).Speed.X = 0;

break;

}

}

After this, I ran the program to check my player could move about happily in the environment.

[pic]

Change to the Engine

The First problem became immediately apparent after adding the player character – he could walk through walls! There was obviously something wrong with the bump map system, therefore I had to go back into the game engine code in order to fix the problem. It turns out that in all my game engine testing, the objects were exactly the same size as the bump map, and that worked okay. As soon as the object was a different size, the bump map was totally ignored. I rewrote the code, so if you have version 1.0 of the Game Engine document, you will need to replace the bump map code with the following within the GameWorld.cs file:

///

/// Check to see if any part of an object has touched a "1" inside the bump map array. All Four corners of the object are tested.

///

/// The location of the object

/// The size of the object

/// True if it has touched, False if not and can pass through

protected bool CheckBumpMap(ref Point3D Location, ref Size ObjectSize)

{

if ((Location.X > 0) && (Location.Y > 0) && (((Location.X+ObjectSize.Width) / BumpMapSize.X) < (LevelSize.Width / BumpMapSize.X)) && (((Location.Y+ObjectSize.Height) / BumpMapSize.Y) < (LevelSize.Height / BumpMapSize.Y)))

{

// Test the two corners:

if ((BumpMap[Location.X / BumpMapSize.X, Location.Y / BumpMapSize.Y] == 0) && (BumpMap[(Location.X + ObjectSize.Width)/ BumpMapSize.X , (Location.Y + ObjectSize.Height) / BumpMapSize.Y] == 0))

return false;

else

return true;

}

return false;

}

With the bump map now fixed, it was time to introduce some more complex characters into my game – the aliens!

Creating the Enemy

[pic]

The alien I lifted directly from the screenshot as well, and modified it so it can face four directions. I gave it two frames of animation, so it looks a bit better than a gliding creature! The only difficulty I had was getting the enemy to look quite right at 64x64 – in the screenshot it was 84 x 84 pixels, making it slightly bigger than the player.

To add the alien in (I am only adding the one for this example), I added in the frames of animation first, then the alien object. I wanted to make sure I placed the alien down in a room directly in the centre of a background square. As for this example I am also setting its size down to 32x32, and each square is 48x48, I need to find a coordinate that is 8 pixels in from the edge of a square. Looking at the map, I decided to start in the bottom right hand corner of the top left room – square 10,8 on the map. This means the coordinates for the alien are (10*48)+8,(8*48)+8, or 488,392:

Alien,EnemyUp,EnemyUp,EnemyUp,EnemyLeft,EnemyUp,EnemyRight,EnemyDown,EnemyDown,EnemyDown,488,392,0,6,6,0,N

And as you can see here I’ve given the alien a speed of 6 both vertically and horizontally. The next thing I did was alter the code on the form initialiser so that my alien has a target location of exactly where he/she is, by altering the “foreach” loop that resizes all the objects down to 32x32:

foreach (GameObject item in Game.WorldObjects)

{

item.ObjectSize.Height = 32;

item.ObjectSize.Width = 32;

item.AutoSizeWithAnimation = false;

if (item.ObjectType == "Alien")

{

item.TargetLocation.X = item.Location.X;

item.TargetLocation.Y = item.Location.Y;

item.TargetLocation.Z = item.Location.Z;

}

}

Next, I worked out all the coordinates to make my alien travel around in a circle, and added the code to my “ReachedTarget” event:

void Game_ReachedTarget(ref GameObject Object)

{

Point3D CurrentTarget = Object.TargetLocation;

if ((CurrentTarget.X == 488) && (CurrentTarget.Y == 392))

{

Object.TargetLocation.Y = 200;

}

else if ((CurrentTarget.X == 488) && (CurrentTarget.Y == 200))

{

Object.TargetLocation.X = 56;

}

else if ((CurrentTarget.X == 56) && (CurrentTarget.Y == 200))

{

Object.TargetLocation.Y = 392;

}

else if ((CurrentTarget.X == 56) && (CurrentTarget.Y == 392))

{

Object.TargetLocation.X = 488;

}

Object.ReachedTarget = false;

}

Running the game now means I have an alien that walks around in the room – although at the moment, they ignore my player!

Adding Artificial Intelligence

So how do we get the alien to interact with the player? Well, the first thing that has to happen is the alien has to “See” him. How do we do that? Well, first, we can cheat!

We know that if this was real life, if the player goes into the room that any aliens will see him. This means we can use the two squares that make up the doorway at the top of the room as an entry point, and the three squares to the right of the picture below as the second entry point.

[pic]

Secondly, if an alien was standing in a direct line to the player, even if the player was outside of the room, and there was no wall between them, they would also “see” the player.

Once we have that out of the way, we need to tell the computer to change the behaviour of the alien. We have two values attached to our aliens, both set to 0 initiaily. I am going to use the first value for “Changes” and the second for “Behaviour”. If Behaviour is set to 0, the Alien follows its standard path. If Behaviour is set to 1, Changes is set to 0 and set the target location to the centre of the square the player is currently standing on. The alien will then chase the player both across the X and Y axis. The problem comes when the alien collides with the bump map – it cannot get through it, and will keep trying to hit its head against the wall. Therefore, when it hits the bump map we then change the behaviour to 2 – just target the X axis rather than the Y axis, and set the Y value to being in the centre of the current background square. Increase changes by 1, If it collides again, change the behaviour to 3 – just target the Y axis rather than the X axis and set the X axis to being in the centre of the current background square, and increase changes by 1 again. Another collision, go back to 2. Continue until changes has gone up to 50, and if it does, go back to mode 0 – find the nearest point to go back to a “holding pattern”. How does this look in flowchart code?

Summary

Graphic Files for Game

Level File Information

Form Setup

Headers to the File

Global Variables

Constructor

Redrawing the Screen

Timers

Key Control

Mouse Control

Event Handlers

Other Methods

Full Code

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download