Getting Started with Game Development, Summer 2026

Table of Contents

Course written and taught by R.W. "Moose 🫎" Singh

πŸ–ΌοΈ Student gallery | πŸ§‘β€πŸ’» Source repository

TIC80 Quick Reference

How to start a new project

  1. Open TIC80
  2. Type new lua and press enter
  3. Type save GAMENAME to save your game

Then, press CTRL+S periodically to save your work!

How to load your work from your flash drive

  1. Open TIC80
  2. Type folder and press enter
  3. Plug in your USB drive
  4. Open the USB drive folder
  5. Click-and-drag your files from the USB drive to the TIC80 folder
  6. In TIC80, type load FILENAME to load your work

Dimensions reference

  • Sprite/tile dimensions: 8 x 8 pixels
  • Screen dimensions: 240 x 136 pixels
  • Map dimensions: 30 x 17 tiles (that's 30*8 x 17*8 pixels!)

Color reference

tic80-colors.png

0 1 2 3 4 5 6 7
BLACK PURPLE RED ORANGE YELLOW LGREEN GREEN DGREEN
8 9 10 11 12 13 14 15
DBLUE BLUE LBLUE SKYBLUE WHITE GRAY DGRAY ALMOST BLACK

If you have TIC80 PRO you can change this palette to any colors you'd like.

Button reference

tic80-buttons.png

0 1 2 3 4 5 6 7
UP DOWN LEFT RIGHT Z X A S

You can remap the buttons in the TIC80 menu by pressing ESC while running the game and going to Options and Setup Gamepad.

Keyboard shortcuts

F1 F2 F3 F4 F5
Code editor Sprite editor Map editor Sfx editor Music editor
CTRL+S CTRL+L CTRL+R ESC CTRL+C CTRL+X CTRL+V
Save Load Run Editor/Prompt Copy Cut Paste
      or game menu      

Tools icons

CODE EDITOR

tic80-cut.png tic80-copy.png tic80-paste.png tic80-undo.png tic80-redo.png tic80-font.png
CUT COPY PASTE UNDO REDO FONT
tic80-shadow.png tic80-run.png tic80-drag.png tic80-find.png tic80-goto.png tic80-bookmark.png tic80-outline.png
SHADOW RUN DRAG FIND GOTO BOOKMARK OUTLINE

SPRITE EDITOR

tic80-ico-pencil.png tic80-ico-dropper.png tic80-ico-select.png tic80-ico-fill.png tic80-ico-flip1.png
PENCIL DROPPER SELECT FILL FLIP H
tic80-ico-flip2.png tic80-ico-rotate.png tic80-ico-delete.png tic80-ico-tiletab.png tic80-ico-chartab.png
FLIP V ROTATE DELETE TILES SPRITES

MAP EDITOR

tic80-map-eye.png tic80-map-grid.png tic80-map-select.png tic80-map-fill.png tic80-map-tile.png
VIEW ALL TOGGLE GRID SELECT FILL CHOOSE TILE

Lua operators

  • Arithmetic: + (Addition), - (Subtraction), * (Multiplication), / (Division)
  • Assign x the value of 123: x = 123 (one equal sign for variable ASSIGNMENT)
  • Relational operators:
    • x equals y: x == y
    • x is greater than y: x > y; x is less than y: x < y
    • x is greater than or equal to y: x >= y; x is less than or equal to y: x <= y
    • x is NOT equal to y: x ~= y
  • Modifying a variable's value: x = x + 1, x = x - 1
  • Logic operators: and, or
  • Concatenate: ..

Advanced tools

If you have TIC80 PRO ($10 on their page) you can save your TIC80 project as a .lua file and use a proper code editor to work on your code more easily.

  • Geany (free/open source code editor)

Lessons

Ahead-of-time constraints

To keep the code simple, there are some constraints I recommend.

  • Keep character images ONE TILE (8x8 pixels) in size

    constraint-bigsprite.gif constraint-smallsprite.png

    WHY? The more tiles a character takes, you will need a spr call for each one, and need to handle the (x,y) offsets.

    Code for the big sprite:

    spr( 94,  x-4, y-4, 0 )
    spr( 95,  x+4, y-4, 0 )
    spr( 110, x-4, y+4, 0 )
    spr( 111, x+4, y+4, 0 )
    

    vs. for the small sprite:

    spr( 94, x, y, 0 )
    

    PLUS the alrger sprite makes it harder to code in COLLISION DETECTION and other things! And if all your characters are different sizes this FURTHER complicates things!

  • Don't design a platformer, make it top-down

    constraint-gravity.png

    WHY? Implementing gravity means adding in physics calculations, like momentum, as well as collision detection for the ground. If you have a consistent flat ground at a specific y coordinate then that's a little easier, but any sort of tile-based platforming plus gravity will be greatly over-complicated.

  • Numbers are easier than health bars or hearts

    Storing "hp" and printing the hp as a number value is EASY. Translating that into a health bar or hearts requires a "hpmax" variable and additional logic to build out.

Lessons

01: Text story

Example: HowTo-01-PrintText.lua

Topics: Tic-80 basics, Print statements

How to use the Tic-80 software and create a little story program!

02: Drawing sprites

Example: HowTo-02-DrawImage.lua

Topic: Sprites, colors, and coordinates

Design some sprites, then draw them to the game screen with the spr function!

03: Random numbers

Example: HowTo-04-Random.lua

Topics: Random numbers

Learn about placing sprites at random (x,y) coordinates, and generating random numbers within a range to print out.

04: Playing sound effects

Example: HowTo-05-Sounds.lua

Topics: snd function, btn function, if statements

Make a little keyboard program where you press buttons to play notes!

05: Distance formula

Example: HowTo-06-Distance.lua

Topics: Creating a function, the distance formula between two points

Learn about how we can check if two objects are near each other in our game with the distance formula!

06: Moving the player with arrow keys

Example: HowTo-07-KeyMove.lua

Topics: Moving multiple characters with their own (x,y) coordinates, using the arrow keys

07: NPC movement

Example: HowTo-08-NPC.lua

Creating a non-player character that acts on its own. You have to tell it how to behave!

08: Handling collision between two objects

Example: HowTo-09-Collision.lua

Let's add scores for the player and the NPC, whoever collects the bubble gets a point!

09: How to make a background or game level

Example: HowTo-10-Background.lua

Now that we have the gameplay, let's make the game a little nicer looking!

Use the Map Editor (F3) to create backgrounds, then set the map to draw in the game code with the map function:

10. Making a title screen / game over screen

Example: HowTo-11-Menus.lua

Now we need a title screen, "you win!" screen, and "game over" screen.

You can build basic menus using the Map Editor (F3) and go between screens by using the map function. You'll also want a separate variable to keep track of which screen you're on.

screen = 1

function TIC()
  if screen == 1 -- Title screen
    map(0,0)
    if btn(4) then screen = 2 end      -- Start game if player hits "Z"

  elseif screen == 2 -- Game screen
    map(30,0)
    if lives == 0  then screen = 3 end -- Game over if you run out of lives
    if score == 10 then screen = 4 end -- You win if score hits 10
    -- [... More game logic ...]

  elseif screen == 3 -- Game over screen
    map(60,0)

  elseif screen == 4 -- You win screen
    map(90,0)

  end
end

Step-by-step game recipes

πŸ–ŒοΈ Paint program

Topics: Mouse coordinates and button click

Learn about how to detect mouse movement and clicks with the mouse() function!

x, y, button = mouse() -- Get mouse info

🐹 Click-a-mole game

This game will put together everything we've learned so far to make a simple "click-a-mole" game.

πŸͺΎ Pickin' Sticks

Putting it all together to make a game!

Starter template projects

These projects have some art and some code already provided. Students can continue expanding from there!

Make sure to go into TIC-80 and type folder!

Download the game file and put it in the folder!

How to…

🧽 How do I clear the screen?

tic80-colors.png

cls(colorId) -- Clear the screen

πŸ’¬ How do I print text to the screen?

print("TEXT", x, y, colorId) -- Draw text at a position

✏️ How do I draw images on the screen?

spr(id, x, y, 0)

πŸ–±οΈ How do I detect mouse clicks and mouse position?

Example: HowTo-03-Mouse.lua

mousex, mousey, mousebtn = mouse()

🎲 How do I make random things happen?

randNum = math.random(MIN, MAX)

πŸ”Š How do I play sound effects?

sfx(soundId, "C-4", 10) -- Play a sound

πŸ”˜ How do I detect button presses?

tic80-buttons.png

if btn(0) then
  -- Do some command
end

πŸ“ How do I determine how far from each other two things are?

function GetDistance( x1, y1, x2, y2 )
  return math.sqrt( ( x2 - x1 )^2 + ( y2 - y1 )^2 )
end

player={ x=0, y=0 }
item  ={ x=20, y=20 }

function TIC()
  dist = GetDistance(player.x, player.y, item.x, item.y)
  if dist <= 8 then
    -- Collision!
  end
end

➑️ How do I get my character to move when arrow keys are hit?

-- SETUP
player={ x=0, y=0 }

function HandleMovement()
  if     btn(0) then
    player.y=player.y-1

  elseif btn(1) then
    player.y=player.y+1

  elseif btn(2) then
    player.x=player.x-1

  elseif btn(3) then
    player.x=player.x+1

  end
end

-- GAME LOOP
function TIC()
  HandleMovement()

  -- (... more code ...)
end

πŸƒ How do I make an NPC move on its own?

-- SETUP
npc = { x=0, y=0, goalx=120, goaly=80 }

function NpcMoveTowardGoal()
  if     npc.goalx < npc.x then -- Goal is to the left
    npc.x = npc.x - 1
  elseif npc.goalx > npc.x then -- Goal is to the right
    npc.x = npc.x + 1
  end

  if     npc.goaly < npc.y then -- Goal is above
    npc.y = npc.y - 1
  elseif npc.goaly > npc.y then -- Goal is below
    npc.y = npc.y + 1
  end
end

-- GAME LOOP
function TIC()
  NpcMoveTowardGoal()

  -- (... more code ...)
end

πŸ“ How do I check if two items are hitting each other?

function GetDistance( x1, y1, x2, y2 )
  return math.sqrt( ( x2 - x1 )^2 + ( y2 - y1 )^2 )
end

-- SETUP
player ={ x=0, y=0, score=0 }
item   ={ x=0, y=0 }

-- GAME LOOP
function TIC()
  dist = GetDistance(player.x, player.y, item.x, item.y)
  if dist <= 8 then
    player.score=player.score+1
  end
end

🌌 How do I make a background / a game level?

F3 is the MAP EDITOR. Use the eyeball button tic80-map-eye.png to view all the maps in your game.

The very top-left of the map will be its coordinate that you use.

map-coordinates.png

-- SETUP
mapx = 30; mapy = 0

-- GAME LOOP
function TIC()
    map(mapx, mapy)
end

⏳ How do I make a countdown timer?

Example: HowTo-12-Countdown.lua

-- SETUP
countdown = 0

-- GAME LOOP
function TIC()
  if btn(4) then countdown = 100 end    -- Start countdown timer

  if countdown > 0 then                 -- Countdown timer
   countdown = countdown - 1
  end

  if countdown == 0 then
   -- Do some action once timer is 0
  end
end

πŸ”₯ How do I shoot one projectile?

Example: HowTo-13-Projectile.lua

You will need an x,y coordinate for the projectile, as well as velocity x,y values. Also handy to have these for the player, if the projectile is going to travel in the same direction as there the player is moving to.

-- SETUP
player ={ x=0,    y=0,    image=1, velx=0, vely=0 }
bullet ={ x=-100, y=-100, image=2, velx=0, vely=0 }

function HandleMovement()
  player.velx = 0
  player.vely = 0

  if     btn(0) then
    player.y=player.y-1
    player.vely=-1

  elseif btn(1) then
    player.y=player.y+1
    player.vely=1

  elseif btn(2) then
    player.x=player.x-1
    player.velx=-1

  elseif btn(3) then
    player.x=player.x+1
    player.velx=1

  end
end

function HandleShoot()
  if btn(4) and bullet.x==-100 then
    -- Put bullet "on" player to start with
    bullet.x=player.x
    bullet.y=player.y

    -- Set bullet direction
    bullet.velx=player.velx*2
    bullet.vely=player.vely*2
  end
end

function UpdateBullet()
  if bullet.x ~= -100 then        -- Is bullet visible?
    bullet.x=bullet.x+bullet.velx -- Move bullet based on horizontal velocity
    bullet.y=bullet.y+bullet.vely -- Move bullet based on vertical velocity
  end

  -- If it goes off screen, disable bullet
  if bullet.x < -20 or
     bullet.x > 240 or
     bullet.y < -20 or
     bullet.y > 136 then
    bullet.x = -100
  end
end

-- GAME LOOP
function TIC()
  HandleMovement()
  HandleShoot()
  UpdateBullet()

  -- DRAW STUFF
  spr(player.image, player.x, player.y,0)
  spr(bullet.image, bullet.x, bullet.y,0)
end

πŸ—ΊοΈ How do I make the map scroll?

Example: HowTo-14-MapScroll.lua

You'll have to define when the map should scroll. The game screen is 240 pixels wide by 136 pixels tall. Maps are 30 tiles wide by 17 tiles tall. If the player goes off the screen, then you can adjust the map coordinates.

-- SETUP
mapx=0; mapy=0
player={ x=0, y=0 }
screen={ pxwidth=240, pxheight=136,
         tilewidth=30, tileheight=17 }

function HandleScreenScroll()
  -- Off right side
  if     player.x > screen.pxwidth then
    mapx=mapx+screen.tilewidth   -- Shift map
    player.x = 0                 -- Reset player position

  -- Off left side
  elseif player.x < -8 then
    mapx=mapx-screen.tilewidth   -- Shift
    player.x = screen.pxwidth-8  -- Reset the player position
  end

end

-- GAME LOOP
function TIC()
  map(mapx, mapy) -- Draw current map

  HandleScreenScroll()

end

↩️ How do I make my character turn when moving?

Example: HowTo-15-Directions.lua

You will need a sprite for each direction. You will also need a variable to store which sprite image to use When the button for a direction is pressed, move the character, but also set its sprite to the correct direction.

lesson-directions.gif

-- SETUP
player={
  x=0, y=0,
  image=256,
  images={
    up=256,
    down=257,
    left=258,
    right=259
  }
}

function HandleMovement()
  if     btn(0) then
    player.y=player.y-1
    player.image=player.images.up

  elseif btn(1) then
    player.y=player.y+1
    player.image=player.images.down

  elseif btn(2) then
    player.x=player.x-1
    player.image=player.images.left

  elseif btn(3) then
    player.x=player.x+1
    player.image=player.images.right

  end
end

-- GAME LOOP
function TIC()
  HandleMovement()

  spr(player.image, player.x, player.y, 0)
end

🎬 How do I animate my sprites?

Example: HowTo-16-Animated.lua

You will need a sprite for each frame. You will also need a variable to store which frame of animation you're on (and ideally the max amount of frames). Each game cycle, add to the frame number, and use this to adjust your sprite each time.

lesson-animated.gif

-- SETUP
player={
  x=0, y=0,
  frame=0, frameMax=2,
  image=256,
  images={
    up=256,
    down=257,
    left=258,
    right=259
  }
}

function UpdateAnimation()
  player.frame=player.frame + 0.1          -- Increment animation frame
  if player.frame >= player.frameMax then
    player.frame=0                         -- Reset back to 0 at the end
  end
end

-- GAME LOOP
function TIC()
  spr(player.image + math.floor(player.frame), player.x, player.y, 0)
end

This code assumes your frames of animation will be next to each other horizontally. If you're adding multiple sprites for different DIRECTIONS, put those vertically.

🐘 How do I make big sprites?

Example: HowTo-18-BigSprite.lua

PLEASE DON'T DO THIS IT IS A PAIN IN THE BUTT AND MAKES COLLISION, ANIMATION, AND EVERYTHING ELSE HARDER!!

If your sprite is one block, you just need one spr call:

lesson-bigsprite-8px.png

spr(player.image, player.x, player.y, 0)

If your sprite is 2x2 blocks, you will need a spr for each part:

lesson-bigsprite-16px.gif

-- SETUP
player={
  x=0, y=0,
  bigimage={
    { i=0,  x=-4, y=-4 },
    { i=1,  x= 4, y=-4 },
    { i=17, x=-4, y= 4 },
    { i=18, x= 4, y= 4 },
  }
}

-- GAME LOOP
function TIC()
  for id, sp in pairs( player.bigimage ) do
    spr( sp.i, player.x+sp.x, player.y+sp.y, 0 )
  end
end

πŸ‘ΎπŸ‘Ύ How do I handle multiple enemies?

thumbnail-multienemies.png

See program: example-code/AllFeatures.lua

We will need to use tables, which have { }. For example, this set of enemies has TWO enemies created inside it. This is before the TIC() function:

-- SETUP
enemies = {
  { mapx=0,  mapy=0, x=50, y=100, image=193, spd=0.5, alive=true },
  { mapx=30, mapy=0, x=150, y=70, image=193, spd=0.5, alive=true }
}

Within the TIC() function we can use a for loop to work with all the enemies while only writing code once:

-- HANDLE ARRAY OF ENEMIES -----------
for id,en in pairs(enemies) do
  if en.alive == true then
    -- Only use enemy if it's on the current map
   if mapx == en.mapx and mapy == en.mapy then
     spr(en.image, en.x, en.y,0)
   end
  end
end

We can also add enemy movement (like chase the player), also within the for loop:

-- HANDLE ARRAY OF ENEMIES -----------
for id,en in pairs(enemies) do
  if en.alive == true then
    -- Only use enemy if it's on the current map
    if mapx == en.mapx and mapy == en.mapy then
      spr(en.image, en.x, en.y,0)

      -- Chase player
      if     player.x < en.x then
        en.x = en.x - en.spd
      elseif player.x > en.x then
        en.x = en.x + en.spd
      elseif player.y < en.y then
        en.y = en.y - en.spd
      elseif player.y > en.y then
        en.y = en.y + en.spd
      end
    end
  end
end

Or checking for collision:

-- HANDLE ARRAY OF ENEMIES -----------
 for id,en in pairs(enemies) do
   if en.alive == true then
     -- Only use enemy if it's on the current map
     if mapx == en.mapx and mapy == en.mapy then
       spr(en.image, en.x, en.y,0)

       -- Chase player
       if     player.x < en.x then
         en.x = en.x - en.spd
       elseif player.x > en.x then
         en.x = en.x + en.spd
       elseif player.y < en.y then
         en.y = en.y - en.spd
       elseif player.y > en.y then
         en.y = en.y + en.spd
       end

       -- Check for collision
       if GetDistance(player.x,player.y,en.x,en.y) <= 8 then
         -- Bump player
         player.x = player.x + math.random(-1,1)
         player.y = player.y + math.random(-1,1)

         if hurttimer == 0 then
           -- Player gets hurt
           player.health = player.health - 1
           player.hurttimer = 100
           sfx(0,"C-4",10)
         end
       end
     end
   end
 end

 -- Hurt timer countdown
 if player.hurttimer > 0 then
   player.hurttimer = player.hurttimer - 1
 end

πŸ’£πŸ’£ How do I shoot multiple projectiles?

See program: example-code/AllFeatures.lua

You will need a table for bullets, just like the table of enemies. However, we will only shoot bullets when the player presses a button. This goes within the TIC() function:

-- HANDLE ARRAY OF BULLETS -----------
-- Create bullets if shooting
if btn(4) and bullettimer == 0 then
  newbullet={}
  newbullet.x = x
  newbullet.y = y
  newbullet.velx = velx
  newbullet.vely = vely
  newbullet.img = 208
  newbullet.spd = 2
  table.insert(bullets,newbullet)
  bullettimer = 10
end
if bullettimer > 0 then
  bullettimer = bullettimer - 1
end

Inside the TIC() function we need to make sure to handle those bullets.

 for id,bul in pairs(bullets) do
  -- Update bullet
  bul.x = bul.x + bul.velx * bul.spd
  bul.y = bul.y + bul.vely * bul.spd

  -- Draw bullet
  spr(bul.img,bul.x,bul.y,0)

  -- Check collision against enemies
  for eid, en in pairs(enemies) do
    if en.mapx==mapx and en.mapy==mapy then
      if GetDistance(en.x, en.y, bul.x, bul.y) <= 8 then
        en.alive = false
      end
    end
  end
end

πŸͺ„ How do I create spot-warps to other rooms?

See program: example-code/AllFeatures.lua

This is hard to do mostly because you have to keep track of map TILES and PIXEL coordinates. I would recommend just doing the screen scrolling "warping" instead of having a specific tile warp elseware.

We will need to create our warp list in a table above the TIC() function:

warps = {
  {
    mapxA=0, mapyA=0,   xA=20*8, yA=7*8,
    mapxB=0, mapyB=119, xB=6*8,  yB=80
  },
}
warptimer=0

And then check each warp in the TIC() function:

-- CHECK FOR WARPS -------------------
if warptimer == 0 then
  for id,wp in pairs(warps) do
    -- A to B warp
    if mapx==wp.mapxA and mapy==wp.mapyA then
      if GetDistance(x, y, wp.xA, wp.yA) <= 7 then
        -- Warp to new location
        mapx = wp.mapxB; mapy = wp.mapyB
        x    = wp.xB;    y    = wp.yB
        warptimer = 100
      end
    -- B to A warp
    elseif mapx==wp.mapxB and mapy==wp.mapyB then
      if GetDistance(x, y, wp.xB, wp.yB) <= 7 then
        -- Warp to new location
        mapx = wp.mapxA; mapy = wp.mapyA
        x    = wp.xA;    y    = wp.yA
        warptimer = 100
      end
    end
  end
elseif warptimer > 0 then
  warptimer = warptimer - 1
end -- if warptimer == 0 then

β›” How do I disallow the player from moving into certain tiles?

thumbnail-solidblocks.png

See program: example-code/HowTo-BlockTiles.lua

We will need to define which tiles we DON'T want the player to move on. We can do this with a table before the TIC() function:

-- SETUP
-- Mark which tiles we can't walk on
nowalk = { 1, 2 }

For this we will have to first predict where the player is moving. Rather than moving them when an arrow key is pressed, we instead do some math to figure out where they're going to go if everything is OK:

function HandleMovement()
  movex=player.x
  movey=player.y

  if     btn(0) then
    movey=player.y-1

  elseif btn(1) then
    movey=player.y+1

  elseif btn(2) then
    movex=player.x-1

  elseif btn(3) then
    movex=player.x+1

  end
end

Next we have to look at what tile is in this spot. We look through all the entries in the nowalk list, and if there's a match, we say "no, you cannot walk":

function HandleMovement()
  movex=player.x
  movey=player.y

  if     btn(0) then
    movey=player.y-1

  elseif btn(1) then
    movey=player.y+1

  elseif btn(2) then
    movex=player.x-1

  elseif btn(3) then
    movex=player.x+1

  end

  tilehere=mget(mapx+movex/8,mapy+movey/8)
  canwalk=true
  for id,badtile in pairs(nowalk) do
    if tilehere == badtile then
      canwalk=false
    end
  end
end

If you can walk, then we can change the player's (x,y) coordinates:

if canwalk then
 player.x = movex
 player.y = movey
end

This will look best if we also offset the player's sprite to be drawn at its center-point instead of its edge:

spr(player.image,player.x-4,player.y-4,0)

Note: This is one of the locations where using a sprite that is multiple tiles wide/tall will make this computation work less well.

πŸ’£πŸ§™ How do I deal with collision between a projectile and an object?

function GetDistance( x1, y1, x2, y2 )
  return math.sqrt( ( x2 - x1 )^2 + ( y2 - y1 )^2 )
end

-- SETUP
player={x=0, y=0,  image=1, hp=5, alive=true}
enemy ={x=10,y=10, image=2, hp=5, alive=true}
bullet={x=5, y=5}

function HandleBullet()
  if GetDistance(bullet.x, bullet.y, player.x, player.y) <= 8 then
    player.hp=player.hp-1        -- Damage player
    bullet.x=-100; bullet.y=-100 -- Remove the bullet
  end

  if GetDistance(bullet.x, bullet.y, enemy.x, enemy.y) <= 8 then
    enemy.hp=enemy.hp-1          -- Damage enemy
    bullet.x=-100; bullet.y=-100 -- Remove the bullet
  end
end

-- GAME LOOP
function TIC()
  if player.alive then -- Only draw player if alive
    spr(player.image, player.x, player.y, 0)
  end

  if enemy.alive then -- Only draw enemy if alive
    spr(enemy.image, enemy.x, enemy.y, 0)
  end
end

Additional code - Enemies and Warp systems

-- HELPER FUNCTIONS ------------------------------

function IsOnThisMap( obj )
  return obj.mapX == mapx and obj.mapY == mapy
end

function GetDistance( x1, y1, x2, y2 )
  return math.sqrt( ( x2 - x1 )^2 + ( y2 - y1 )^2 )
end

-- DEBUG -----------------------------------------

function Debug( title, thing, x, y, level )
  print( title .. ": " .. type(thing), x + (level*8), y)
  if thing == nil then
    return
  end
  for key, value in pairs( thing ) do
    y = y + 8
    if type( value ) == "table" then
      Debug( key, value, x + (level*8), y, level+1 )
    else
      print( key .. "=" .. value, x + (level*8), y )
    end
  end
end

function Debug2()
  print( "MAP: " .. mapx .. "," .. mapy, 2, 128 )
  print( "PLAYER: " .. x .. "," .. y, 50, 128 )
end

-- WARP HANDLER ----------------------------------
warps = {}
warptimer = 0

function CreateWarp( tile1X, tile1Y, tile2X, tile2Y )
  newWarp = {}
  newWarp.tileX = tile1X
  newWarp.tileY = tile1Y
  newWarp.mapX = math.floor( newWarp.tileX / 30 ) * 30
  newWarp.mapY = math.floor( newWarp.tileY / 16 ) * 16
  newWarp.x = (newWarp.tileX - newWarp.mapX)*8
  newWarp.y = (newWarp.tileY - newWarp.mapY)*8

  newWarp.tileDestX = tile2X
  newWarp.tileDestY = tile2Y
  newWarp.mapDestX = math.floor( newWarp.tileDestX / 30 ) * 30
  newWarp.mapDestY = math.floor( newWarp.tileDestY / 16 ) * 16
  newWarp.xDest = (newWarp.tileDestX - newWarp.mapDestX)*8
  newWarp.yDest = (newWarp.tileDestY - newWarp.mapDestY)*8

  table.insert( warps, newWarp )
end

function HandleWarps()
  if warptimer == 0 then
    for id, wp in pairs( warps ) do
      if IsOnThisMap( wp ) then
        if GetDistance( x, y, wp.x, wp.y ) <= 8 then
          mapx = wp.mapDestX; mapy = wp.mapDestY
          x = wp.xDest; y = wp.yDest
          warptimer = 100
        end
      end -- IsOnMap
    end -- for
  elseif warptimer > 0 then
    warptimer = warptimer - 1
  end -- warptimer
end -- function

-- ENEMY HANDLER ---------------------------------
enemies = {}

function CreateEnemy( mapX, mapY, tileX, tileY, health, imgTopLeft, imgTopRight, imgBottomLeft, imgBottomRight )
  newEnemy = {}
  newEnemy.mapX = mapX
  newEnemy.mapY = mapY
  newEnemy.tileX = tileX
  newEnemy.tileY = tileY
  newEnemy.x = (tileX - mapX) * 8
  newEnemy.y = (tileY - mapY) * 8
  newEnemy.health = health
  newEnemy.imgTopLeft = imgTopLeft
  newEnemy.imgTopRight = imgTopRight
  newEnemy.imgBottomLeft = imgBottomLeft
  newEnemy.imgBottomRight = imgBottomRight
  table.insert( enemies, newEnemy )
end

function UpdateEnemies()
  for id, en in pairs( enemies ) do
    if IsOnThisMap( en ) then
      -- Enemy behavior
    end
  end
end

function DrawEnemies()
  for id, en in pairs( enemies ) do
    if IsOnThisMap( en ) then
      spr( en.imgTopLeft,     en.x-4, en.y-4, 0 )
      spr( en.imgTopRight,    en.x+4, en.y-4, 0 )
      spr( en.imgBottomLeft,  en.x-4, en.y+4, 0 )
      spr( en.imgBottomRight, en.x+4, en.y+4, 0 )
      print( en.health, en.x, en.y - 8 )
    end
  end
end

-- SETUP -----------------------------------------
--           MAPX  MAPY  TILEX  TILEY  HEALTH  SPR1  SPR2  SPR3  SPR4
CreateEnemy( 0,    0,    1,     1,     3,      13,   14,   29,   30 )
CreateEnemy( 30,   0,    34,    4,     3,      13,   14,   29,   30 )

--          TILE1X, TILE1Y, TILE2X, TILE2Y
CreateWarp( 57,     9,      62,     10 )

Make sure to call this within TIC():

UpdateEnemies()
DrawEnemies()
HandleWarps()

Surf: Exploring Games on Tic-80

tic80-surf.png

If you have extra time at the end of class, you can go to the PROMPT view in Tic-80 and type in surf. From the menu, select [tic80.com], then [Play], then [Games]. Here, you can try out a bunch of games that other people have made for the Tic-80!

Teacher/parent resources

Lessons/projects files

  • TIC-80 - You can download TIC-80 for Windows for free, it is just one file. Place it on the desktop.
  • Project files - Starter code and example code files. When you run TIC-80, type folder and hit enter. The project files should be extracted in that directory.

Extra

  • Moos-a-dee games - Instead of letting the students go on gaming websites I allow them to either play TIC-80 games (via the surf command) or play games that I made. These games are free, feel free to distribute.

More example code

(Need the $10 pro version to open these in Tic-80, but you can view the source code just fine.)

Clicking the file will show you the source code, or you can right-click and SAVE AS, save it into the TIC-80 folder (type folder from the TIC-80 prompt!) to be able to load it in the program.

Class files   Additional examples
Text story 1   Turn a player when moving
Text story 2   Drawing a bigger sprite
Drawing sprites   Distance and collision
Paint program   Timer / state change
Random numbers   Animated small and big sprites
If statements, sound effects   Enemy move and hurt player
Distance function   Map scroll and object placement
Click-a-mole game   Shoot projectile
Keyboard input   Selection menu with keyboard
Non-player character movement   Selection menu with mouse
Object collisions   Spawn multiple items
Background art   Player and multiple enemy objects
Menus and game states   Mouse up event
Pickin' Sticks    

Contact information

You can reach the author of this material at rsingh13 AT jccc DOT edu.