LessonsAdvanced: Bigger programsLesson 44

Animation and games

Move controls smoothly with move, animate frame by frame with a timer, answer the arrow keys, find collisions, and put it together in a small game.

  • Advanced
  • Lesson 44 of 83
  • About 12 minutes
  • 7 examples

Moving a control: move

The simplest animation is one command. move slides a control to a point over a time you give, and the engine draws every step between. Give it a list of points, such as a graphic's points, and it follows them one after another:

ExampleRun it in the playground
create graphic "Path"
set the style of it to "polygon"
set the points of it to "40,300" & return & "160,80" & return & "280,300" & return & "400,80"
set the foregroundColor of it to "#94A3B8"
set the dashes of it to "6,6"

create graphic "Ball"
set the style of it to "oval"
set the filled of it to true
set the backgroundColor of it to "#FF7A00"
set the rect of it to 0,0,32,32
set the loc of it to 40,300

move graphic "Ball" to the points of graphic "Path" in 400 milliseconds
put the loc of graphic "Ball"
Output
400,80
Stage what the code made, as the OXT-Beyond engine draws it
The stage, with a grey dashed zigzag line from the bottom left to the top right, and an orange ball at its top right end, where the move finished.

move waits until the control arrives. Add without waiting and the script goes on at once while the control moves; the movingControls lists the controls still on their way:

ExampleRun it in the playground
create graphic "Ball"
set the rect of it to 0,0,20,20
move graphic "Ball" to 200,100 in 500 milliseconds without waiting
put the loc of graphic "Ball"
put the number of lines of the movingControls
Output
10,10
1

Frame by frame

For anything move cannot do (a ball that bounces, a game where everything moves at once) animate it yourself: a handler that changes positions a little, then sends itself again a moment later, as in Timers and waiting. Each run is one frame. About 16 milliseconds between frames gives 60 a second, as smooth as most screens show.

ExampleRun it in the playground
local sBall, sTrail, sDX = 15, sDY = 10, sSteps = 0

on stepBall
   lock screen
   set the left of sBall to the left of sBall + sDX
   set the top of sBall to the top of sBall + sDY
   if the right of sBall >= the width of me or the left of sBall <= 0 then
      multiply sDX by -1
   end if
   if the bottom of sBall >= the height of me or the top of sBall <= 0 then
      multiply sDY by -1
   end if
   set the points of sTrail to the points of sTrail & return & the loc of sBall
   unlock screen
   add 1 to sSteps
   if sSteps < 100 then
      send "stepBall" to me in 16 milliseconds
   else
      put "stopped at" && the loc of sBall
   end if
end stepBall

create graphic "Trail"
set the style of it to "polygon"
set the foregroundColor of it to "#CBD5E1"
set the points of it to "40,40"
put it into sTrail

create graphic "Ball"
set the style of it to "oval"
set the filled of it to true
set the backgroundColor of it to "#14B8A6"
set the rect of it to 20,20,60,60
put it into sBall

stepBall
Output
stopped at 280,280
Stage what the code made, as the OXT-Beyond engine draws it
The stage, with a thin grey line tracing the path of a teal ball that started in the top left and bounced off the edges of the stage, and the ball where it stopped, near the bottom.

Three habits in this example are worth keeping:

  • Keep the long id. After create, it is the new control's long id (Referring to objects). Kept in a script local, it finds the control from any handler, whichever stack is in front when the timer fires. A name such as graphic "Ball" is looked for on the card in front, which, when a timer fires, may be another window's.
  • Measure from me. the width of me is the width of the card or stack whose script this is, which is the same whichever window is in front.
  • Lock the screen. lock screen (Making code fast) holds what is drawn until unlock screen, so the ball's two moves and the trail's new point appear together, as one frame, never half done.

The arrow keys

A key pressed while no field is being typed in goes to the card as a message: arrowKey for an arrow key, with left, right, up or down, and keyDown for any other, with the character. A player moved by the keys is a pair of handlers. Here, send presses the keys, so the example runs by itself:

ExampleRun it in the playground
local sPlayer

on arrowKey pWhich
   switch pWhich
      case "left"
         set the left of sPlayer to the left of sPlayer - 20
         break
      case "right"
         set the left of sPlayer to the left of sPlayer + 20
         break
      case "up"
         set the top of sPlayer to the top of sPlayer - 20
         break
      case "down"
         set the top of sPlayer to the top of sPlayer + 20
         break
   end switch
end arrowKey

create graphic "Player"
set the filled of it to true
set the backgroundColor of it to "#8B5CF6"
set the rect of it to 100,100,140,140
put it into sPlayer

repeat 5 times
   send "arrowKey right" to me
