Thursday, January 27, 2011

Furry Legends (demo)

Furry Legends isn't likely to be what you think it is unless you think it's a puzzle-platformer featuring a creepy-looking, green fuzz ball. Besides its terrible name, the demo also has the misfortune of being released the same day as Fish'em All! It's hard not to draw the conclusion that Nintendo took this week as an opportunity to dump its clunkers at once.


Furry Legends Screenshot


Prior interest: none


This game's marketing failure begins with the lousy name. I think the intent of the word "Furry" was humor and I imagine that "Legends" is supposed to excite interest in the story. But together, the words of the title are just bland. It would have been better to go all humorous ("Wholly Rollers" perhaps?) or all epic ("Saga of Furland"). To try and split the difference just leaves the consumer confused.


Odds of purchase: low


Playing the demo makes clear the developers were going for a wacky story, which seems entirely needless. Gameplay, at least for the first level, tends to be relaxed even when the enemy shows up. You basically roll around and jump with the occasional attack which is triggered by holding "Z", aiming with the analog stick and shaking the WiiMote. Later you get a boost power-up that acts something like the boost ball in Metroid Prime. In fact, the game feels like an extended riff on Samus' morph ball with significantly less weight. I don't know how well a full game of rolling platforming would stand up as the sections in Metroid games charm by offering a change of pace.


I should mention that there seems to be one oversight in part of the demo's level. There's a puzzle that needs to be solved by rolling a stone onto a pressure trigger. But I managed to block the stone from settling on the trigger and it rolled to a part of the level I couldn't seem to retrieve it from. My solution was to restart the level, which probably wasn't what the designers intended.

Fish'em All! (demo)

There's no way around it: Fish'em All! is a blight on the WiiWare service and among the many reasons the Wii has a reputation for shovelware. The gameplay consists entirely in waving the Wiimote from side to side in order to catch jumping fish with a net. Sure there are three game modes (including "Fishtris", which is really a match-three game), some unique adversaries (including the shark from Jaws), and some other controls to move and jump. But there's no other game mechanic besides catching jumping fish with a net.


Fish'em All! screenshot


Prior interest: none


Nothing about the game appeals.


Odds of purchase: none


It's a bad sign when you get sick of a game before you've even finished the demo. Now I didn't try with a second player, which often helps this sort of game much the same way a few friends make a B-movie better. Not that I can imagine feeling the need to buy the game after experiencing the demo, which leaves little to the imagination.


As an aside, the player characters are named Jack and Walter. Hmmmmm:
Grumpier Old Men poster


Finally, you can't help but admire this woman's enthusiasm:

Wednesday, January 26, 2011

Art of Balance (demo)

Family lore has it that my mother's heirloom, crystal pepper shaker was broken when her father-in-law and some of my uncles were stacking whatever happened to be on the table the first time they visited my parents. Art of Balance, if it had existed then, might have saved the day. It's a simple game of stacking one geometric object (no breakable table settings here) on another and seeing if the whole thing stays up.


Art of Balance screen


Prior interest: none


I thought Art of Balance looked like a newly released game from the makers of Jett Rocket reusing the same engine for water and lighting effects. But it turns out the game was released on WiiWare almost a year ago and several months before Jett Rocket. Clearly, it's a low profile game that could use a little publicity.


Odds of purchase: low


