Unit 4 · Lesson 10 Intermediate ⏱ ~60 minutes

Sprite Engine Basics

Welcome to Unit 4 — Building Games! HyperXTalk includes a powerful built-in sprite engine that lets you create 2D games with moving characters, collision detection, and smooth animation. In this lesson you'll learn the fundamentals: placing sprites, making them move, tracking keyboard input, and detecting when sprites collide.

⚠️ The Sprite Engine is a separate download — grab it from the HyperXTalk community forums and add it to your stack before using any of the commands in this lesson. From v0.9.16 onwards it will be available directly in HyperXTalk via Tools → Package Manager → Sprite Engine.
🎯 Learning Objectives
  • Set up the Sprite Canvas widget on a card
  • Create, position, and size sprites
  • Set velocity to make sprites move automatically each tick
  • Use the spriteTick handler as your game loop
  • Track which keys are held down for smooth movement
  • Detect collisions between sprites
  • Read sprite position and size with spriteInfo

🎮 Overview

The HyperXTalk sprite engine revolves around three things: a Sprite Canvas widget that does the rendering, a set of commands for creating and controlling sprites, and a tick loop that calls your card script many times per second so you can update game state.

The engine script is included with HyperXTalk. Add it to your stack once and all the sprite commands become available everywhere. The basic structure of any sprite game looks like this:

card script
-- 1. Set up everything when the card opens on openCard setSpriteCanvas "GameCanvas" createSprite "player", "hero.png", 100, 100 resumeSprites end openCard -- 2. Update game state every tick on spriteTick -- called ~60 times per second end spriteTick -- 3. Handle sprite collisions on spriteCollision pA, pB answer pA & " hit " & pB end spriteCollision
💡 Always call resumeSprites at the very end of openCard, after all your sprites are created and configured. Starting the tick loop before everything is set up can cause errors.

🖼️ The Sprite Canvas

The Sprite Canvas is a widget — add it to your card from the Tools palette just like any other control. Resize it to fill whatever area you want your game to occupy. Give it a memorable name in the Property Inspector (e.g. GameCanvas) then tell the engine about it at the start of openCard:

setSpriteCanvas "GameCanvas"

If you only have one Sprite Canvas on the card you can skip this — the engine will find it automatically. But calling it explicitly is good practice and avoids confusion if you ever add a second canvas.

You can also set the canvas background colour:

-- RGB values as 0-1 floats: dark navy blue setSpriteBackground 0.1, 0.1, 0.2

🧩 Creating Sprites

A sprite is a named image that lives on the canvas. Create one with createSprite, passing its name, image source, and optionally its starting position and size:

-- Name and image only (position defaults to 0,0) createSprite "player", "hero.png" -- Name, image, and starting position createSprite "player", "hero.png", 100, 200 -- Name, image, position, and size createSprite "player", "hero.png", 100, 200, 64, 64 -- Move or resize afterwards setSpritePosition "player", 150, 300 setSpriteSize "player", 80, 64

The image source can be a file path or the name of a HyperXTalk image object stored in the stack. If you don't provide an image, a magenta placeholder rectangle is drawn instead — useful for testing layout before your art is ready.

You can show and hide sprites, swap their images, and remove them:

setSpriteVisible "player", false -- hide setSpriteVisible "player", true -- show setSpriteImage "player", "hero2.png" -- swap image destroySprite "enemy1" -- remove sprite destroyAllSprites -- remove all sprites

🏃 Movement and Velocity

The most natural way to make sprites move is to set their velocity — the number of pixels to move each tick. The engine adds this to the sprite's position automatically on every tick:

-- Move right at 3 pixels per tick setSpriteVelocity "player", 3, 0 -- Move left and slightly upward setSpriteVelocity "player", -2, -1 -- Stop moving setSpriteVelocity "player", 0, 0

Positive X moves right, negative X moves left. Positive Y moves down, negative Y moves up — the canvas coordinate system has (0,0) at the top left.

🟢 At the default 60fps, a velocity of 3 moves a sprite about 180 pixels per second — roughly 3 tile widths per second if your tiles are 64px. Adjust to taste for the feel of your game.

⏱️ The Tick Loop

The tick loop is the heartbeat of your game. Every tick (about 60 times per second), the engine sends a spriteTick message to your card. Handle it to update game logic — check for collisions, apply physics, respond to input:

card script
on spriteTick -- Bounce off the right edge of a 600px canvas if spriteX("ball") > 600 then setSpriteVelocity "ball", -spriteVX("ball"), spriteVY("ball") end if -- Bounce off the left edge if spriteX("ball") < 0 then setSpriteVelocity "ball", abs(spriteVX("ball")), spriteVY("ball") end if end spriteTick

You can pause and resume the tick loop at any time:

pauseSprites -- freeze everything (use for pause menus, game over screens) resumeSprites -- start or restart the loop

⌨️ Key Tracking

For smooth player movement you need to know which keys are currently held down, not just which key was last pressed. The standard pattern is to track this yourself using rawKeyDown and rawKeyUp handlers in your card script, storing held keys in local variables:

