Writing Games with Processing: Hunting Platty

19. December, 2013

As it is, the game is pretty boring. The player has some control but no goal. Let’s add a routine which makes the enemies hunt the player.

There are many complex algorithms for path finding but we aim for something simple.

Add a new method to the class Enemy:

  void hunt() {
    int dx = signum(px - x) * tileSize;
    int dy = signum(py - y) * tileSize;
    
    x += dx;
    y += dy;
  }

This just moves the crocodile one step closer to where the player currently is. signum() is a simple helper function which returns the sign (1, 0, -1) of the argument:

int signum(float value) {
  return value < 0 ? -1 : value > 0 ? 1 : 0;
}

A small helper method then makes all the enemies hunt Platty in a loop:

void moveEnemies() {
  for(Enemy e: enemies) {
    e.hunt();
  }
}

Which leaves us with the last problem: When should the crocodiles move? One solution would be to move them in draw(). If you try it, you’ll see that this makes it a pretty short game – the screen is refreshed about 60 times per second but the key repeat is only 20 times per second, so poor Platty is eaten after a single step.

The solution is to move the enemies once after the player has made his move:

void movePlayer(int dx, int dy) {
  ...

  moveEnemies();
}

Et voila, hunting in 16 lines of code (click the image to start the animation):

Hunting Platty

You can find the whole source code here.

Next: Losing the game
Previous: Moving Around
First post: Getting Started


Writing Games with Processing: Moving Around

18. December, 2013

The game would be pretty boring if Platty would just sit there, waiting to be eaten. The player needs to be able to move him around:

void keyPressed() {
  if(key == CODED) {
    if(keyCode == UP) {
      movePlayer(0, -1);
    } else if(keyCode == DOWN) {
      movePlayer(0, 1);
    } else if(keyCode == LEFT) {
      movePlayer(-1, 0);
    } else if(keyCode == RIGHT) {
      movePlayer(1, 0);
    }
  } else if(key == ' ') {
    movePlayer(0, 0);
  }  
}

As you can see, I’m using the cursor keys to move the character. You could as easily use the keys WASD or any other combination but since we don’t need the mouse, the cursor keys felt like a good choice. Also note that the player can sit around and wait using the space bar.

The code to move the character is pretty simple:

void movePlayer(int dx, int dy) {
  px += dx * tileSize;
  py += dy * tileSize;
}

An alternative would be to always center the screen around the character and move everything else.

Or you can try to move the player one pixel at a time. Change the code to

void movePlayer(int dx, int dy) {
  px += dx;
  py += dy;
}

and play a bit. How does that feel?

When playing with the game, you might notice a slight problem: You can move outside of the visible screen. We need to check that the new coordinate is valid before moving the character:

void movePlayer(int dx, int dy) {
  dx *= tileSize;
  dy *= tileSize;
  
  int newX = px + dx;
  int newY = py + dy;
  if(newX >= 0
    && newX < width
    && newY >= 0
    && newY < height
  ) {
    px = newX;
    py = newY;
  }
}

Nice. We have a player character, we have control, we have enemies. What else do we need? Moving the enemies.

You can find the whole source code here.

Previous: Enemies
First post: Getting Started


Writing Games with Processing: Enemies

17. December, 2013

The next element of our game are the crocodiles. Since we can have several of them, let’s use a class to collect all data necessary to draw them:

class Enemy {
  color c;
  int x, y;
  String name;
  
  Enemy(String name) {
    this.name = name;
    c = color(222,40,107);
  }
  
  void draw() {
    fill(c);
    ellipse(x + tileSize/2, y + tileSize/2, tileSize, tileSize);
    
    fill(0);
    textAlign(CENTER, TOP);
    textSize(12);
    text(name, x + tileSize/2, y + tileSize + 2);
  }
}

As you can see, the code is pretty similar to the drawing Platty, the player character. I’m giving them names to make it easier to distinguish them on the screen and while debugging.

Next, we need a list of enemies and a helper function to easily create enemies:

