Horse Game

This tutorial will help you create an awesome Horse Game, where you go around collecting apples to get points!

1. Follow the build your game tutorial

To start, follow the game setup tutorial video from the tutorials tab on the codecontest.org website. You should now have a movable character, and be able to have multiple players join your server.

2. Setting up the environment


Setting Game Boundaries and Background

1. Drawing the background

To draw the background the drawBackground function should be used, and the loadImage function should be used. The background image should be added to the code/client/asset/ folder first. Then using a loadImage function, it should be added to the game.js file in the preload method.

// File: code/client/src/game.js
preload(){
	...
	g.loadImage('grass', 'grass.png');
}

After that, still in the game.js file a drawBackground function should be used in the create method, to set the size of the image, and the background for the game.

// File: code/client/src/game.js
create(){
	...
	g.drawBackground('grass', 1, 2000, 2000)
}

The string, should be the name that you gave to your image. The first number can be changed to scale the size of your image, ie. 0.5 for half size. The last two numbers can be changed to customize the width and height of your background.

2. Setting game boundaries

The first step to setting up the boundaries for the game is to tell the server the size of the game. To do this a setBounds function should be used in the onInit method in the room.js file.

// File: code/server/rooms/room.js
onInit(){
	...
	g.setBounds(2000, 2000);
}

Make sure it is written after the setup function. The numbers can be changed to customize the width and height of the game bounds.

3. Getting the camera to follow a character

To get the camera to follow a character the getCharacters function for that character should take in a function as a parameter. The function should use the myId function to find which player the camera should be following. Then, use the cameraFollow function to assign the camera to that character.

// File: code/client/src/game.js
create(){
	...
	g.getCharacters('players',
		(player) => {
			if (player.id === g.myId()) {
				g.cameraFollow(player.sprite);
			}
		});
}

After getting your initial background setup, we are going to customize it by uploading our own background image. Make sure you find or make a background that works for your game! One important detail to remember is that backgrounds should be “repeatable” which means that if you lined a bunch of them up, it would look like one big background!

After you find an image that you like complete the following steps: (Need Help?)

  1. Download the image onto your computer.
  2. Upload the image onto your repl.
  3. Drag the image into the code/client/asset/ folder.
  4. Rename the image to something simple like, grass.png.

Once your image is in your code/client/asset/ folder, all we need to do is change a few lines of code.

Hint: Click on a function name, to get more information on how to customize it!

  1. Change the loadImage function.
  2. Change the drawBackground function.
  3. Change the setBounds function.

After customizing those functions my code looks like this:

// File: code/client/src/game.js

create() {
// Change this line:
	g.drawBackground('grass', 1, 2000, 2000);
// To say this:
	g.drawBackground('grass', 0.3);
}

After making these changes, if you download your code as a zip and upload it to blobbert.io, your game should now have your background in it!

3. Customizing the characters

In this section we are going to replace the default character image with our own custom one! This will follow a similar process to when we customized the background. You can use either Google Images, or make your own!

After you find an image that you like complete the following steps: (Need Help?)

  1. Download the image onto your computer.
  2. Upload the image onto your repl.
  3. Drag the image into the code/client/asset/ folder.
  4. Rename the image to something simple like, horse.png.

Then we just need to add another loadImage function.

preload() {
// Add this new line of code:
	g.loadImage('players', 'horse.png');
}

After making these changes, if you download your code as a zip and upload it to blobbert.io, your game should now show your character image!

4. Rotating the character

In this section we are going to make it so the horse looks where it is going! We will do this by modifying our actions in the onMessage method.

The first action we are going to change is the moveUp action. We are going to change it from an inline function (only one line) to a multiline function (many lines). We can do this by adding curly braces { } before and after our function code:

moveUp: () => {
	g.move(player, 'y', -speed);
},

Now that we have a multiline function, we can add some more code to the function so that the horse is able to rotate!

moveUp: () => {
	player.rotation = 0;
	g.move(player, 'y', -speed);
},

We are going to repeat those same steps for the moveDown, moveLeft, and moveRight actions. Using the following values for the rotation on each: Math.PI, Math.PI * 3 / 2, Math.PI / 2. After we complete these changes our actions should look like this:

