Contact: Rando Wiltschek
Game creation tutorial
Welcome back to the game creation tutorial. I hope you could follow the instructions so far. In this section we'll add some challenges to the game.
What you do in this section:
- Deploy laser cannons
What you learn while doing so:
- More ways to make objects move
18. Laser cannons
Completed the last section successfully? Then open your file now, else open "bermuda_06.mfa" and continue from there. In this chapter we will add laser cannons to the level.
Remember how we placed the corals in the level? Now we do the same with the "laser cannon" object. Make sure that in the "View" menu "Snap to grid" is turned off. Now add instances of the laser cannon in the level. The general idea is that there aren't too many of them, (not more than one per two screens in average) and very few in the beginning and more towards the end.
Hold down ctrl while dragging an object to make copies of it.
Use the arrow keys to nudge an object pixel by pixel.
Make sure you delete the one outside of the frame in the end.
Create a new group "Laser cannon" and move it above the "Display" group. Open the "Player only" group and copy the event where the player collides with the background:
Paste it in the "Laser cannon" group and change it to this:
If you wonder what this event did:
+ Flash of Player >= 0
It makes sure that the player is not flashing anymore (invincible after respawning).
The actions stay the same (wreck the player a little).
For your convenience, the objects already have all the animations, which we can use to solve most of the timing issues. Add the following events:
+ Special - Limit conditions - Restrict actions: 0, 0, 1, 10
> Laser Cannon - Animation - Change - Animation sequence..: "Charge"
+ Laser Cannon - Animation - Has an animation finished? "Charge"
> Create object: "Laser" at X: 0, Y: 19, relative to "Laser Cannon"
+ Laser - Animation - Has an animation finished? "Stopped"
> Laser - Destroy
The first event will play the charge animation every 90/100 second. The second event creates the laser object, when this animation is over. The third event removes the laser object, when it's animation is over. So now we have to check if the player touches the laser beam. Copy the event where the player touches the laser cannon and paste it at the end of the "Laser cannon" group. We can easily modify this one: Change "Laser cannon" to "Laser" by double clicking on it's icon. Then remove the action "Bounce".
That's it, pretty cool huh? The laser cannon poses quite a threat to the player now. If you think it is too hard, change
+ Special - Limit conditions - Restrict actions: 0, 0, 0, 90
to something a little bit higher.
To be thorough, let's add some more objects that can be damaged by the laser:
That was the laser cannon. Let's see what's next!
Alright, let's make this sea a bit more alive. If you examine the "fish" object, you'll notice that there are four animations used. The "stopped" animation contains all the colors of the fishes, but is only temporary. Probably I should have deleted it, but hey, who can think of everything? ;)
The other animations are at the very end of the animation list. One for each color. They already contain frames for the left and right direction. If you feel like it, you can add more types of fish by adding new animations at the end. They occupy the animation slots 12, 13 and 14. This will be important when we get to the code.
Open the properties of the fish object and go to the RunTime Options tab. Change "Inactive if too far from window" to "Yes". This will save some performance and also prevent the fishes to come to the view too soon.
Go to the movement tab. Change the type to "Bouncing Ball" and speed to 0. You can leave everything else alone - we will set it up by code.
Create a new group and call it "Fish". Open the "Mines" group and copy the first two lines of events ("Start of frame" and "On loop Create mines"). Paste them in the "Fish" group.
Modify them now. In the first event, edit the action and change "Create mines" to "Create fish" and 40 to 150.
Should the game run slow later, change the number to something smaller.
In the second event, replace the "Create object" action. Let it create a "fish" object somewhere outside the screen. Now drag the actions on the mine object to the fish object while holding down shift. This will move them instead of copying.
Holding shift while dragging events moves them instead of copying.
Add some more actions:
> Fish - Animation - Change - Animation sequence..: Use a calculation: 12 + random(3)
This will change the color of the fish. Remember the animation slots 12, 13 and 14? This is where we select them.
The function "random(n)" returns a value between 0 and n - 1.
> Fish - Direction - Select direction..: 14, 15, 16, 17, 18
This lets the fish go to the left when they start.
> Fish - Movement - Set deceleration..: random(20)
Deceleration defines how fast an object loses speed. We don't want the fish to go at constant speed. So this comes handy.
> Fish - Order - Bring to back
You should have this now:
Now we have fishes all over the place. We just have to make them move. Here goes - add this event:
Everytime fishes go slower than "7", one of them is selected and gets a new speed and it's direction is modified slightly. Add this event to add fishes that go to the right:
If a fish leaves the screen to the left, it's color is changed, it is turned to the right and it's height is changed. This way it looks like a brand new fish. You can copy the actions for this event from the event where we create the fishes. Just make sure you remove the action that sets the X position and you modify the direction.
I almost forgot - we also want smaller fishes!
So add the following action to the event that we just created, and to the event "On loop 'Create fish'":
> Fish - Scale / Angle - Set scale: (Random(61)+40)*0.01 in the first dialog, and 0 in the second
The scaling is expressed proportionally to the original size (which would be 1.0). Our formula creates a random value between 0.4 and 1.0, so the fish will be 40-100% of original size. The 0 in the second dialog tells MMF to use a faster method. If you use 1 instead, the scaled fish will look less ugly, but at the expense of computation time.
Run it. Now ain't that nice!
Set the UFO's movement like this:
At the time of writing this chapter, Clickteam added a nice new option to Active Objects' RunTime Options called "Create at start". This comes very handy for us. If we untick it, the object will not be created just because it sits on our frame. It only gets created by code.
So please go and untick it for the following objects:
UFO, Fish, Torpedo, Energy Ball, Mine, Magnet Ray, Enemy, Bubble and Laser.
They all should only be created by code (I've already seen the fish swim in from out of the frame a couple of times..).
Okay. To make things a little harder for the player, we'll create the UFO 1.5 seconds after the player has picked up a treasure. This can be achieved many ways. But here I want to introduce a handy extension called the "Function Eggtimer Object". It comes with one of the bonus packs, so it this point you should already have it.
So, go to the frame editor, right click and select "Insert Object". Press "f" then "u" on the keyboard and you should have it. Select it if it's not selected and press "OK".
In the Object List, drag the Function Eggtimer into the System folder.
This extension will not be visible in the game and it does not matter where you put it in the frame.
Let's go to the event editor and find the event where the player picks up a treasure.
It's the second event in the "Treasure" group.
Add this action:
> Function Eggtimer - Queue Function: "Create UFO", 1.5 * FrameRate
We have used loops before. Basically this is what we do here too. The function eggtimer calls it's own loop system. So "Create UFO" specifies the name of the loop to call, and 1.5 * FrameRate specifies when. The function eggtimer uses frames to measure time. Currently the frame rate is 50, so for 1.5 seconds we'd use 75. But if we change the frame rate later, we still want it to be 1.5 seconds. So we use the MMF keyword "FrameRate", which returns 50 in our case.
The keyword "FrameRate" returns the amount of frames per second.
Create a new event group and call it "UFO". Move it above the "Display" group.
In there, create a new event:
+ Function Eggtimer - On function: "Create UFO"
As with loops, make sure you spell it exactly like before. MMF is case sensitive.
Now we want to add an action to create the UFO. As it moves to the right, it should be placed somewhere left to the screen. If we use a fixed position, it will only work well in the beginning. So it should be relative to the view. One way is to create it relative to the player, but then it will vary in height. The most common way would be to create it relative to the camera, because the camera is always in the center of the view.
But we will make use of the fact that the perspective object always moves with the screen - so we create the UFO relative to that. Add a "Create Object" event and select the UFO to be created. Now click "Relative to:" and select the "Perspective" object and click "OK". Enter X = -30 and Y = 140.
If you test the game, the UFO should now appear short after picking up a treasure. To prevent that the UFO comes too often at once, add this condition:
+ Special - Limit conditions -Restrict action: 0, 0, 5, 0
Now there can only be one in 5 seconds.
Let's make it shoot. Add this event:
+ the timer - Every: 0, 0, 1, 0
> UFO - Shoot an object..: Energy Ball, Speed = 20, Shoot in direction of...: Relative to: Player, Options: Located = In direction of Player, X = 50, Y = 0
The nice thing when using "Shoot an object.." is that you don't have to give that object a movement. It will go in the specified direction in a straight line. You may wonder why we aim 50 pixel to the right of the player and not at him directly. Now the trick is this: By activating "Located in direction of Player", the offset we specified (X and Y) is calculated for the direction of the Player object. When you set it up, it assumes that the player is facing right.
This means, the UFO will always aim 50 pixel in the direction that the player is facing! This makes the shots a bit less predictable and harder to evade. If we wanted to make it perfect, we could use the player's speed to calculate where he is going exactly and fire the shot there.
The following events are not particularly interesting and you should be able to create them without trouble:
What we do here is let the Player shoot the UFO with his torpedos. It will grant 500 points, as it is fairly hard to achieve. It will also shake the screen to indicate the hit. The UFO can also receive damage from mines and the laser, but it is purely visual.
Also add these two events. They damage the player when an Energy Ball or the UFO hits him.
Pay attention that the "Start loop Damage player 1 times" action is the last action for each event.
Unless you know exactly what you're doing, you should always run loops as the last action as it often causes undesired effects.
Running a loop deselects all objects. So an action does not affect the desired objects but all of their type.
That's the UFO! As you may notice, it already makes the game a lot harder.
For your convenience, here's "bermuda_07.mfa".
21.1. The plan
In this chapter, we will design and create the enemy submarine. The idea is to make it a "boss". So at the end of the level, the player has to defeat it, to continue.
The enemy can only take one single hit until he sinks. He will also be able to shoot torpedos, like the player. The camera will stop, when the enemy comes into sight, and the level ends, when the enemy is destroyed.
For the sake of simplicity, we will make sure that there are no mines around the enemy. He will dodge the player's torpedos, and try to shoot when the player is at his own height.
Okay, let's get started!
Go to the frame editor and take the enemy from outside the playfield. Drag him to the end of the level and place him somewhere in the center, before the level ends.
While you drag an object, get close to the edge of the frame to scroll in that direction.
I place the enemy in range of a laser cannon. So when the player encounters the enemy, the laser cannon will pose an additional threat (as if the enemy wasn't hard enough..).
Open the properties of the enemy object. In the "RunTime Options" check "Create at start" and "Use fine detection". Open the "Values" tab and add an alterable value that you call "Activated" and one that you call "Y_Speed".
21.2.2. A new torpedo
We need a second torpedo for the enemy. We could reuse the torpedo of the player, but we'd have to revisit many events and add checks for which direction the torpedo goes or who fired it.
To avoid that, we just clone the torpedo into a new one. So go to the level editor, right click on the torpedo and select "clone". You will be asked how many rows and columns you want. Leave those settings like they are and click "OK". Now you have a new torpedo called "Torpedo 2". Put it into the "Objects" folder and open it's properties. In the movement tab, set "Direction" to 180. This will make it go in the opposite direction.
The vector movement uses 360 degrees, where 0 points to the right.
Most movements use 32 directions instead. You can translate them to 360° by multiplying with 11.25 or back by dividing with the same value.
The new topedo is ready to use.
21.2.3. Torpedo checker
We want the enemy to avoid the torpedos fired by the player. One way to do so is to create an extra object, that will follow the enemy around and check for torpedos.
This kind of object is commonly refered to as "sensor".
The main disadvantage of this solution is that if you have more than one enemy relying on such an object, things become a lot more complicated. Usual solutions are using alterable values of the sensor to communicate its status, or to have one sensor per enemy and iterate enemies and sensors with a loop, or to have one sensor and move it to each enemy in a loop to make a check there.
Fortunately we only have one enemy of this kind, so we can avoid the hassle. If we wanted to expand this game, we'd either have to make sure there is only one enemy at a time or find a solution that works for multiple enemies.
Right click on the frame area in the frame editor and select "Insert Object". Select the "Active" from the list and click "OK".
Left click in the frame area to place the object. The position does not matter. Right click on the object and select "Rename". Name it "Torpedo checker" and click "OK".
Double click on the object to edit it's animations. In the toolbar, click on "Size". Enter 128 for "w" (width) and 64 for "h" (height). Click "Apply".
Click on the "Clear" button. Now click on the "Fill" tool. Left click on a color from the color palette. It does not matter which color you choose.
|Now click at the center of the picture, to fill with that color. Click on the "Hotspot" button. This tool allows you to specify the center of the object. This determines the relative position of the picture to the object's coordinates and the center of rotation. From the "Quick Move" buttons, click on the one at the middle right. This should change x to 127 and y to 32.||
The hotspot is the center of an object and determines its position and rotation point.
The action point is the position from where objects are shot from. It can also be retrieved as an expression.
Okay, we're done here. Click on the "OK" button to close the animation editor. Put the "Torpedo checker" object into the "System" folder and open it's properties. Uncheck "Use fine detection" in the RunTime tab. As the object is a rectangle already, fine detection (pixel perfect) would not make any difference, so we can save the resources. Also uncheck "Destroy object if too far from frame". This is unlikely to happen but we better make sure.
21.3.1. AI Agent
Artificial intelligence in computer game terms means that the character possessing such intelligence avoids doing things the player would consider stupid, while doing things the player would expect that character to do. He does not really have an intelligence in the sense that he would think about how to do things or why to do things. He would rather compare various preprogrammed options according to their suitability at the given time (e.g., is it better to take cover or to fire some more shots?) and then use a pattern of actions to accomplish his decision (e.g., throw a grenade (for distraction), find closest cover, run there, crouch, wait, make next decision).
In general it is easier to make an AI agent too difficult than too easy. While the player can easily outsmart your average NPC (non player character), the AI has a much higher precision and faster calculation capabilities than a player and often also access to more information than the player. It is usually not sensible to simulate a player's way of perception (e.g. analyse the color and shape of pixels to determine if there is something to shoot at) but instead access the underlying data (iterate through all objects that could be shot at and see which ones have a clear line of fire from the AI agent) and then add delay (even though the enemy was aware of the player the split second he came around the corner, he pretends not to see the player immediately) and noise (the AI agent could blow the player to pieces with one super precise shot, predicting the player's every move, but instead he might slightly miss, based on random numbers). This way is much easier and faster to compute and allows the AI agent's difficulty to be scaled (he could miss more often or take longer to see the player).
In our case we do not have so many options what the enemy submarine might do. First we make the enemy as tough as possible, and if you want you can add delay or noise yourself later.
Effectively the enemy has three options: Shoot, go up, and go down.
Because he will only be able to take one single shot, his priority will be survival. So if the player shoots a torpedo, he will evade it at any cost, going either up or down. If there is no threat from a torpedo, he will try to get level with the player, so he can make a good shot himself. Like the player he has a delay until he can shoot. So once that delay has passed, and he is at one height with the player he will fire his torpedo. That's all he can do.
Let's make sure everything is set up for the fight. Create an event group "Enemy". Add these event to clear out the mines around the enemy and make the camera stop when the enemy appears:
Is the mine right of the enemy minus 100 pixel and left of the enemy plus 100 pixel?
"X Right Frame" is a function of the storyboard that allows you to retrieve the X position of the last visible pixel at the right side of the view.
We set the enemy's alterable value "Activated" to 1. In later events we check for this and only make him do things when he is activated.
Add this event to always place the torpedo checker at the enemy.
For testing, add the following event so you can jump straight to the enemy, without having to play the entire level:
If you run the game now and press enter, you will be taken straight to the end. Note how the Torpedo checker is aligned to the enemy with its hotspot.
21.3.3. Move & Shoot
Now we want the enemy to move to the height of the player - unless there is a torpedo coming for him. So we compare the player's Y coordinate with the enemy's. We set the alterable value that we named "Y_Speed". It will determine how far the enemy moves every frame. This way he can accelerate and decelerate. You will probably remember this approach from previous custom movements we made. Add these two events:
The opposite event would be when there actually is a torpedo coming in (the range of the torpedo checker). In this case we want the enemy to move out of harm's way. This time we compare against the torpedo's Y position to make the enemy move the right direction. Add these events:
Now we want to actually make the enemy move, according to the Y_Speed that we have set. Additionally we want to give it a maximum speed and smoothen the movement a bit. Add the following event, I will explain how it works as you go:
We only want the enemy to move when he is not destroyed. So we check which animation he is playing. You may wonder why we don't check his Alterable Value "Activated". This is because he is activated when he comes in range - so if the player shoots some torpedoes ahead, the enemy will not be able to evade them.
We "clamp" or "cap" the Y_Speed, using the functions Max(x,y) and Min(x,y). They are explained in section 2, 10.2. So what happens is that with Min and Max we restrict Y_Speed to never be larger than 25 or smaller than -25. Then we multiply it by 0.95 to slow down the movement gradually (every frame).
In the second action, we actually change the enemy's position, according to the speed. Usually I would use another alterable value (X_Pos, Y_Pos) to store an object's position as a floating point value to get a smoother movement. But in this case it works good enough.
The next event is to make the enemy fire a torpedo when he is at the right height. So we check if he's activated, if he's at the height of the player (+- 15 pixel) and if the player is still alive. We restrict him, so he can only fire a torpedo every half a second, like the player. Add the event. Make sure the torpedo is created at the action point of the enemy, so it appears at the right height.
This event should be pretty self-explanatory. "X Left Frame" gives is the exact position if the most left pixel that is visible on screen.
|The event when the player is hit by the enemy's torpedo is very similar to being hit by a mine. Remember to check the Flash value, to see if he is not invincible.||
|Lets quickly add some more collisions for the torpedo: mines, backdrops and the laser beam.||
What happens when the enemy is hit by the player's torpedo? He is destroyed, deactivated and the player gains some score.
Now the last event, which is almost identical to the player's events:
If the enemy is destroyed, make him sink.
If he gets low enough, stop him and make him part of the scenery.
Run the game and test the enemy! Remember that by pressing "Enter" you can jump directly to the end.
If everything works correctly, select the "Torpedo checker" object and untick "Visible at start" in the Display Options.
Find this event and delete it, or add a "Never":
This was the final section of this tutorial. If you could follow to this point, I'm confident you are now very capable of creating your own project or build up on this one. As you may have noticed, there are many things left that you can do:
- The enemy is barely beatable, make him easier, or give the player an alternative
way of beating him
- There is no main menu or startup screen yet
- The game needs sound effects and music
- There is only one level
- There is space for more obstacles and enemies before the player reaches the final one
- There is no highscore screen or "end of game sequence"
So, good luck and success with your own endeavours!
Now, get started!
If you had trouble recreating everything, feel free to continue from the file "bermuda_08.mfa".
|This is the end of this tutorial. I hope you have enjoyed it. We have implemented laser cannons, fishes, UFOs and a hostile submarine.|