As a cash-strapped consumer, a game like Art of Balance which delivers tons of gameplay for a low pricetag, ought to represent a real temptation. Sadly, its niche has already been filled by Boom Blox Bash Party in my library. (Which, come to think of it, is the biggest problem I have with Jett Rocket: it's too similar to another retail game that I already own.) What it comes down to is when I want to play a physics-based puzzle game I can't imagine picking up Art of Balance because Boom Blox just offers me so much more.

Tuesday, January 25, 2011

Let's Code: Pong (part 1)

Lately I've been playing around with writing games including two simple puzzle games based on ideas suggested by my son. Today, I wanted to present my code for a Pong clone. It's not the most exciting game in the world, but it's dead simple to code and a good "Hello World"-style program for starting to write action games.


The first decision that must be made when starting a new coding project is which framework to use. Since my justification for writing games is to help me keep my programming skills active and to continue learning, I wanted to write in an interesting language that I don't know well: Lua. A quick Google search discovered LÖVE, which is an open-source 2D game engine in active development. Since Pong is a 2D game it's not a big deal that our framework isn't 3D. My other little projects simulate a 3D environment with an isometric viewpoint. In the future, if I do a first-person game or something, I'd want to switch frameworks.


I've also decided to use the love.physics module, which is certainly overkill for Pong but will be necessary for physics-based puzzles I'm working toward. It really isn't as hard to use as the LÖVE manual suggests. The module is based on the Box2D library, which has pretty good documentation. Once the simulated world is set up, the physics library can deal with a bunch of details like ball movement, bouncing off walls and paddles, and so on.


love.physics tutorial


love.physics has a fairly basic tutorial that simulates a ball falling to the ground and being rolled around. If you want to follow along, go ahead and grab a copy of LÖVE and try out the tutorial. Below, I'll cover some of the same territory if you'd prefer just to read. Playing around with the tutorial, I noticed that the ball will eventually roll off the edge of the world and disappear. To solve that, I added a two walls and a ceiling to keep physics objects from getting lost. My philosophy is that if you do something twice, you should think about writing a function instead. If you do something more than twice, then you certainly should write a function. Since I needed four walls rather than just the ground, I wrote a make_wall function:


function make_wall(world, x, y, w, h)
local wall = {}
wall.body = love.physics.newBody(world, x, y, 0, 0)
wall.shape = love.physics.newRectangleShape(wall.body, 0, 0, w, h, 0)
wall.shape:setRestitution(1)
return wall
end


Even if you are new to Lua, this code should be fairly easy to read. make_wall needs 5 inputs: world (the physics world), x, y (the coordinates of the center of the wall), and w, h (the width and height of the wall). Lua has a very lightweight object system and this function could be seen as a constructor for a wall object, which is the return value. As PIL points out, "Tables in Lua are not a data structure; they are the data structure." So wall is initialized as an empty table. Objects in the physics module require a body, which represents the object's center of mass, position, attitude, and velocity, and a shape, which defines the space an object occupies and how it interacts with other objects. When we assign values to wall.body and wall.shape, Lua automatically creates variables (members in OOP terms). Walls should be static, so the mass is set to 0, which is the way Box2D represents infinite mass. The shape of the wall is simply a rectangle centered on the center of mass. In order to make balls bounce off the walls, restitution (essentially bounciness) is set to 1.


In love.load(), I'm going to create my walls thusly:


local walls = {}
...
-- Floor
table.insert(walls, make_wall(world,
love.graphics.getWidth()/2,
love.graphics.getHeight()+1,
love.graphics.getWidth(),
2))

-- Ceiling
table.insert(walls, make_wall(world,
love.graphics.getWidth()/2,
-1,
love.graphics.getWidth(),
2))

This inserts a floor two pixels high1 just below the graphics frame and a ceiling just above it. If we weren't using a physics engine, we could just code the ball to reverse vertical motion when it hits the side of the screen. Standard Pong only needs these two walls, so make_wall didn't save us many lines of code. However, it's easy to imagine variations with different wall configurations.


We only need one ball for our basic game, but it's simple and clean to write a make_wall function. And who knows? We might want to add more balls to the game at some point. Initially, I'd set the mass of the ball based on it's size (using Body:setMassFromShapes) but that causes the physics of the game to change when the size of the ball is changed. It seemed easier to just fix the mass to something (somewhat) sensible like the mass of a tennis ball and adjust the force applied until it felt right. It turns out one Newton works pretty well. The radius of the ball is measured in pixels and it can be adjusted without impacting the physics of the game. Some people would probably want these variables to be made constants, but a) there's not much of a performance gain and b) Lua doesn't support making variables constant. Besides, the simplest way to have a variable remain constant is to not alter it's value. The ball shouldn't have any linear damping (more or less the same as drag or fluid resistance) or friction to slow it down. The way Box2D simulates collisions, I don't need to make both the walls and the ball bouncy, but it doesn't hurt to specify the restitution here as well.


local ball
-- Mass of a tennis ball http://hypertextbook.com/facts/2000/ShefiuAzeez.shtml
local ball_mass = 0.057
local ball_force = 1
local ball_radius = 3

function make_ball(world, x, y, r)
local ball = {}
ball.body = love.physics.newBody(world, x, y, ball_mass, 0)
ball.shape = love.physics.newCircleShape(ball.body, 0, 0, r)

ball.body:setLinearDamping(0)
ball.shape:setFriction(0)
ball.shape:setRestitution(1)

return ball
end