const actions = {
	moveUp: () => {
		player.rotation = 0;
		g.move(player, 'y', -speed);
	},
	moveDown: () => {
		player.rotation = Math.PI;
		g.move(player, 'y', speed);
	},
	moveLeft: () => {
		player.rotation = Math.PI * 3 / 2;
		g.move(player, 'x', -speed);
	},
	moveRight: () => {
		player.rotation = Math.PI / 2;
		g.move(player, 'x', speed);
	},
};

After making these changes, if you download your code as a zip and upload it to blobbert.io, your game should now rotate the horse in the direction that it is looking!

5. Adding some apples

In this section we are going to add some apples into the game for the horses to eat! We will do this by utilizing a character set.


1. Uploading an image

First, we need to upload a new image to use for the new character set. To do this we will go into the preload method in game.js and add a new image named after the new character set.

preload() {
	g.loadImage("zombies", "zombie.png");
}

2. Adding the Character

Now we need to add the character to the room.js and game.js files. In the room.js file we need to put the setupCharacters function in the onInit method.

onInit() {
	g.setupCharacters("zombies");
}

Then, in the game.js file we need to put the addCharacters function in the init method. If you want to change the size of your character you can do this in the addCharacters function by adding another parameter. ie. addCharacters('players', 0.5), the lower the number the smaller your character will be: 0.5 would be half size, 2 would be twice as big.

init() {
	g.addCharacters("zombies");
}

And the getCharacters function in the create method.

create() {
	g.getCharacters("zombies");
}

3. Creating the Character

Now you can use the createACharacter function in room.js file to create the new character wherever it is needed. In this example the new character will be created near the top left of the screen and will be created as soon as the game starts. To do this the createACharacter will be placed in the room.js file in the onInit method.

onInit() {
	g.createACharacter("zombies", g.nextCharacterId("zombies"), {x: 20, y: 20});
}

The X and Y values can be changed to position the character anywhere in the game.


After getting the initial character set setup, we need to find or make a custom image for our apples.

After you find an image that you like complete the following steps: (Need Help?)

  1. Download the image onto your computer.
  2. Upload the image onto your repl.
  3. Drag the image into the code/client/asset/ folder.
  4. Rename the image to something simple like, apple.png.

Once your image is in your code/client/asset/ folder, all we need to do is make the following changes:

  1. Change the loadImage function.
  2. Change the setupCharacters function.
  3. Change the addCharacters function.
  4. Change the getCharacters function.
  5. Change the createACharacter function.

After making all of the above changes, my code looked like this:

// File: code/client/src/game.js
init() {
	...
	g.addCharacters('apples', 0.25);
}

preload() {
	...
	g.loadImage('apples', 'apple.png');
}

create() {
	...
	g.getCharacters('apples');
}
// File: code/server/rooms/room.js
onInit() {
	...
	g.setupCharacters('apples');
	const numberOfApples = 10;
	for (let i = 0; i < numberOfApples; i++) {
		const x = Math.floor(Math.random() * this.gameWidth);
		const y = Math.floor(Math.random() * this.gameHeight);
		g.createACharacter('apples', g.nextCharacterId('apples'), { x, y });
	}
}

After making these changes, if you download your code as a zip and upload it to blobbert.io, your game should now have some apples spread around!

6. Picking some apples

In this section we are going to add the ability for a horse to pick up an apple by walking over it! The first thing we need to do is add an initial score for each horse when they connect. This score will represent the number of apples each horse has collect.

// File: code/server/rooms/room.js
onJoin(client, data) {
	g.createAcharacter('players', client.sessionId, { x: 200, y: 200, score: 0, ...data });
}

Next we will need to make it so the horse’s dimensions shift when it changes rotation. This will make it so that our collision detection is accurate from any direction! We are going to do this by adding a bit more code to our actions.

// File: code/server/rooms/room.js
onMessage(client, data) {
	...
	const actions = {
		moveUp: () => {
			player.rotation = 0;
			if (player.width > player.height) {
				const [ w, h ] = [player.height, player.width];
				player.width = w;
				player.height = h;
			}
			g.move(player, 'y', -speed);
		},
		...
	};
}

We will do the same thing for the moveDown, moveLeft, and moveRight actions. On the moveLeft and moveRight actions we will change the if statement to use a less than sign (<) instead of a greater than sign (>):