ArrayList<Enemy> enemies = new ArrayList<Enemy>();
void addEnemy(int x, int y, String name) {
  Enemy enemy = new Enemy(name);
  enemy.x = x;
  enemy.y = y;
  enemies.add(enemy);
}

To draw them, we just loop over the elements in the list and call the draw() method of each element:

void drawEnemies() {
 for(Enemy e: enemies) {
    e.draw();
  }
}

Next, we add two enemies in the initial setup:

void setup() {
  size(640, 480); //VGA for those old enough to remember
  
  addEnemy(20, 20, "Kenny");
  addEnemy(600, 20, "Benny");
}

After adding the new drawEnemies() method to draw(), we’re done:

void draw() {
  drawBackground();
  drawPlatty();
  drawEnemies();
}

Let’s see what we’ve got:

RunPlattyRun_V2

You can find the final code here.

Next: Moving around
Previous: Setup and a Simple Player Character
First post: Getting Started


Writing Games with Processing: Setup and a Simple Player Character

16. December, 2013

We have an idea, now we need to write the game. Let’s use a screen size of 640 x 480:

void setup() {
  size(640, 480); //VGA for those old enough to remember
}

The next item on the task list is to render the game area. Let’s create a nice grass background:

void draw() {
  fill(174, 204, 27);
  rect(0, 0, width, height);
}

Too boring? Well, we can always add a more complex background later. Right now, we’re in development mode: We want to test some game ideas, quickly prototype something and play. Fancy graphics is our least priority right now.

What else do we need? The player character, Platty, the platypus:

int px = 320, py = 240;

void drawPlatty() {
  fill(235,220,160);
  rect(px, py, 20, 20);
  
  fill(0);
  textSize(18);
  textAlign(CENTER, CENTER);
  text("P", px+10, py+10);
}

void drawBackground() {
  fill(174, 204, 27); // green
  rect(0, 0, width, height); // fill whole screen
}

void draw() {
  drawBackground();
  drawPlatty();
}

That’s it. Again nothing fancy, just the bare minimum.

You will notice that some of the numbers are related but it’s not obvious from the code. At this point, we’ll have to make our first design decision: Can the player move freely (pixel by pixel) or is the movement tile base (as in chess)?

At this stage, both are equally simple to implement. An ego shooter or a jump&run would use fluid movement while a puzzle type game often uses tiles.

Let’s use tiles:

int px = 320, py = 240;
int tileSize = 20;

void drawPlatty() {
  fill(235,220,160);
  rect(px, py, tileSize, tileSize);
  
  fill(0);
  textSize(tileSize-2);
  textAlign(CENTER, CENTER);
  text("P", px+tileSize/2, py+tileSize/2-2);
}

Much better.

Note how I split tasks into different helper methods to keep the code readable. I could have done the player character rendering inside of the main draw() method. But by moving the code into different methods, I can keep things together that belong together while at the same time documenting my code. drawBackground() is like a comment explaining what the code in the method does. But unlike a comment, it can’t become outdated.

You can find the final version of the code here.

Next: Enemies!

First post in the series: Getting Started


Writing Games with Processing: Getting Started

15. December, 2013

Ever wanted to write your own game? Can’t be too hard, right?

Most games sold today are ego shooters. But the genre is sucked pretty dry. The most innovative games in the last years were simple, surprising and cheap. Examples are BraidFez, Thomas Was Alone. Those games didn’t thrive from multi-million development budgets, they throve from simple ideas. They were great not despite but because of their limitations.

In this series of blog posts, I’ll show you how to develop a very simple game using Processing.

Processing is a simple environment to create amazing computer generated images using a simplified version of Java like the one on the right (code).

Komplexe Methoden –  M.1 Zufall und Rauschen –  M.1.6 Agenten im Raum –  M_1_6_01_TOOL

Komplexe Methoden – M.1 Zufall und Rauschen – M.1.6 Agenten im Raum – M_1_6_01_TOOL

While you download the software, we need an idea.

In my case, the idea came from a brainstorming session organized by Zurich Game Developers at the MechArtLab. Everyone had to buy a Kinder Surprise egg (illegal in the USA, btw.) and create a game with the content. The eggs in my group contained a platypus, a crocodile and a frog.