The final type of object we need to make for our Pong game is the players' paddles. Classic Pong doesn't really simulate the ball bouncing off a flat paddle, but has the ball bounce off at an angle determined by the location the ball hits the paddle. In other words, the closer to the edge of the paddle, the steeper the angle the ball with come off the paddle (and the more likely your opponent will miss). This makes the game much more interesting since it matters not just whether a player intercepts the ball, but where on the paddle they do so. At some point experimenting with the number of facets would be interesting.


function make_paddle(world, x, y, w, h)
local paddle = {}
paddle.body = love.physics.newBody(world, x, y, 0, 0)

-- Don't use a rectangle for the paddle since the bounces
-- off a flat surface are boring. In stead, we use a flattened wedge:
-- /|
-- | |
-- \|
paddle.shape = love.physics.newPolygonShape(paddle.body, 0, -h/2,
-w/2, -h/6,
-w/2, h/6,
0, h/2,
w/2, h/2,
w/2, -h/2)
paddle.shape:setRestitution(1)
return paddle
end


Now that we can make a paddle, let's build a player object, which has a paddle and a score. The cpu variable will describe the behavior of the player if it is computer controlled. More on that momentarily.


local players = {}

function make_player(world, x, y, l, cpu)
local player = {}

player.score = 0

player.paddle = make_paddle(world, x, y, 2*ball_radius, l)

player.cpu = cpu

return player
end


It's easy to imagine all sorts of AIs for Pong from carefully calculating the intercept location of the ball to moving more or less at random. Most Pong implementations use some variation of what I call the chase AI in which the computer tries to match the location of the ball on the y-axis with some sort of lag. My approach follows Zeno's Paradox of Achilles and the tortoise. In this case, Achilles (the paddle) does occasionally catch the tortoise (the ball) if the delay is set low enough, the paddle is long enough and the ball is moving slow enough along the y-axis. Set delay to 2 for the classic paradox.


-- Basic Pong AI is to have the paddle chase the ball. Use the delay parameter
-- to create an AI that responds more slowly to vertical movement.
function make_chase_ai (delay)
return function (paddle, ball)
local delta_y = ball.body:getY() - paddle.body:getY()
paddle.body:setY(paddle.body:getY() + delta_y/delay)
end
end


If you're familiar with mostly conventional languages (such as BASIC, Java or C++), you might find it odd that make_chase_ai returns a function. In Lua, functions are first-class values which means the can be assigned to variables, passed to other functions and, as seen here, be return values. What I'm doing here is creating a closure over the delay free variable. It's probably easiest to explain by showing how it's used. Here's how I initialize a computer player in love.load():


players[1] = make_player(world,
10,
love.graphics.getHeight()/2,
12*ball_radius,
make_chase_ai(50))

players[1].paddle.body:setAngle(math.pi)

So I pass the physics world to the make_player function, put the paddle 10 pixels in from the left of the screen and centered vertically, set the length of the paddle to 12 ball radii, and provide an AI that chases the ball with a delay of 502. This isn't the best player in the world, but it's surprising how often he catches the ball at the last moment, which makes for some interesting play. Once the function is created, delay is fixed to 50 and it is assigned to the player's cpu variable. In OOP terms, cpu is a virtual method for the player object.


In passing, notice that I rotated the paddle body 180° since make_paddle creates a wedge facing to the left. I could have created the paddle to have facets on both sides or written a mirror_shape function3 to flip the paddle on its vertical axis. But applying the principle of parsimony it seemed simplest to rotate the paddle and be done with it.


Here's how the other player might be initialized if you want to pit an Achilles AI against the more pedestrian AI above:


players[2] = make_player(world,
love.graphics.getWidth()-10,
love.graphics.getHeight()/2,
12*ball_radius,
make_chase_ai(2))

So this player is positioned on the right side of the screen and has a lower delay, but is otherwise the same as player 1. So what benefit is using a closure instead of just making delay a player attribute like paddle length and position? It might help to see how the AI is executed in love.update:

for _, player in ipairs(players) do
if player.cpu then
player.cpu(player.paddle, ball)
end
end

Let's review what happened:

  1. We called make_chase_ai with parameters of 50 and 2 respectively.
  2. We assigned the output, which is a function, to each player's cpu variable.
  3. We checked to see if a player had a true value in cpu.
  4. And if it did, we executed the AI in each time step.

Each step creates one more layer of abstraction, which means we could easily plug a totally different type of AI or no AI at all into a player. For instance, here's an AI that jumps at the last minute:

