Unity and FBX Animation

Unity interpolates between all keyframes imported from a model (FBX) Therefore instant-jump animations get smoothed Therefore you get a frame or two of motion where there should be none So all your camera cuts look terrible There’s a way to fix this using events. You call an event just before the jump and set the position (Via Unity Japan blog) However curves aren’t available at runtime. So I’ve written an editor-only preprocessing script that detects single-frame jumps. We have hundreds of animations that need this, so I’ll need an AssetImporter script But I can’t change the .FBX files themselves, so I have to save the animation clips somewhere else… etc. All because Unity interpolates between all keyframes imported from a model

A New Beginning

A lot has happened since I last wrote a long blog post. To cut a long story short, I decided to quit my PhD and persue a passion I’ve had since I was a kid — video games.

It’s taken a while, looking for jobs, getting an offer, going through all the paperwork, visa applications, moving house… but I finally started my new job this week! I’m working for Opus, a small-ish game studio based in Tokyo. What drew me to them at first was that they were one of the only Japanese studios that have published a game on Steam. They seem more forward-looking than other studios, and their size means I’ll (hopefully) have a chance to play a creative role. I’ve enjoyed my first week :)

It wasn’t easy to make the decision to quit my PhD and change careers again, especially to the risky video games industry. I’ve been playing games since I was 4, sitting on my dad’s knee watching him play our Atari 800XL. He taught me how to run games on our old 286 and I loved playing Monkey Island, MechWarrior.

I studied computer science at university, but I never thought hard about making video games. Last year was so inspiring in so many ways, I changed my mind.

If I had to pin it on one game, it was Fez.

Playing Fez convinced me that there are games worth making that can really touch players’ hearts. I loved the sheer joy and beauty that’s crammed into a platformer. It’s a love letter to classic video games. It inspired me to think of my own games that would make the player feel a certain way, or tell an SF story. For far too long all I’ve seen are games that take 100+ people to make, and are only about reproducing movie-like stories and visuals.

Fez, and other beautiful small games like Journey, Kentucky Route Zero, Monaco, as well as meeting inspiring people at BitSummit continually reinforce that I’ve made the right decision to join the industry.

I’m only just starting out, but for the first time in a while, I’ve got a good feeling.

Löve version of Equations for Organic Motion

Originally by Justin Windle (it seems), I found the Equations for Organic Motion on CodePen a while ago, but I couldn’t get a version working tonight. So I made my own from the source.

I needed a nice wobble motion so my version has wiggling circles to go with the pulsating ones.

-- Equations for Organic Motion

local lg = love.graphics

-- For speed and laziness
local sin = math.sin
local cos = math.cos
local tan = math.tan
local pow = math.pow
local pi  = math.pi
local abs = math.abs
local exp = math.exp

function love.load()
  funcs = {
    function(t) return sin(t) end,
    function(t) return cos(t) end,
    function(t) return cos(t)*sin(t) end,
    function(t) return sin(t)*sin(t*1.5) end,

    function(t) return sin(tan(cos(t)*1.2)) end,
    function(t) return sin(tan(t)*0.05) end,
    function(t) return cos(sin(t*3))*sin(t*0.2) end,
    function(t) return sin(pow(8,sin(t))) end,

    function(t) return sin(exp(cos(t*0.8))*2) end,
    function(t) return sin(t-pi*tan(t)*0.01) end,
    function(t) return pow(sin(t*pi),12) end,
    function(t) return cos(sin(t)*tan(t*pi)*pi/8) end,

    function(t) return sin(tan(t)*pow(sin(t),10)) end,
    function(t) return cos(sin(t*3)+t*3) end,
    function(t) return pow(abs(sin(t*2))*0.6,sin(t*2))*0.6 end,
  }

  mode = 'pulse'
  speed = 1
end

function love.update(dt)
end

function love.keypressed(key, code)
  if key == "p" then mode = 'pulse' end
  if key == "w" then mode = 'wobble' end
  if key == "=" then speed = speed * 2 end
  if key == "-" then speed = speed / 2 end
end

function love.draw()
  lg.setColor(255, 255, 255)
  lg.print("Original Source:\nEquations for Organic Motion", lg.getWidth() - 200, 20)
  lg.print("p : Circle pulse mode\nw : Wobble mode\n+ : Double speed\n- : Half speed", lg.getWidth() - 200, 60)

  local t = love.timer.getTime() * speed

  lg.setColor(255,255,255)
  for i = 1, #funcs do
    xOff = (((i-1) % 4) * 150) + 75
    yOff = math.floor((i-1) / 4) * 150 + 75
    lg.push()
    lg.translate(xOff, yOff)

    if mode == 'pulse' then
      local radius = funcs[i](t) * 50
      lg.circle('fill', 0, 0, radius, 22)
    elseif mode == 'wobble' then
      local x = funcs[i](t) * 10
      local y = funcs[i](t/1.3) * 10 ------------ Change Y, fiddle with this for fun
      lg.circle('fill', x, y, 10, 22)
    end
    lg.pop()
  end

  -- showfps
  --lg.setFont(game.dFont)
  lg.setColor(255,0,0,255)
  lg.print(tostring(love.timer.getFPS()), lg.getWidth()-30, lg.getHeight()-20)
end

Let nobody doubt my commitment to organic motion.

Show full size

2D Platformer Perspective