end repeat
repeat 3 times
   send "arrowKey down" to me
end repeat
put the topLeft of sPlayer
Output
200,160
Stage what the code made, as the OXT-Beyond engine draws it
The stage, with a violet square to the right of and below where it started, after the arrow keys moved it.

Collisions

intersect tells whether two controls overlap. Their rectangles are compared, unless you ask for "opaque pixels", which compares what is actually drawn: slower, but exact for round or odd shapes.

ExampleRun it in the playground
create graphic "Player"
set the rect of it to 20,20,60,60
create graphic "Coin"
set the rect of it to 100,20,120,40
put intersect(graphic "Player", graphic "Coin")
set the left of graphic "Player" to 90
put intersect(graphic "Player", graphic "Coin")
Output
false
true

A small game

Put the pieces together: a basket at the bottom of the stage, coins that fall from the top, and a point for every coin the basket catches. Each frame moves every coin down, checks each for a catch, and starts a fallen one again at the top. Here the basket heads for the lowest coin by itself, so the example plays itself; in a real game the arrow keys would move it. randomSeed makes random give the same numbers each run, which is how a game can replay a level:

ExampleRun it in the playground
local sBasket, sScore, sFrame, sCoins

on gameFrame
   add 1 to sFrame
   lock screen
   -- the basket heads for the lowest coin, at most 8 pixels a frame
   put empty into tLowest
   repeat for each line tCoin in sCoins
      if tLowest is empty or the top of tCoin > the top of tLowest then
         put tCoin into tLowest
      end if
   end repeat
   put item 1 of the loc of tLowest - the width of sBasket / 2 into tGoal
   put max(-8, min(8, tGoal - the left of sBasket)) into tStep
   set the left of sBasket to the left of sBasket + tStep
   repeat for each line tCoin in sCoins
      set the top of tCoin to the top of tCoin + 6
      if intersect(tCoin, sBasket) then
         add 1 to sScore
         newCoin tCoin
      else if the top of tCoin > the height of me then
         newCoin tCoin
      end if
   end repeat
   set the text of field "Score" of me to "Catch the coins:" && sScore
   unlock screen
   if sFrame < 140 then
      send "gameFrame" to me in 16 milliseconds
   else
      put "score:" && sScore
   end if
end gameFrame

on newCoin pCoin
   set the loc of pCoin to random(440) + 20, 40 + random(60)
end newCoin

set the randomSeed to 7
set the backgroundColor of this card to "#0F172A"
create field "Score"
set the rect of it to 10,8,300,36
set the opaque of it to false
set the showBorder of it to false
set the textColor of it to "#F8FAFC"
set the textStyle of it to "bold"
set the textSize of it to 16

repeat with i = 1 to 4
   create graphic ("Coin" && i)
   set the style of it to "oval"
   set the filled of it to true
   set the backgroundColor of it to "#FACC15"
   set the rect of it to 0,0,24,24
   newCoin it
   put it & return after sCoins
end repeat
delete the last char of sCoins

create graphic "Basket"
set the style of it to "roundRect"
set the filled of it to true
set the backgroundColor of it to "#8B5CF6"
set the rect of it to 200,320,280,340
put it into sBasket

put 0 into sScore
put 0 into sFrame
gameFrame
Output
score: 7
Stage what the code made, as the OXT-Beyond engine draws it
The stage, a dark blue night sky headed Catch the coins: 7, with gold coins falling at different heights and a violet basket at the bottom.

A real game adds a few things to this shape: arrowKey handlers to move the basket, a frame count or time limit for each level, and a closeCard that cancels the timer (Timers and waiting) so the game stops when its card closes.

Try it yourself

Make a square that grows: a graphic 10 pixels wide in the middle of the stage that, frame by frame, gets 10 pixels wider and taller (keeping its middle where it is) until it is 200 pixels wide, then puts its rect.

Show a solution
ExampleRun it in the playground
local sBox

on grow
   put the loc of sBox into tMiddle
   set the width of sBox to the width of sBox + 10
   set the height of sBox to the height of sBox + 10
   set the loc of sBox to tMiddle
   if the width of sBox < 200 then
      send "grow" to me in 16 milliseconds
   else
      put the rect of sBox
   end if
end grow

create graphic "Box"
set the filled of it to true
set the backgroundColor of it to "#F43F5E"
set the rect of it to 235,175,245,185
put it into sBox
grow
Output
140,80,340,280
Stage what the code made, as the OXT-Beyond engine draws it
The stage, with a large rose-coloured square, 200 pixels wide, in the middle.

Words in this lesson

Each one finds every lesson that uses it. The Dictionary has the whole story of each, with its syntax and more examples.