function make_jump_ai (min, max)
return function (paddle, ball)
local delta_x = math.abs(ball.body:getX() - paddle.body:getX())
if (delta_x < max and delta_x > min ) then
paddle.body:setY(ball.body:getY())
end
end
end

...

players[2] = make_player(world,
love.graphics.getWidth()-10,
love.graphics.getHeight()/2,
12*ball_radius,
make_jump_ai(15, 20))

When the function is called, it takes just the paddle and ball objects and uses a completely different algorithm to move the paddle around. This allows polymorphism, which in turns allows much simpler code in higher level functions such as love.load and love.update.


This has gone on long enough and I've only covered the setup portion of the game. Next time, we'll set the world in motion and interact with it.




1 - I picked 2 pixels because creating a one pixel tall rectangle causes LÖVE (or more accurately Box2D) to crash.


2 - 50 isn't a magic number. It's just what seemed most interesting given a particular configuration of ball size, screen size, ball speed, and paddle length. For a while I was using 100.


3 - I cannot tell a lie. I wrote a mirror_shape function, but decided not to use it since it was more cumbersome than I imagined when I started writing it.

Tuesday, January 4, 2011

Robox (demo)

Robox might be what would happen if Terry Gilliam (from Monty Python and director of Time Bandits) set out to make a Metroid-type game. If you remember from the Flying Circus show there were occasional animated transitions that start with a live-action scene, end with a live-action scene, and show some seemingly random action using photo-collage and crude, rounded off drawings, you'll have a pretty good mental image of Robox. You play a probe, dropped off on an unknown planet, which loses all of it's natural abilities and must hunt for them via exploration, platforming and solving puzzles. It's a promising start.


Robox screenshot


Prior interest: none/low


Either I hadn't heard of Robox before now or I'd confused it with Frobot. If I had heard of the game, it's likely I'd have been slightly interested in the unique graphical style of the title. Theses days, WiiWare (and the Wii itself) has a full stock of arty 2D platformers, so if you want to sell another, you need to make the gameplay stand out. In turn, the best way to show off unique gameplay is with a good demo, so it's a good thing we have them now.


Odds of purchase: none


Like a Terry Gilliam cartoon, the game shifts from one action to the next unpredictably, but charmingly. The demo begins with a rack of squared-off robots (Roboxen?) lined up for deployment. Starting the game drops one of them into the atmosphere below where you need1 to tilt the Wiimote to avoid cliffs. Upon landing, you have no choice2 but to hold the Wiimote sideways and walk to the right. After a bit, the scene shifts3 to the inside of Robox where you use the pointer control and D-pad to solve an insultingly easy puzzle with a parasite or biological component or whatever. That allows Robox to jump, which is convenient since there is a small mesa just to the right. To the right of the mesa there's a slope4 down which a suddenly spawned boulder rolls and crushes a third of the robot's battery power followed shortly by a second boulder which takes another third. Understand that you move very slowly, jump only so high as to clear the boulder for a fraction of a second, and the boulder falls much faster than you can move. And going uphill is even slower. And there is a new boulder being spawned constantly at that same location over and over again.


Thankfully, your little robot will respawn to the point where he gained the jump ability after every death. Hatefully, however, checkpoints are few and far between and the obstacles ramp up the difficulty exceedingly quickly. There's one bit where a swinging, killer vine lives directly over spike-lined pit. If you mistime your jump and hit the vine, all forward momentum is lost and you will fall into the pit, which is far too deep to escape and so you will die. Which means slowly ambling past increasingly difficult traps once again to try one more time to slip past the vine/pit/spike vortex once again. There's no rhyme or reason to the pacing which goes from pedestrian to punishing in the space of a moment.


Hard games can be fun. Robox might very well be have a good balance between challenge and accomplishment once you've found better upgrades and mastered the system. Even games that work on repetition and memory can be fun if action/consequence loop is tight. But the Robox demo fails to show anything but brutal difficulty. The good news, I suppose, is that having a demo will weed out the uninterested/weak.





1 - To be honest, I don't think you actually need to do anything in this segment. There seem to be no consequences for hitting rocks. Would the sequence continue forever if you put the controller down and walked away? I might try it sometime.


2 - Sure you could go left, but only a few steps before you run into an insurmountable wall.


3 - Sadly, there is no actual animation—just a quite cut to a vaguely circui-board-looking screen and a few pages of expositionary text.


4 - I call it the learning curve.