In designing my game, I’ve been thinking about perspective. I’m trying to make a 2D platformer, but despite the name not all 2D games have a purely two-dimensional art style.

For the most part, existing 2D games have two-dimensional art. Most of the classics are completely two-dimensional in both gameplay and art: Super Mario games on NES and SNES, the first few Sonic games, Donkey Kong Country, to name a few.

Exceptions to this are games with two-dimensional art but some depth to the gameplay River City Ransom and Streets of Rage, and games with two-dimensional gameplay but 3D art like Shadow Complex and Rochard.

What do I mean by perspective?

Returning to fully two-dimensional games, the important thing to note is that unlike 3D games, they are not constrained by the conventions of 3D camera projections, and are free to fake perspective how they choose. So the word perspective in 2D games is a little vague, but put simply, in this case I use it to talk about the angle between virtual camera and the world. Based on this I’ve split perspectives into two very general categories.

Completely Side-on

The vast majority of early platforming games are in this genre. On Nintendo consoles alone there are the Super Mario Bros, Metroid and Castlevania series. More recently Fez, Spelunky, Super Meat Boy and Dustforce have all used this projection.

The defining feature of this group is that the platforms that the player stands on have no depth at all. There are pixels below the player supporting them, but little or no foreground or background on that layer. There can be parallax layers behind and in front of the character to add depth, but the “camera” is effectively 90 degrees to the scene.

Show full size

▲ Clockwise from top: Super Mario World, Metroid, Fez.

This is all very fascinating, but effect does this art style have on gameplay and expressivity?

  • Makes for pixel-perfect platforming. There is no ambiguity about where the player will land.
  • Unambiguity makes the controls feel “tight” and responsive.
  • Less space to add art details to terrain. Artists have to use background layers behind and under the character to add detail.
  • Looks more ‘retro’. Either from the years of retro video games we grew up with or unrealistic projection, despite any art style I feel side-on platformers look retro.

Varying levels of depth

This is a much more nebulous and vaguely defined group than the previous one. In essence it contains all games that aren’t completely side-on. This is far from an exhaustive list, but to give a few examples: Donkey Kong Country, Aladdin, Rayman Origins, Dust: An Elysian Tail.

Unless the game features a 3D engine, there is no real camera and no real projection. Any sense of perspective must be faked by the art style. The z-depth of platforms is shown through a texture that differentiates it from the front and back of the platform. In Aladdin it’s flat sand with a highlighted front edge, compared to repeating sand dunes. In Rayman it is much more subtle, but the layer containing the player is lighter than the rest.

Show full size

▲ Aladdin's floors have depth, showing some of the ground.

Some games like Donkey Kong vary this z-space, giving the appearance of widening and narrowing platforms.

Show full size

▲ Donkey Kong Country's ground-depth varies.

However in areas that require more precise platforming, the z-space is reduced, as shown in Donkey Kong and Rayman. Compare the space that Rayman is standing on to the depth of the floating platform.

Show full size

▲ Rayman

So what does this kind of projection give rise to?

  • More space for artistic expression – can add ground details
  • Looser platforming controls – The exact place where the player will make contact with the ground is made less clear by the amount of foreground and background detail to each platform.
  • More realistic appearance by giving depth to the scene through broadening the platforms on which the player stands.

Which to use?

It depends on the effect you are trying to achieve. If your game’s primary mechanic is not platforming, and you want some more room for artistic expression and possibly realism, then go for the latter group. On the other hand if you are trying to create a “tight” platformer, a completely orthographic perspective could help.

Using Löve2d's SpriteBatch

I keep forgetting how to use SpriteBatch, so I wrote a short snippet to remind myself and anyone else interested in Löve2d.

function love.load()
  -- Load our image we want to draw many times
  image = love.graphics.newImage("dirt.png")

  -- The number of tiles we want to draw is pretty much the number
  -- that will fit on the screen
  maxX = math.ceil(love.graphics.getWidth()  / image:getWidth())  + 2
  maxY = math.ceil(love.graphics.getHeight() / image:getHeight()) + 2
  local size = maxX * maxY
  print(size)

  -- Set up a sprite batch with our single image and the max number of times we
  -- want to be able to draw it. Later we will call spriteBatch:add() to tell
  -- Love where we want to draw our image
  spriteBatch = love.graphics.newSpriteBatch(image, size)
  setupSpriteBatch()
end

function love.update(dt)
  -- If we were using a moving background, we would want to only call
  -- setupSpriteBatch when its range or position changed
  setupSpriteBatch()
end

function setupSpriteBatch()
  spriteBatch:clear()

  -- Set up (but don't draw) our images in a grid
  for y = 0, maxY do
    for x = 0, maxX do
      -- Convert our x/y grid references to x/y pixel coordinates
      local xPos = x * image:getWidth()
      local yPos = y * image:getHeight()

      -- Add the image we previously set to this point
      spriteBatch:add(xPos, yPos)
    end
  end
end

function love.draw()
  -- Draw the spriteBatch with only one call!
  love.graphics.setColor(255,255,255)
  love.graphics.draw(spriteBatch)

  -- Draw FPS in the bottom right corner
  love.graphics.setColor(255,0,0)
  love.graphics.print(tostring(love.timer.getFPS()), love.graphics.getWidth()-30, love.graphics.getHeight()-20)
end

For a more advanced example using Quads, check out Efficient Tile-based Scrolling.

subscribe via RSS