card script
-- Track movement keys as local variables local gKeyRight, gKeyLeft on rawKeyDown pKey if pKey is 100 then put true into gKeyRight -- D if pKey is 97 then put true into gKeyLeft -- A end rawKeyDown on rawKeyUp pKey if pKey is 100 then put false into gKeyRight if pKey is 97 then put false into gKeyLeft end rawKeyUp on spriteTick if gKeyRight then setSpriteVelocity "player", 3, 0 if gKeyLeft then setSpriteVelocity "player", -3, 0 if not gKeyRight and not gKeyLeft then setSpriteVelocity "player", 0, 0 end if end spriteTick

Key codes for common game keys:

KeyCodeKeyCode
A97D100
W119S115
↑ Up arrow65362↓ Down arrow65364
← Left arrow65361→ Right arrow65363
Space32Return13
🟢 Put rawKeyDown and rawKeyUp in your card script, not a button script. The card receives key events naturally when no other control has focus. If the Sprite Canvas takes focus, it forwards key events back to the card automatically.

💥 Collision Detection

The engine checks for sprite collisions automatically every tick and sends a spriteCollision message to your card whenever two sprites overlap. Handle it to react to collisions:

card script
on spriteCollision pA, pB -- pA and pB are the names of the two sprites that collided if (pA is "player" and pB is "coin") or \ (pA is "coin" and pB is "player") then destroySprite "coin" answer "Coin collected!" end if end spriteCollision

You can also check for collision manually at any point using spritesCollide:

if spritesCollide("player", "goal") then pauseSprites answer "You reached the goal!" end if

For precise collision with complex sprites, you can define a custom hitbox — a rectangle smaller than the visible sprite that's used for collision testing. This avoids false collisions at the transparent edges of an image:

-- For an 80x64 sprite, use a tighter 30x56 hitbox offset 25px from the left, 8px from top setSpriteHitbox "player", 25, 8, 30, 56

📊 Reading Sprite State

spriteInfo returns a comma-delimited string with everything you need to know about a sprite. There are also convenience functions for reading individual values:

-- Full info string: name,x,y,w,h,vx,vy,visible,group put spriteInfo("player") into tInfo -- Convenience functions put spriteX("player") -- x position put spriteY("player") -- y position put spriteWidth("player") -- width put spriteHeight("player") -- height put spriteVX("player") -- horizontal velocity put spriteVY("player") -- vertical velocity
🛠️ Exercise — Catch the Star

Build a simple game where the player chases a star around the canvas using the A and D keys. When they collide, the star jumps to a new random position and the score increases.

  1. Create a new stack. Add a Sprite Canvas widget from the Tools palette and name it GameCanvas. Size it to fill most of the card. Add a Label above it named ScoreLabel.
  2. Add this to the card script:
    card script
    local gKeyRight, gKeyLeft, gScore on openCard put 0 into gScore setSpriteCanvas "GameCanvas" createSprite "player", empty, 200, 200, 40, 40 createSprite "star", empty, 350, 150, 30, 30 resumeSprites end openCard on spriteTick if gKeyRight then setSpriteVelocity "player", 4, 0 else if gKeyLeft then setSpriteVelocity "player", -4, 0 else setSpriteVelocity "player", 0, 0 if spritesCollide("player", "star") then add 1 to gScore setSpritePosition "star", random(550), random(350) set the text of field "ScoreLabel" to "Score: " & gScore end if end spriteTick on rawKeyDown pKey if pKey is 100 then put true into gKeyRight if pKey is 97 then put true into gKeyLeft end rawKeyDown on rawKeyUp pKey if pKey is 100 then put false into gKeyRight if pKey is 97 then put false into gKeyLeft end rawKeyUp
  3. Switch to Browse mode and press A and D to chase the magenta star. Each time you catch it, it jumps to a random new position and your score goes up.

Bonus challenges: Add W and S keys so the player can also move up and down. Add a second star that moves on its own by setting its velocity in openCard and bouncing it off the canvas edges in spriteTick.

📝 Review Questions
Question 1
What command starts the sprite engine tick loop, and when should you call it?
resumeSprites — call it at the very end of openCard, after all sprites have been created and configured. Starting the loop before everything is set up can cause errors.
Question 2
In the canvas coordinate system, which direction is positive Y?
Positive Y moves downward. The origin (0,0) is at the top-left of the canvas. So a positive Y velocity makes a sprite fall, and a negative Y velocity makes it rise.
Question 3
Why use rawKeyDown/rawKeyUp rather than just checking key state inside spriteTick?
Using rawKeyDown/rawKeyUp to track held keys in boolean variables gives you smooth, frame-perfect movement — the sprite moves every tick the key is held. Checking inside spriteTick alone would miss the key being held between ticks, causing stuttery movement.
Question 4
What is a hitbox, and why would you use one?
A hitbox is a custom collision rectangle smaller than the sprite's visible image. Because sprite images often have transparent padding around the character, using the full image size causes collisions to fire before sprites visually touch. A tighter hitbox makes collisions feel more accurate and fair.
Question 5
What does item 3 of spriteInfo("player") return?
The Y position of the sprite. The spriteInfo string is formatted as name,x,y,w,h,vx,vy,visible,group — so item 1 = name, item 2 = x, item 3 = y, item 4 = width, item 5 = height, and so on.