Archives December 2021

Bale Example: Dodgeball

Dodgeball is a very simple game using a bale wherein the player moves the turtle from the left to right sides of the screen using the keyboard.

This is similar to a popular example used to teach Scratch, and the basic concept should be familiar to most Scratch learners.

In the case of this example, there are multiple balls that are controlled by a ‘bale’, a group of turtles who all execute the same code, first the code in the bale’s init procedure, and then the code in the ‘main’ procedure repeatedly. Each member of the bale executes consecutively, and once all members of a bale have executed (a cycle), the bale’s graphical output (its ‘turtle tracks’) is updated.

The bale’s execution is started by Myrtle, using the startbale command.

Myrtle checks to see if the user has pressed any of the movement keys and moves if required. She also checks to see if she’s been hit by a ball, or if she’s reached the right side of the screen, and if either condition has been met, the game ends.

The balls meanwhile move up and down at different speeds, gradually sliding towards the left side of the screen (to prevent Myrtle from procrastinating!)

NEWTURTLE "myrtle

TO start
  ;this is a very simple yet addicting and frustrating game
  ;add in your own sound effects!

  reset
  setmodel [bk 7.5 stamp "myrtle]
  ;myrtle's position is usually her tail, so we need to move her a bit
  ;so that her position is center-ish of her shell

  startbale "balls 11
  pu
  setxy -170 0
  rt 90
  forever [
    if keyp [
      make "key lowercase readchar
      if :key = "k [sr 10]
      if :key = "i [sl 10]
      if :key = "j [bk 10]
      if :key = "l [fd 10]
    ]
    ;pressing keys causes turtle to move

    if nearp 15 [pr "ouch! repeat 1000 [rt 1] finish]
    ;got hit by a ball

    if xpos > 180 [pr "win! finish]
    ;made it to the other side!
  ]
END

ON start flag queue []
  start
END


NEWTURTLE "snappy


NEWTURTLE "libby


NEWBALE "balls

TO init
  setmodel {"setfc baleindex "ico 10}
  st
  penup
  setxy -160 + baleindex * 30 100
  output {"dir 1 "speed 1 + (0.1 * random (5 * (5 + baleindex)))}

END

TO main
  if :dir = 0 [bk :speed] [fd :speed]

  slideleft 0.1
  ;the balls gradually move toward the left side
  ;of the screen to discourage procrastination!

  if ypos < -100 [make "dir 1]
  if ypos > 100 [make "dir 0]
END

 

Bales, Missiles and Triggers, Oh My!

turtleSpaces is great for creating 3D models and animations, but we also want it to be great for games. Unfortunately, the interpreter is not the fastest thing around — but that’s okay! We can compensate by creating new commands that work ‘under the hood’ to take care of certain game elements.

Bales are groups of turtles that all execute the same code, for example alien attackers in a space battle game. They are declared similarly to turtles, using a NEWBALE declaration.

When each member of a bale is initialized, it executes the code in the bale’s INIT procedure. Then, it repeatedly executes the code in the MAIN procedure in sequence with the other members of the bale, that is each member of the bale executes the code in succession using the same thread. At the end of each cycle, when all bale members have executed, their ‘turtle tracks’ are rendered. This makes it seem like they’ve all moved at the same time.

Take for example this code from a Space Invaders-inspired game (you can try it out here: https://turtlespaces.org/weblogo/?pub=81)

NEWBALE "ships

TO init
  ;bales are groups of turtles that execute the same code in sequence,
  ;one after the other. They are useful for things like groups of
  ;enemies, to keep them in lock step, where hatchlings can end up
  ;executing at different rates of speed and fall out of sequence
  ;with each other.
  
  ;new bale members always execute the init procedure, after which
  ;they repeatedly execute the main procedure until they are
  ;removed or the entire bale is stopped.
  
  penup
  noaudiowait
  newmodel "bullet [setfillcolor 15 icosphere 1.5]
  setmodelscale 1.5
  setposition {
    (-150 + 30 * (remainder baleindex - 1 5))
    100 - (20 * (int ((baleindex - 1) / 5)))
    5
  }
  ;based on the number (index) of the bale member, place
  ;it in the attacking wave
  
  if 1 = remainder int (baleindex - 1) / 5 2 [setx xpos + 10]
  ;let's offset the turtles in alternating rows
  
  right 180
  ;turn to face down
  
  showturtle
  ;show the turtle
  
  rollright baleindex * 10
  ;set the default roll rotation
  
  output [dir 1]
  ;the pairs provided by output become variables in main
  ;in this case we're setting the default move direction, which
  ;is the same for all members in this bale
END

Each member of the bale is placed based upon its index number in the bale. Once we position the bale member, it moves on to the main execution loop:

TO main
  dosleep 10 [
    ;take at least 10 milliseconds to do the following:
    
    rollright 10
    ;roll right ten degrees
    
    if and xpos < 150 :dir = 1 [drift 5 [1 0 0] drift 1 [0 -1 0]] if and xpos = 150 :dir = 1 [make "dir 0 drift 10 [0 0 -1] drift 10 [0 -1 0]] if and xpos > -150 :dir = 0 [drift 5 [-1 0 0] drift 1 [0 -1 0]]
    if and xpos = -150 :dir = 0 [make "dir 1 drift 10 [0 0 1] drift 10 [0 -1 0]]
    ;we use drift to move the ships because we're rolling them for effect
    
    if 1 = random 20 [
      newmissile "missile "bullet [0 -1 0] 20 + random 20 [200]
      playsound "zap3
    ]
    ;fire a missile randomly
    
    if or ypos < -100 containsp "myrtle near position 20 [ make "hit true ] ;if an alien reaches the bottom of the screen it's over if baleindex = last balemembers balename [ if :delay > 0 [dec "delay]
      ;this increases the speed of the invaders 'music'
      ;once each time all the bale turtles have executed
      ;(each loop)
    ]
  ]
END

Each bale member moves across the screen, randomly firing missiles, until they are destroyed or reach the bottom of the screen, triggering a ‘game over’ condition.

newmissile "missile "bullet [0 -1 0] 20 + random 20 [200]

Missiles are simple elements that are controlled by the game engine. They move through 3D space, and are detected by primitives such as NEARMISSILEP and NEARMISSILES. They can also be detected by the nearmissile trigger. They are much more efficient than using turtles as projectiles.

Similarly, triggers are blocks of code that are executed when a certain condition occurs. These conditions are monitored by the underlying game engine, and are ‘triggered’ when these conditions are detected to be true. Once again, this is much more efficient then having a turtle constantly execute Logo code waiting for the condition to occur!

The following trigger belongs to the previously mentioned alien ships bale:

ON boom nearmissile block [missile2 10]
  hideturtle
  playsound "explosion
  inc "score
  show :score
  stopmissile flat list :boom :boomanim
  die
  ;this turtle is no more
END

This trigger increments the player’s score, disposes of the missile that triggered it, and then ends the turtle to whom the trigger belongs, in this case a member of the alien ship bale.

As you can see, bales, missiles and triggers all contribute to empowering turtleSpaces coders to create faster-paced, more engaging games.