Advertisement
Implementation

Let's Prototype a Game: Trainpunk

by

Making quick, simple prototype games is a great way to test out new gameplay ideas quickly, without committing to creating a full-blown game. I like to use Game Maker for this, and in this post I'll share one such game with you: Trainpunk, a train-based strategy game.

Editor's Note: This is a new type of post we haven't tried before. Please let us know what you think, and whether you'd like to see similar posts in the future!


Concept and Gameplay Demo

Here's a video of me sketching out the original concept for the game, and then of me playing the prototype game itself:

I'd show myself drawing the art as well, but it's so simple that there's no real reason to.

In the final game, two teams of trains try to attack the enemy base while being shot at by turrets. You play by applying upgrades using the buttons at the bottom.

Please download the final game (plus the source code and sprites) and tweak the gameplay as much as you like! You will need a copy of Game Maker 8.


Building the Game

Here's a rough outline of the steps I took to build the game. You could follow along from scratch, or use this to better understand the full source.

Step 1

Download Game Maker 8, and open it.

Step 2

Create a new GAME object, with these properties:

  • Sprite: "No sprite"
  • Solid: false
  • Visible: true
  • Depth: -999
  • Persistent: true
  • Parent: (no parent)
  • Mask: (same as sprite)

Give it these events:

#Create Event:
execute code:

alarm[0] = 150;

#Alarm Event for alarm 0:
execute code:

alarm[0] = 150;
global.points += 1;
global.pointsB += 1.25;

#Step Event:
execute code:

if !instance_exists(oUpgradeHP) 
if !global.points = 0
{
global.points -= 1;
instance_activate_object(oUpgradeHP);
instance_activate_object(oUpgradeRA);
instance_activate_object(oUpgradeSS);
instance_activate_object(oUpgradeAS);
}

if !global.pointsB = 0
{
ID=choose(1,2,3,4);
//
if ID = 1 {global.hp2 += 1;}
if ID = 2 {global.ra2 += 4;}
if ID = 3 {global.ss2 += 1;}
if ID = 4 {global.as2 = global.as2/1.1;}
//
global.pointsB -= 1;
}

if !instance_exists(oTurretA) {
instance_deactivate_all(true);
sleep(500);
game_restart();
}
if !instance_exists(oTurretB) {
instance_deactivate_all(true);
sleep(500);
game_restart();
}

#Other Event: Game Start:
execute code:

// Train Team A
global.hp1 = 5; // Health Points
global.ra1 = 64; // Range
global.ss1 = 6; // Shoot Speed
global.as1 = 30; // Attack Speed
// Train Team B
global.hp2 = 5; // Health Points
global.ra2 = 64; // Range
global.ss2 = 6; // Shoot Speed
global.as2 = 30; // Attack Speed

global.points = 1;
global.pointsB = 1;

room_goto_next();

#Draw Event:
execute code:

draw_set_color(c_black);
draw_set_font(font0);
//
draw_text(x+4,y+4,"TEAM [PLAYER] - HP="+string(global.hp1)+" - RA="+string(global.ra1)+" - SS="+string(global.ss1)+" - AS="+string(global.as1));
draw_text(x+4,y+18,"TEAM [ENEMY_] - HP="+string(global.hp2)+" - RA="+string(global.ra2)+" - SS="+string(global.ss2)+" - AS="+string(global.as2));

Step 3

Create another new object, _Block, and make it solid. It's just for looks - it doesn't do much itself.

Step 4

