Preparations for My First Game Jam
|
Quite unexpectedly, I got an email about an upcoming planned Game Jam event (as part of studies). After some investigation I learned:
Game Maker is a software (includes a free version) that allows easy creation of working games. I downloaded it and went through several tutorials to grip the basics of the software. And hell did I like it! The Making of Shooter 1945 GameThere are some really great tutorials for beginners: http://sandbox.yoyogames.com/make/tutorials They teach you the basics and along the way you build cool working games! Here I’m going to concisely recreate the steps and my impressions when working my way through the second tutorial (in the list above) and making a scrolling arcade shooter. Step 1In a scrolling arcade shooter, the illuaion of motion is achieved when the background keeps moving at a certain speed (usually downwards) and the plane you control stays stationary unless the player moves it. Game Maker (GM) has a lot of functionality that helps easily implement various game features, like the scrolling background in this case. So here’s the first step. We take a simple raster file (this and the other resources are provided in the freely downloadable tutorial material): We make GM aware of this file, i.e. we include it in project hierarchy. As we will want this to be part of the scrolling background, we create a new background. We now need to apply the background to the game world. In GM, we call the game world a room; there may be several rooms comprising the game world. We create a new room in the project hierarchy. In the Backgrounds tab, we uncheck “Draw background color” since we will fill the background not with a particular color but instead with repeating copies of the image. Next, we select the background resource we added above. Notice how Tile Vert and Tile Hor options are by default enabled. This takes care of filling the room with repeating copies of the image. Finally, we set Vert. Speed to 2 which makes the background move at the speed of 2. (by default, my room size is 1024 x 736) This concludes the first step. Now we have an empty game with running background. You can check it out here. (In all examples, press Esc on the keyboard to exit the game.) Step 2Before we proceed to the next step, let me get a few terms out of the way. In game design, a sprite is the visual appearance of an in-game object. You won’t be too wrong if you think about it as a piece of graphics used to represent an object in the game. An object is an element in the game that can interact with other elements. It’s something that can shoot, be destroyed, move around, be picked up etc. A sprite is thus a property of an object. One object can have only one sprite but one sprite can be used by different objects. An instance of an object is one of several identical copies of the object that are present in the game at any given time. If two identical enemies are attacking you at the same time you would say that they are of the same ‘type’, i.e. of the same object. And yet they are two different entities in the game because they may have different health, different positions on the screen etc. The game engine probably assigns different IDs to them. Step 2 is about adding spice to the background. We will add some islands so that as the plane flies over the sea, the same islands will appear at different locations and will give the impression of randomness. Since our islands will come in three ‘types’, we create three different sprites for each of them. Again, we use the resources provided by the tutorial: We create an island object for each of the sprites. Here you need to think about the name of the object (cannot be the same as the sprite name; I noticed that people would usually add “obj_” to differentiate the object name from the corresponding sprite name). Also, set the Depth to something big, e.g. 100. This indicates the number of an imaginary layer the object is on. A layer with number 100 will be drawn on top of objects in layers with higher numbers. Obviously, planes, bullets and explosions will need to be drawn above the islands (they should be allowed to “cover” islands) so their Depth values will have to be smaller (“not as deep down”). Now we need to manipulate these island objects so that they will keep going downards together with the background (at the same speed – so that they do not appear moving on their own) and once they get outside the screen, new islands should come from above at random locations. There is another thing we need to discuss before moving forward. GM uses something called Events and Actions. These can be applied to objects and together allow to manipulate in-game objects in a wide variety of ways – create/destroy them, change their properties, make them interact with other objects etc. An event is something that happens in the game. It may be directly related to that particular object (like object creation, object animation completing) or some generic game event (like player pressing a button, specific amount of time having elapsed etc.). The action is then what should be done to that object if the event happens. One specific event, Step, runs its actions periodically, i.e. every step. GM engine redraws the objects at each step (refreshes the screen, you can think of a step as a single still frame in a film). So here’s what we want. Whenever an island is created, it should start moving in the same direction as the background with the same speed. And this is implemented by the use of events and actions:
However that does not fully describe the expected behaviour of the islands. As mentioned above, we want them to randomly appear from above. Here’s how we implement that. As sson as the island disappears from the screen below, we instantly toss it over above the screen. So it’s still the same island just reappearing above (like the Pacman character reappearing from the opposite side). We could have implemented it so that new islands are spawned above the screen while old ones are destroyed below the screen but for some reason the tutorial uses this technique (seems to me like it’s aimed at saving CPU memory because fewer objects will be created). We need to regularly check the y-position of each island object as it is moving downwards. The moment the y-position exceeds room height move the island over to above the screen just as much that the island would not be initially visible but would appear soon. GM has built-in variables to refer to the rightmost x-position and the bottommost y-position on the visible screen area. They are called room_width and room_height, respectively. Thus we have
So the condition looks like this: “Jump to position” action must follow the if-conditional so that if the condition is met, the action is executed. “Jump to position” takes 2 arguments – the coordinates of where to move the object to. We set the y-coordinate to -65. The island will appear above the screen, exactly 1 pixel above the line y = 0 (since the sprites of the islands are all squares 64 x 64 px in size). For the x-coordinate we use the random function random() whose argument gives the upper bound of the interval from which the random number will be generated (the lower bound always being 0). So the x-coordinate is The island behaviour is now fully described. However, they do not exist in our game yet. We need to add them somewhere in our room. So we simply go to the room and place the three objects there. They exist at the start of the game and start scrolling down then follow the instructions described in object properties. So now we already have the background layer done. Check it out here. Step 3The next step is to add controllable player’s plane. We create a sprite with these graphics:
This is to achieve animation effect. If you inspect the planes more carefully, you’ll see that the propellers look slightly different in each version. If you keep changing the three frames it gives you the impression of the propellers rotating. That’s exactly what GM will be doing with the object that uses this sprite – it will rapidly change the images to create animation for the plane. We create an object for the plane, “obj_plane” and use the sprite discussed. We place the object in the room so that it exists at the start of the game, like the islands:
The plane moves to the left, to the right, backward and forward when the player presses corresponding arrow key on the keyboard. So we add four keyboard events – “Keyboard Left/Right/Up/Down” to “obj_plane” object. To each such event we add a corresponding “Jump to” action which moves the plane a little in the appropriate direction with respect to its current position. Let’s say the plane moves 8 px to the left if you press the left key. So the “Jump to” values would look like this: The y-position is not changed while the x-position is decreased by 8. That corresponds to moving to the left. Notice that the “Relative” box is checked. That is required since you move the plane with respect to its current position. Were the box not checked, the coordinates (-8;0) would refer to absolute coordinates of the room (i.e., the plane would be moved somewhere very close to the upper left corner of the screen). Similarly, right corresponds to (8;0), up to (0;-2), down to (0;2). We use 2 for vertical movement intentionally so that the plane doesn’t appear to move backwards as the background is only scrolling at the speed of 2. The plane should not move outside the screen, so each “Jump to” must only happen if the plane isn’t too close to the screen boundary. So an if-conditional should precede every “Jump to” to check plane position. The sprite is 64 x 64 in size and we would only want to allow its left edge touch the left boundary of the screen, not more. One more sidenote. Sprites occupy many points on the screen not just one, still when we refer to objects we speak about a single point. That point is the origin of the sprite (and as a result, the object). Origin represents the whole sprite/object. You can set in GM which point you will use as the origin. If you open the properties of a sprite, you’ll see this:
With this in mind, the plane should not be allowed to get closer than 32 points to the boundary. Therefore the conditional that checks if the current position is not too close the left boundary should look something like this: For the right boundary, x should not exceed “room_width-32″. Conditions for top and bottom are similar. Specifically for bottom, make the distance bigger since we will display game info like health, score and lives there. That’s it. We’ve got a running background and a controllable plane. Check it out here. Step 4The next step is to implement shooting and add enemy ships. Shooting is implemented via an intermediary object – the bullet. It is a full-blown in-game object that has its properties and interacts with other objects. First we create the bullet sprite and the bullet object that uses the sprite.
1. spawn when player presses the Space key 2. always go upwards 3. destroy enemy ship when hit We add the “Keyboard – Space” event to the “obj_plane” object. The action is “Create”. The object to be created is obviously enough, “bullet” and the coordinates are (0;-16) relative to the ship, i.e. shift the bullet upwards so that its bottom touches the ship’s top (bullet dimensions are 32 x 32, origin centered at (16;16)). For the bullet object, we add “Created” event and “Set Vertical Speed” action which sets the value to -8. We also add an if-conditional to periodically check if the bullet is outside the room and destroy it if it is. This way bullet objects are cleared when they leave the screen and don’t cause memory leaks in the system. Thus we add a “Step” event to the bullet object, a conditional which checks if y <= -16 and a destruction action. We need to make sure the player cannot produce too many bullets, there must be a delay before the next bullet can be fired. We introduce a new boolean variable, “can_shoot” which tracks when the ship can fire a bullet. “can_shoot” must be TRUE for a bullet to be fired when Space key is pressed. Set “can_shoot” to TRUE at the beginning of the game. Set it to FALSE right after (or just before) a bullet is fired. Space presses are effectively ignored now. Wait a given amount of time then set “can_shoot” back to TRUE. Since the variable “can_shoot” has to be TRUE initially, the action that defines it and assigns a value to it must be attached to some event which we know for sure will always happen at the beginning of the game. Both the islands and the plane are invariably created at the beginning of the game so Create event on them would work well. You can attach this variable creation action to “Create” events in either the islands or the plane, it makes no difference. For the sake of coherence, let’s add a “Create” event to the plane object and include defining “can_shoot” variable there. How do we wait a given amount of time? Luckily, GM has a tool called Alarm. An alarm is essentially a timer which can be given name, set to a specific amount of time and when that amount of time passes something can happen. We modify the actions in the “Keyboard – Space” event in the plane object like this: * test if “can_shoot” = TRUE * if TRUE, execute the following block of actions (in that order): set “can_shoot” = FALSE; create a bullet; create an alarm “Alarm 0″ and set it to desired number of steps Use as many steps as you want the delay between two consecutive shots to be. Let’s make the delay 15 steps (0.5 sec under default GM settings, since 30 steps are “taken” every second). Add a new “Alarm – Alarm 0″ event to “obj_plane”. It means “do these actions when Alarm 0 goes off”. The actions amount to setting back “can_shoot” to TRUE since the required delay has been achieved. So here’s what it looks like: (1) is a Create event which sets “can_shoot” to TRUE at the beginning of the game (2) executes the displayed actions – if player can shoot, create the bullet but make them unable to shoot and set the timer (3) when the timer goes off, make the player able to shoot again – set “can_shoot” = TRUE. All the actions that are controlled by the same if-statement have to be enclosed by start/end block actions-parentheses (green triangles shown in the screenshot); individual actions can follow if-statmenets on their own. Time to make the enemies.
Again, we create a sprite and an object for this type of plane. We add a “Create” event to the object and an action “Set Vertical Speed” (set the speed to 4). Now the actions that control the behaviour of this enemy type. We want it to spawn at a random location above the screen, just like the islands. We could place it on the map at the beginning and make it reappear but that would feel strange and repetitive. Plus we will want more than one enemy plane to be on the screen, something which was not required of the islands. So the approach used for the islands does not work so well in this case. Instead we create a fake object, “obj_controller”, that we will use to control the enemy planes appearing. This object itself will not be something that the player can interact with; it’s only required as a container to hold the required actions. So here’s what “obj_controller” events and actions look like:
However, currently only one plane will be created. We need to spawn them regularly. There are possibly many different ways to implement this in GM but let’s do it this way. When the first plane is created, we set an alarm and when it goes off, “Alarm” event (in “obj_controller”) creates a new plane and sets the same alarm again. This way we get a closed loop. Let the first alarm (the one under “Create” event) expire after 200 steps and the repeated one (under “Alarm” event) after 500 steps. So it looks something like this:
Next we need to take care of enemy planes that reach the bottom of the screen. If we destroyed them, there would only be one new plane every 16 secs – boring! Instead we make the planes that reached the bottom reappear at the top – just like the islands. So include a “Step” event in enemy plane object and check for its y-position. When it’s become y >= room_height, jump the plane object to position (random(room_width-16);-16). Finally, we have to define collisions, i.e., what happens when the bullet collides with the enemy plane and when the enemy plane collides with the player plane. In the case of collision of an enemy plane with a bullet, do the following: * destroy the bullet * create an explosion effect at the place of collision and play an explosion sound. (The enemy plane does not have explosion animation. It already uses an animation, that of rotating propellers, so to implement explosions, we will have to rely on a new object – explosion object.) * move the enemy plane object up outside the screen so that it can reenter as a new plane. (This treatment is similar to the islands in the background. We could destroy the enemy plane and instead spawn a new one but that’s the approach taken in the tutorial.) * increase the score by 5 Collision happens between two objects. Which object then should the collision event and the actions be added to? The short answer is that both can be used so it’s a matter of choice. The only thing to note is this: collision is viewed from the perspective of the object to which we add collision events and actions. The object from whose perspective collision is viewed, is referred to as “Self” in GM, whereas the other object participating in the collision is referred to as “Other”. So now it’s clear what should be done. Just import the sound to the project hierarchy as you imported the background; create a sprite and an object for the explosion. Add “Collision” event to enemy plane object. The actions of this event are: play sound, create explosion object, destroy other (referring to the bullet) object; Jump the enemy plane to position (random(room_width-16);-16). One more thing about the explosion object. If we just create it and leave it there, it will stay there forever. It will repeatedly play out explosion animation, just like planes repeatedly exhibit rotating propellers. We need to destroy every explosion object instance after it has finished playing its animation, since it has no use after that. For this reason, we add an “Other – Animation End” event to the explosion object and the action “Destroy the instance” (refer to self).
As mentioned above, the score has to be increased by 5 for each kill. GM abstracts and manages score, health and lives systems by itself. So you don’t have to create and regularly update corresponding variables here. You simply say “increase the current score by 5″ to GM and it just works. So we add one more action to the collision event, “Set Score” and set it to 5. Enable “Relative” checkbox since we want to add 5 to whatever the current score is, not set it to the value 5! Here’s what the final version of the collision event actions looks like:
We make this new explosion object destroy itself after its animation has ended, like in the previous case. We then add one more collision event to the enemy plane and select the “obj_plane” object. The actions to be executed when the enemy plane collides with the player plane are as follows: * destroy the enemy plane (refer to self) * play the second explosion type sound * create the second explosion type object. Here we will do something special: instead of creating an explosion object and destroying the player plane we will change the “obj_plane” object to the second explosion object. * end the game There is nothing special to be said about the first two actions. Simply add them to the collision event. Concerning the player plane destruction. We turn the plane into second type explosion so that controllable player plane no longer exists; once the explosion finishes its animation it destroys itself and we’re done. Why did we not just destroy the plane and create the explosion object there? No idea. That’s what the tutorial says to do. Perhaps it’s in place to implement the same behaviour with only one action (change to) as opposed to two (destroy; create). Saving CPU resources sounds reasonable. And the last bit of all – ending the game. There is the action “End Game”. If you add that one as the last action in the collision event, it will do its job alright. However, you will notice that the game ends abruptly. The explosion animation doesn’t get to play out, in fact the explosion object doesn’t even show up. What you will see is the two planes making contact and then the whole console closing out. That’s because “End Game” action is enforced without any delay, so it sort of ends the game “too soon”. Now the solution, as I thought to myself while experimenting with GM, might be to create an alarm instead of ending the game, add the “Alarm” event to the enemy plane object and end the game when the alarm goes off. That would implement the required wait. (The “Sleep” action which the tutorial mentions has been long removed from GM so you have to resort to the alarms.) But as I discovered the hard way, there’s a pitfall. Pitfall! Let’s assume you setup the alarm as described above: The enemy plane instance is destroyed on collision event. Since the object no longer exists, the events attached to it will no longer trigger any actions (since they too no longer exist). Remaining actions in the collision event are executed successfully and the “Alarm 0″ is setup. But from the perspective of the original enemy plane which was destroyed, there’s no “Alarm 0″ event that could trigger “End Game” action. (And the other enemy planes, which have their “Alarm 0″ events, can’t trigger Game End since they did not collide with anything.) Thus you will never see the game ending when this setup is used. If an object is destroyed, that particular object’s events are no longer valid! The idea of setting a timer and ending the game from within the timer expiration event is OK, it’s just that these actions and events cannot stay with an object that gets destroyed if we want them to be executed. They need to be moved to another object, one that remains intact in the collision event. I believe there are numerous ways of implementing this, some of them probably more effective than what I’m going to suggest here, but as a beginner in GM, I suggest this workaround. Create a fake object, “obj_wait” (again, uncheck “Visible” box, do not add any sprite to it). Replace the “End Game” action in the collision event of the enemy plane with creation of “obj_wait”. And for “obj_wait”, add a creation event. Setup “Alarm 0″ in that event. Add an “Alarm 0″ event and the “End Game” action there.
Now we are completely done. Check out this version of the game here. Just a little sidenote: you may have noticed that we update score all the time but never show it anywhere in the game. Here’s the next version of the game that displays the score at the bottom of the screen. That concludes this post. We have produced a complete game in the sense that it provides challenges (incoming enemy planes) and actions to overcome them (shooting them down). There’s a loss condition (crashing your plane into an enemy plane) but no victory condition. This game while simple, has all the elements to be fully playable. Additional enemy plane types with more interesting behaviours should be added; health and lives should be implemented and displayed on screen. Also, background music and various sounds can be added. However, I’m leaving all those enhancements because from this point on you can add all sorts of things to make the game more fun to play. The point of this post was to show creation of a minimal working game. And it has been done. P. S. to get the orange score which you can see in version v1.75, some coding is required (i.e., it cannot be done with the functionality offered by plain UI). I created another fake object, “obj_score”, placed it on the map and added a “Draw” event. The actions for the Draw event were as follows: * set the color to orange * execute a piece of code |




























