Space Quest: The Beginning

1st Tutorial

Welcome, aspiring game programmer! I shake you warmly by the hand (actually, that's one of Willy Wonka's lines). We will learn to design our first game level in this tutorial.

We will work with a WYSIWYG level editor which can be downloaded for free from here: http://www.3dgamestudio.com/

Install the application, and then run it. Then, create a room that is centered in the origin. Use a total of five blocks; you will need to keep a small opening at the top, because that's how our player will leave the current game level.


Here's how my level looks now. I have also added the exit gate, which is created as a separate prefab; that's why it has a different color in the level editor. We can't add actions/functions to regular walls; however, we can assign them to entities (3D models, sprites, and level prefabs).

To create the gate, start a new level, add a wall that's got the proper size, build it, and then save it. Then, open the old level and add the gate as a prefab. If its size isn't right, you can recreate it or (my recommendation) adjust the surrounding walls.

When you are done, build the level, and then run it. Press the "0" (zero) key to unlock the camera, and then fly in the level to see what you've created. Next time we will add some code for our player, power-ups, boulder and exit gate.

Writing Our First Function

2nd Tutorial

Welcome back! This time we will start to write our own code. So, open your favorite code/script editor, and then type in the lines of code in the image. You can download Notepad++ if you've never used it before; it is really easy to use and very powerful.

As you can see, the lines of code are enclosed within a pair of winged brackets; that's how you start/end a function or action. By the way, functions are executed on their own, while actions can be attached to various entities. To give you an idea, we will attach an action to the exit gate.

Everything starts with function main; without it, there's no way for the code to be executed, so our game wouldn't do anything. Always make sure to create a function main() in your games, and have it include instructions that initiate things, load levels, etc. One more thing: main() is of type void because it doesn't return any value; we don't want to evaluate and reuse the result of its execution.

The first line of code sets a black sky color; we can use RGB values which range from 0 to 255 here, with 0 being the darkest black, and 255 the brightest white. Then, we limit the frame rate to 70 frames per second. I know that modern PCs deliver much more than that, especially when we are coding a simple game, but we need to think about the guys who own older computers as well. By limiting the frame rate to 70 fps, everyone will have a nice, smooth experience; don't forget that most gaming consoles do the same thing, limiting the frame rate to 60.

The following lines of code set the video resolution (mode 8), color depth (32 bits) and screen mode (full screen). We wait for a frame, and then we evaluate if the game soundtrack is already running or not. If it doesn't run, we start playing it in a loop, and then we load the game level we've created in our previous tutorial. Finally, we wait for 3 frames to make sure that the level has been loaded in its entirety, and then we set the camera angles in a way that makes it focus on the player. By the way, the camera is our eye in the level, the way we're seeing what's happening in the game.

It's time to create our player. You will need a player model (a spaceship), which can be downloaded for free using the same resource site which was mentioned in the first tutorial. Choose any model you want, its shape doesn't matter. We will also need to create a target for our player, an entity that will guide it as it moves around in the level. We will make this entity invisible, of course.

The player will fire a bullet whenever we press the left mouse button; this way, we'll (hopefully) be able to defeat the enemies. The last lines of code inside our function will keep the camera pointed towards the player at all times, making sure that it is fully visible each and every frame.

That's all for now! Duplicate my code and then save it as a .c file. We will add all the missing pieces next time.


Coding The Player Function

3rd Tutorial

It's time to code player's function! The first line waits until the virtual entity that guides the player around is created; otherwise, the code would look for it without finding it, and the game would crash. We define a few variables, and then a couple of vectors.

A variable can store numerical values, while vectors group three variables together. You would use a variable to store the number of bullets, and a vector to store player's X, Y, and Z coordinates, get it?

The player will use a polygon-based
collision detection mechanism. Most games use simplified collision detection shapes (spheres, ellipsoids, etc.) but since we're going to activate "real" collision for a single entity (player's ship), performance won't take a hit. We will also set player's FLAG2, because we want to get access to its properties from within other functions. By activating FLAG2, we're creating a unique entity.

We'll use a while loop to run player's instructions; that loop will continue to do its job for as long as the player is alive and its height doesn't reach -100. You will see why I coded the things that way later on.