After a quick brainstorming session we came up with this game idea: “Kribbit, the frog, runs a zoo. A small zoo. A very small zoo. Only a single compound. He has two animals: A poor, lonely platypus and a couple of crocodiles. The crocodiles promised to be nice to the platypus and not to eat him. But every time Kribbit looks the other way, the crocodiles try to eat poor Platty.”

That’s it. Game idea? Check.

What? Too simple for you? Well, complex game = low chance of success.

Next post: Setup and a simple player character.


Wine on OpenSUSE Without Sound

4. September, 2013

Yesterday, I tried to install the Windows game Homeworld on my openSUSE 12.3. After a couple of problems, the no-cd patch, I could start the game with:

/opt/cxoffice/bin/cxrun --bottle Homeworld homeworld.exe /1600 /enable3DNow /enableSSE /device gl /heap 1073741824

Note: For /device gl, you must install DirectX 9.

The main problem: No sound. Starting the Wine Configuration, I saw that it was using winealsa.drv in the Audio tab.

Looking into the terminal finally solved the mystery:

ALSA lib dlmisc.c:236:(snd1_dlobj_cache_get) Cannot open shared library /usr/lib/alsa-lib/libasound_module_pcm_pulse.so
ALSA lib dlmisc.c:236:(snd1_dlobj_cache_get) Cannot open shared library /usr/lib/alsa-lib/libasound_module_pcm_pulse.so

My system is 64bit but Crossover is probably a 32bit application.

Installing alsa-plugins-pulse-32bit finally solved the problem.

Also make sure you have this in your ~/.asoundrc:

pcm.!default {
    type pulse
}
ctl.!default {
    type pulse
}

Related:


Anomaly – Warzone Earth

21. September, 2012

Anomaly – Warzone Earth (forum) is basically a reverse tower-defense game. The computer builds the towers and you try to get past them.

The game works on Linux, Mac and Windows. Installation on Linux 64-bit was painless: just unpack the archive and execute the game.

By default, it runs in full-screen mode which is a bit of a problem for me since I have two screens. Alt+Enter toggles between full-screen and windowed mode.

Your goal is to lead a train of 5 armored vehicles through a dangerous area. You can heal the vehicles, give them directions, change their order, add and remove units. In later stages, you will get decoys that draw enemy fire, or smoke grenades that cover your for a time.

Some tactical tips:

  • Don’t rush. In most levels, you have lots of time.
  • Crawlers have a slightly higher range than the basic enemy towers. If you circle an adjacent city block, you can often take out some of the enemies with little or no damage at all.
  • Heal units using corners. Place the repair item in the corner and four units will be in range instead of the normal three.
  • If you need to heal the leading unit, place the repair token a bit ahead so the unit gets enough exposure.
  • Keep an eye on health. If a unit is losing a lot of health and you can’t reach it or you’re out of repair tokens, you can put them in a different place in the train. If the first or last unit is out of range, move the damaged unit there. Losses are really expensive in this game.
  • Try to clear all enemies. In the first levels, supplies will be dropped anywhere but soon, they will be dropped after destroying enemy towers. Plus, each kill gives you a bit of money that you should spend on more units or upgrading existing ones.
  • The first unit takes most damage. Upgrade it first.
  • When a unit is under attack, you can cancel the effects by dropping a repair token.
  • If you walk in circles around the basic towers, they will aim at you. That way, you can buy your tanks an extra moment since the turret will first have to turn back before it can open fire.
  • Plan ahead. The closest reward might not be the easiest. Try to single out towers. Drive past and out of range to repair or recharge shields.
  • Each tower has a specific weakness. Exploit them.
  • Some can’t turn. Don’t attack them head on; drive by instead or kill it from behind.
  • Behemoths do area damage. Use a decoy and place it far away from your units.
  • When you need to squeeze through a bunch of towers, use a smoke bomb.
  • Decoys work well with flash towers but you need to place it at a distance or the damage will spill.

Dismissed.