Programming Games

Class 5 - Animation

Play homework

Super Mario Maker

Vacant Kingdom

Topics

Description

So far our sprites have been static, flat images that float around in the screen. This week we’ll add animation to make our game more dynamic.

Class Notes

Animation

Frames

Let’s make an animated image. Consider how animation was traditionally made for the screen. It started as a movie strip, with each separate part of the strip consisting of an individual drawing, slightly changed from the previous and next drawing in the strip. Watching the screen as the next strip flicks by gives the appearance of an animation.

And so it is in our games.

We will create a separate sprite for each “frame” of animation. Our process in Pico-8 will be to have a variable hold the number of the current sprite, and to advance the number of the current frame to move through the animation.

Consider an animation consisting of 5 frames for example. We will need 5 sprite images.

function _init()
  spritenum=1
end

function _update()
  --animation
  spritenum+=1 --same as writing spritenum=spritenum+1
  if spritenum>5 then 
    --this will reset back to frame 1
    spritenum=1
  end
end

function _draw()
  spr(spritenum,64,64)
end

Note: we do not have to start at 1. Imagine our first sprite number is 28. We could initialize spritenum at 28 and if spritenum is greater than 32 then reset spritenum back to 28.

Look at him go!

Animation speed

If you want to slow down your animation then you can advance the sprite number slower, or faster if you want the animation to be faster.

spritenum+=0.5 --half as slow as before

How does this work even without changing anything else in our code?

function _draw()
  spr(spritenum,64,64)
end

The answer is that in Pico-8 it takes the first argument in the spr() function if it’s a float (decimal) and rounds down to the nearest lower whole number (integer).

spr(1.5,64,64) --rounds down to sprite 1

There is no sprite 1.5 but Pico-8 would round down to 1 and display sprite 1.

So for example, if we are changing at the speed spritenum+=0.3 then:

spr(spritenum,64,64) --spritenum=1 the first time this runs
spr(spritenum,64,64) --1.3 => rounded down to 1 the next time
spr(spritenum,64,64) --1.6 => rounded down to 1 the next time
spr(spritenum,64,64) --1.9 => rounded down to 1 the next time
spr(spritenum,64,64) --2.2 => rounded down to 2 the next time
spr(spritenum,64,64) --2.5 => rounded down to 2 the next time
spr(spritenum,64,64) --2.8 => rounded down to 2 the next time
--etc...

Pongs Review


David Berreby: How good/scary is ChatGPT?

A conversation with science writer David Berreby about AI, writing, and the creative process

Wednesday, September 25th, 4:00pm, Natural Sciences Lecture Hall, room 1001

Humans have long had tools that help us create (the musician’s metronome, the writer’s spell checker). But now we’ve been abruptly asked to adjust to artificial intelligence (AI) tools that claim to do the creating for us. Two years ago, it was new text from ChatGPT and other “Large Language Models.” Today, the same machine-learning technology also generates spoken words, images, videos, musical composition, scientific hypotheses, scholarly analyses, and more. At times AI’s results are deeply flawed or flimsy. But at other times these systems manage to mimic human efforts astonishingly well. How should we reckon with them? As one scholar who studies AI has said, “If you haven’t had an existential crisis about this tool then you haven’t used it very much yet.”

David Berreby is a science writer who has written about AI for the past decade. In this conversation, we’ll look at various AI tools for creative tasks and discuss the practical, psychological and moral ramifications of using them. The talk will be hands-on, rooted in examples from Berreby’s own experience as well as from real-time use in the room. The aim is to get a better understanding of how AI is affecting writers, students, scholars and other creators, and take an informed look at the kinds of decisions AI asks us to make. Participants should emerge with a better sense of whether and how and when they can use AI – and when they shouldn’t.

The NSS lecture series is made possible in part by generous contributions from Con Edison.


Multiple Enemies: Using tables to make arrays of objects

The time has come for us to produce a large number of characters, enemies or other “things” in a game. We will do this by using a table to create an array. Anything in our software that we have multiple of, each with their own individually assigned attributes we call an object.

Imagine we create an enemy and give it some attributes:

enemy1={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}

If we want a second enemy, we do almost the exact same thing again:

enemy2={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}

For a third enemy, again:

enemy3={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}

So far, we have this:

enemies1={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}
enemies2={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}
enemies3={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}

Now imagine we actually needed 20 enemies. Or 200. It would be tedious to hand write this all out, over and over, only changing the number of the enemy.

Rather than do that, we can take advantage of two things: we can use a for-loop to do something many times. And we can use a table to hold many of the same kind of object.

enemies={}
for i=1,10 do
  enemies[i]={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}
end

This will declare and initialize a table named enemies. Then we use a for-loop to fill it up. We are essentially creating a x, y, w, h and spritenum for each enemy. If we didn’t use a for-loop, we’d have to individually create all of them by hand:

enemies[1]={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}
enemies[2]={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}
enemies[3]={x=rnd(126),y=rnd(126),w=8,h=8,spritenum=2}
--etc, up to enemies[10]

Now we will make a game with a large number of enemies of a type. We also need a table for our player’s attributes.

function _init()
  p={x=64,y=120,w=8,h=8,spritenum=1}
  enemy={}
  for i=1,10 do
    enemy[i]={
    x=rnd(126),
    y=rnd(114), --not all the way down
    w=8,
    h=8,
    xspeed=1,
    spritenum=2
    }
  end
end

This has declared an initalized 1 player and 10 enemies.

In our update, we can advance our enemies to the right, and have them wrap around the screen.

  for i=1,#enemy do
    enemy[i].x=enemy[i].x+enemy[i].xspeed
    
    if enemy[i].x>126 then
      enemy[i].x=0
    end
  end

And we can draw them to the screen:

function _draw()
cls(0)
  
  for i=1,#enemy do --operate on every enemy
    spr(enemy[i].spritenum,enemy[i].x,enemy[i].y)
 
  --check if each enemy has collided with player
    if collide(p,enemy[i]) then
      --if so, play sound
      sfx(0)
    end
 
  end

  spr(p.spritenum,p.x,p.y) --draw the player
  
end

Code homework

For homework this week you will begin to design a survival or action arcade game. This can be of several different types such as stealth games, escape, “bullet hell”, Dalek attack, shoot ‘em up (e.g. Space Invaders), Frogger, or fight game, etc. See our Play Homework for an example.

Credits