Create yet another new object, called _BaseA, with these properties:

  • Sprite: _BaseA
  • Solid: true
  • Visible: true
  • Depth: -1
  • Persistent: false
  • Parent: (none)
  • Mask: (none)
  • The sprite is just a 16x16 block. Now, give it these events:

    #Create Event:
    execute code:
    
    hp = 100;
    
    
    #Step Event:
    execute code:
    
    if hp < 0.9
    instance_destroy();

    Step 5

    Here's an image of the final game, for reference:

    This time, create a new object to represent one of the player's trains (a white rectangle on the tracks). Call it _TrainA and give it these properties:

    • Sprite: _TrainA
    • Solid: false
    • Visible: true
    • Depth: 0
    • Persistent: false
    • Parent: Make it the parent of _BaseA
    • Mask: (none)

    The sprite should be a 16x16 train, facing right. Give the object these events:

    #Create Event:
    execute code:
    
    // Train Team A
    // Sets stats to global variables decided by upgrades.
    hp = global.hp1; // Health Points
    ra = global.ra1; // Range
    ss = global.ss1; // Shoot Speed
    as = global.as1; // Attack Speed
    
    direction = 360;
    speed = 2;
    
    
    #Alarm Event for alarm 0:
    exit this event
    
    #Step Event:
    execute code:
    
    n = instance_nearest(x,y,oBaseB);
    
    if distance_to_point(n.x,n.y) < ra
    {
       if alarm[0] < 1 {
       i=instance_create(x,y,oBulletA);
       i.speed = ss;
       alarm[0] = as; 
       speed = 0;
       }
    }
    else
    speed = 2;
    
    if hp < 0.9
    instance_destroy();
    /*
    hp = global.hp1; // Health Points
    ra = global.ra1; // Range
    ss = global.ss1; // Shoot Speed
    as = global.as1; // Attack Speed
    */

    Step 6

    We'll make the player's turrets now. Create a new object called _TurretA, and give it these properties:

    • Sprite: _TrainA
    • Solid: false
    • Visible: true
    • Depth: 0
    • Persistent: false
    • Parent: oBaseA
    • Mask: (none)

    I used the same sprite as for the player's train, here, but feel free to change it to a actual turret. It should be 16x16px. Add the events:

    #Create Event:
    execute code:
    
    hp = 25; // Health Points
    ra = 128; // Range
    ss = 15; // Shoot Speed
    as = 20; // Attack Speed
    
    #Alarm Event for alarm 0:
    exit this event
    
    #Step Event:
    execute code:
    
    n = instance_nearest(x,y,oBaseB);
    
    if distance_to_point(n.x,n.y) < ra
    {
       if alarm[0] < 1 {
       i=instance_create(x,y,oBulletA);
       i.speed = ss;
       alarm[0] = as; 
       }
    }
    
    if hp < 0.9
    instance_destroy();

    Step 7

    The turrets have to shoot bullets, so create a new object to represent these. Call it oBulletA, and give it these properties:

  • Sprite: _BulletA //A thin
  • Solid: false
  • Visible: true
  • Depth: 1
  • Persistent: false
  • Parent: (no parent)
  • Mask: (same as sprite)
  • The sprite should be a thin bullet (1px tall by 16px wide, within a 16x16px transparent square), facing to the right. Here are its events:

    #Create Event:
    execute code:
    
    n = instance_nearest(x,y,oBaseB);
    direction = point_direction(x,y,n.x,n.y);
    
    
    #Step Event:
    execute code:
    
    image_angle = direction;
    
    
    #Collision Event with object o_Block:
    destroy the instance
    
    #Collision Event with object oBaseB:
    execute code:
    
    #instance_destroy();
    other.hp -= 1;
    
    </small>
    
    <p>8. Create a New Object (oSpawnerA)
    <br><small>Information about object: oSpawnerA
    
    Sprite: _TrainA //Still using the train sprite
    Solid: false
    Visible: false
    Depth: 0
    Persistent: false
    Parent: <no parent>
    Mask: <same as sprite>
    
    Create Event:
    execute code:
    
    alarm[0] = 180;
    instance_create(x,y,oTrainA);
    
    
    Alarm Event for alarm 0:
    execute code:
    
    alarm[0] = 180;
    instance_create(x,y,oTrainA);

    Step 9

    When the player can update their HP, the relevant button lights up:

    We'll create this button now. Create a new object called oUpgradeHP, and give it these properties:

    • Sprite: _UpgradeHP
    • Solid: false
    • Visible: true
    • Depth: -99
    • Persistent: false
    • Parent: (no parent)
    • Mask: (same as sprite)

    Give it these events, too:

    #Create Event:
    execute code:
    
    image_alpha = 0.5;
    
    Mouse Event for Left Released:
    execute code:
    
    global.hp1 += 1;
    instance_deactivate_object(oUpgradeHP);
    instance_deactivate_object(oUpgradeRA);
    instance_deactivate_object(oUpgradeSS);
    instance_deactivate_object(oUpgradeAS);
    
    
    Mouse Event for Mouse Enter:
    execute code:
    
    image_alpha = 1;
    
    Mouse Event for Mouse Leave:
    execute code:
    
    image_alpha = 0.5;

    Step 10

    Duplicate oUpgradeHP three times so you have four objects - one button each for upgrading HP, RA, SS and AS. (oUpgradeHP, oUpgradeRA, oUpgradeSS, oUpgradeAS).

    Change the Left Released event's code for each:

    oUpgradeHP -> global.hp1 += 1;
    oUpgradeRA -> global.ra1 += 4;
    oUpgradeSS -> global.ss1 += 1;
    oUpgradeAS -> global.as1 = global.as1/1.1;

    Step 11

    Duplicate oBaseA, oTrainA, oTurretA, oSpawnerA and oBulletA to create oBaseB, oTrainB, oTurretB, oSpawnerB and oBulletB.

    Change their Parents (from "A" to "B") and change all their code from:

    n = instance_nearest(x,y,oBaseB);

    ...to:

    n = instance_nearest(x,y,oBaseA);

    ...as required.

    Also set oTrainB to have direction = 180; in the create event, instead of direction = 360;. Last, choose left-facing sprites for each, where appropriate.

    Step 12

    Add all your tilesets, sprites, art and so on and create two new rooms: one is rm_intro, where you should only put the obj_Game, and the other one is the main room (rm_main).

    Build your rm_main like so:

    1. Place bases on either side.
    2. Place train tracks running from the bases (you can use the tileset in the source download).
    3. Place turrets to defend; when all turrets are dead the other team wins.
    4. Place spawners, which spawn new trains at intervals.
    5. Place the four Upgrade buttons.

    You could try to make the room symmetrical so it'll be somewhat fair, or place more turrets on the Enemy side for harder gameplay. It's up to you - the whole point of a prototype is to test things!

    Now play the game. As you play, consider what needs changing. Should the trains be faster or slower? Should upgrades come more frequently? Should the computer have an unfair advantage? Should the player have more input other than just applying upgrades? Tweak away!

    I hope you learned a little something about making games - but more importantly, I hope you learned how any idea can be quickly smashed together into a prototype.

    Editor's Note: This is a new type of post we haven't tried before. Please let us know what you think! Is there anything you'd like to see us do differently with the format in the future? Would you like more detail in the video, or a fuller set of instructions for actually building the game?

    Related Posts
    • Game Development
      Games
      Game Input SimplifiedPreview
      Cross-platform games all have one thing in common: they all need to deal with a multitude of game input devices (controllers), and dealing with those input devices can become a daunting task. This tutorial demonstrates one way to handle multiple input devices through the use of a simple, unified API.Read More…
    • Code
      Corona SDK
      Build a Poker Game in Corona: Game LogicOd95a preview image@2x
      In the first part of this tutorial, we set up the project and created the game's interface. We also created and implemented a function to create a deck of cards. In this second tutorial, we will create the game logic.Read More…
    • Game Development
      Implementation
      Write Once, Publish Everywhere With HaxePunk: Making a GamePreviewretinaimage
      You've probably had this experience before: you hear about an awesome game, but then you find out that it's only coming out on the one platform that you don't own. It doesn't have to be this way. In this tutorial, you will learn how to use Haxe to make a game in one development platform that can target multiple gaming platforms, including Linux, Mac, Windows, iOS, Android, and Flash.Read More…
    • Game Development
      From Scratch
      Make a Megaman-Inspired Game in Construct 2Megaman 400px
      I am going to walk you through the creation of a Megaman-inspired shooter/platformer game. We will be more focused on the shooting aspects of the gameplay rather than the platforming. In this tutorial I will be using Construct 2 as the tool to make the game, but I will explain the logic using pseudocode so that you can follow this tutorial in any language or engine of your choice.Read More…
    • Game Development
      Implementation
      How to Make Your First Roguelike400px
      Roguelikes have been in the spotlight recently, with games like Dungeons of Dredmor, Spelunky, The Binding of Isaac, and FTL reaching wide audiences and receiving critical acclaim. Long enjoyed by hardcore players in a tiny niche, roguelike elements in various combinations now help bring more depth and replayability to many existing genres.Read More…
    • Code
      Corona SDK
      Corona SDK: Create a Shooter GameCoronasdk bullets preview@2x
      In this tutorial, I'll show you how to create a shooter game with limited bullets with the Corona SDK. The objective of the game is to shoot a high amount of targets with only five bullets. During this tutorial, you'll work with timers, touch controls, and physics. To learn more, read on!Read More…