if (player.width < player.height) {

The last thing we need to do is use the handleCollision function:

// File: code/server/rooms/room.js
onUpdate(dt) {
	...
	g.handleCollision('players', 'apples', (player, apple) => {
		player.score += 1;
		g.deleteACharacter('apples', apple.id);
	});
}

Once all these changes are made, my code looks like this:

// File: code/server/rooms/room.js
onJoin(client, data) {
	g.createAcharacter('players', client.sessionId, { x: 200, y: 200, score: 0, ...data });
}

onMessage(client, data) {
	...
	const actions = {
		moveUp: () => {
			player.rotation = 0;
			if (player.width > player.height) {
				const [ w, h ] = [player.height, player.width];
				player.width = w;
				player.height = h;
			}
			g.move(player, 'y', -speed);
		},
		moveDown: () => {
			player.rotation = Math.PI;
			if (player.width > player.height) {
				const [ w, h ] = [player.height, player.width];
				player.width = w;
				player.height = h;
			}
			g.move(player, 'y', speed);
		},
		moveLeft: () => {
			player.rotation = Math.PI * 3 / 2;
			if (player.width < player.height) {
				const [ w, h ] = [player.height, player.width];
				player.width = w;
				player.height = h;
			}
			g.move(player, 'x', -speed);
		},
		moveRight: () => {
			player.rotation = Math.PI / 2;
			if (player.width < player.height) {
				const [ w, h ] = [player.height, player.width];
				player.width = w;
				player.height = h;
			}
			g.move(player, 'x', speed);
		},
	};
}

onUpdate(dt) {
	...
	g.handleCollision('players', 'apples', (player, apple) => {
		player.score += 1;
		g.deleteACharacter('apples', apple.id);
	});
}

After making these changes, if you download your code as a zip and upload it to blobbert.io, you should now be able to walk around collecting the apples!

7. We need more apples!

The last thing we are going to do in this tutorial is make it so that the apples re-spawn after they have all been eaten! This way the game can go on forever! The first thing we need to do is move the code we used to create the first apples into a method so that we can use it again to create more!

// File: code/server/rooms/room.js

// Scroll down until you see this code:
	g.setupCharacters('players');
	g.setupCharacters('apples');
// Then delete this code:
	for (let i = 0; i < 10; i++) {
		const x = Math.floor(Math.random() * this.gameWidth);
		const y = Math.floor(Math.random() * this.gameHeight);
		g.createACharacter('apples', g.nextCharacterId('apples'), { x, y });
	}
// Stop deleting here.
}

Then we will create a new method called createApples underneath our onInit method:

// File: code/server/rooms/room.js

// In that same spot:
		g.setupCharacters('players');
		g.setupCharacters('apples');
	}

	createApples() {
		for (let i = 0; i < 10; i++) {
			const x = Math.floor(Math.random() * this.gameWidth);
			const y = Math.floor(Math.random() * this.gameHeight);
			g.createACharacter('apples', g.nextCharacterId('apples'), { x, y });
		}
	}

	onJoin(client, data) {
		...
	}
	...

After we add our createApples method we can call it anytime we want and it will add new apples into the game! The first place we want to use it, is in our onInit method where we were originally creating the apples:

// File: code/server/rooms/room.js

// In that same spot:
	g.setupCharacters('players');
	g.setupCharacters('apples');
// Add this new line of code:
	this.createApples();
}

The last thing we need to do is check for when we run out of apples. We will do this by adding a little bit of code to the end of our handleCollision function:

// File: code/server/rooms/room.js
onUpdate(dt) {
	g.handleCollision('players', 'apples', (player, apple) => {
		player.score += 1;
		g.deleteACharacter('apples', apple.id);
// Add these lines of code:
		if (Object.keys(this.state['apples']).length === 0) {
			this.createApples();
		}
// End of the new code.
	});
}

After making these changes, if you download your code as a zip and upload it to blobbert.io, the apples should all re-spawn after all of them have been eaten!

8. Moving forward

Congrats! You finished this tutorial! But that doesn’t mean your game is done! You can still customize, add new features and improve it as much as you like. What’s Next?


Need Help?

Proofread your code, check with your team and classmates, and if you’re still stuck!

Join us on Google Meet