The player will constantly turn toward its virtual target, which is placed in front of it. If the player needs to make a sudden mouse move (to avoid an enemy that's really close, etc.) the code will allow it to do that by rotating the ship faster; otherwise, if the mouse movement isn't fast, the code will change player's pan angle gradually.

We are constantly monitoring the distance between the player and its virtual target; we want to make sure that it is always kept below 50 units. If it exceeds this value, we move the player towards the target, ignoring passable entities and the player itself. I will teach you how to code engine exhaust particles in a future tutorial; the code that computes their position is already included in player's function. We will code particles that are generated 20 units below player's ship and 5 units above its origin; otherwise, they would be generated inside the player model, so they wouldn't be visible.

The last code section checks player's y coordinate. If the gate opens, the player will be able to exit the level. In this case, its z coordinate will be set to -100 units, and the level number will be increased. The player is now ready to move on to the next level.

Auxiliary Player Functions

4th Tutorial

Welcome to a new tutorial. This time we will code a few functions that may not look that exciting, but are essential. The first one controls the virtual target that sits in front of the player, helping it rotate and move around. Sure, we could have created a function which moves player's ship directly, but I guarantee that its movement wouldn't have been that smooth; it is much better to create an invisible entity in front of the player, and then have it move towards it, adjusting its speed and angles gradually.

Okay, let's explore the code. So, the first function is attached to our invisible player target, which was also made passable; this way, we prevent it from getting stuck by colliding with enemies, space rocks, etc. The target is displaced when you move the mouse around; tweak those 50s if you want to change player's movement speed.

The next function is activated when the player collides with something. You don't want that to happen too often, because whenever this function is run, the player loses a life. When this unfortunate event happens, we make the player insensitive to collisions. Since a typical collision can last for a few frames, we'd subtract much more than one from players_lives, and we don't want that to happen.

It's time to create an explosion; you will need to use an explosion sprite with 9 frames here, which can be downloaded for free from the Internet. If you can't find a sprite with 9 animation frames, you can use one that's got a different number of frames, of course; just replace the 9 from "while (my.frame < 9)" inside function player_explodes() with your sprite's number of frames.

Since the player has died, it makes sense to play a dead sound. Then, we wait for 2 seconds, we make sure that the player doesn't move the mouse anymore, and then we reset player's position, making it sensitive to collision with the level entities again.

Weaponizing The Player

5th Tutorial

This tutorial will teach you how to code player's weapon. As you may remember from one of the previous tutorials, player's ship will fire bullets when you press the left mouse button. To make this happen, I created function players_bullets().

The first line of code ensures that the player is alive; it doesn't make any sense to fire bullets if you are dead, right? Then, we set up a vector which will store the coordinates of our bullets. We want to create the bullet in front of player's ship; otherwise, it will collide with it, making the player die without being hit by an enemy projectile - how lame is that?

This is a 3D space game, so we'll use a 3D projectile named laser1.mdl. Once again, you can use TurboSquid or any other 3D game model site to get the file for free. We also play a laser firing sound, which lasts for one second.

Bringing Particles To Life

6th Tutorial

I know it's been a while, but I have finally managed to find the time for a new tutorial. This time we will learn how to create particle effects. We have used sprites before (remember the explosion effect?) but the truth is that particles run much faster. We can easily create fantastic effects that use 10,000 particles, but even modern computers would struggle while trying to render 10,000 sprites at the same time. I hope that you are as excited as I am - let's get started!

The first function fades the fire/engine exhaust effect; it decreases the alpha (transparency) value until it goes below zero. Believe it or not, particles can live even if they aren't visible, so we make sure to set their lifespan to zero - this destroys them, freeing up the video card for more work (new particles).

Function fire_effect() sets random X and Y velocities for our particles; we don't want to touch the Z component, because it would bring the particles closer to player's eyes. The transparency value ranges from 25 to 75, and the bitmap used for the fire effect is a tga file. You already know the drill; you'll find lots of bitmaps like these on the web. The initial size of the particle is 25 units, and its BRIGHT and MOVE flags are set. The last line of code sets the event function - the one discussed above.

Function fade_star() is the event for the one below it; it is very similar with function fade_fire(), so there's no need to discuss it.

The last function in this tutorial is star_effect(). Just like the ones above it, it takes a particle pointer as a parameter, and then sets the alpha, bitmap, size, flags and the event function.

The code uses a random transparency value, which ranges from 5 to 55, while the size of the generated particles ranges from 2 to 3. This way, we get a great star field effect, even though it uses a single bitmap.

Power-Ups And Asteroids

7th Tutorial

Welcome to a brand-new tutorial! This time we will code the power-ups that need to be collected and the asteroids that should be avoided.

You will notice that we are going to use actions this time; we need to do that, because we will attach them to entities. The first action is attached to the power-ups; the gate to the next level won't open until the player has collected all the items.

Each power-up is passable, and we use the total_power variable to store the number of available entities. The code waits until the player entity is loaded, and then sets the height of all the power-ups to player's Z coordinate; otherwise, if we tried to place them in the level, some of them may be above or below player's height, making it difficult for him/her to collect them all.

We want to make those entities look as random as possible; that's why they use random pan, tilt and roll angles. Each power-up is driven by a "while" loop which rotates the entity and keeps running until the distance between the player and the power-up is less than, or equal to 30 units. As soon as that happens, we play a sound effect, we increment players_power, and then we add a random score that ranges from 8 to 12. The power-up will be made invisible, and it will disappear for good after 3 seconds.

The action attached to the asteroid waits until the player shows up, aligns its Z to player's Z, gets a random orientation angle and keeps spinning all the time. Since this is a deadly asteroid, the loop will continue to run for as long as the entity exists; we don't want to end it prematurely, because it will be a permanent threat.

That's all for today! And here's your homework: make these power-ups and asteroids look more random by setting different sizes for each one of them. You will have to play with the entities' scale_x, scale_y and scale_z parameters, but I promise that the code is quite simple. Good luck!