Example: Invasion (3D Space Invaders Clone)

Check out this simple space invaders clone, featuring 3D UFO’s and UFO and player movement. This runs well on both the application and in the web version using modest hardware.

Rather than using hatchlings each with its own thread (worker), Invasion creates 15 static turtles representing each UFO, then iterates through moving them using the main (Myrtle) worker, more like a conventional single-threaded application would.

One procedure is used to generate randomly-colored UFO models, which are then assigned to each UFO turtle. The UFO turtles move in a 3D pattern created by ‘rolling’ them (rotating them on the Z axis), moving them left or right using slideleft and slideright, and raising and lowering them (using raise and lower) – a great exploration of 3D movement in turtleSpaces.

The code is commented and follows below. It’s also available inside the WebIDE under the menu FIle -> Browse Published…

NEWTURTLE "myrtle

TO game
  ;invasion is a simple space invaders clone with a 3D twist
  
  reset
  release
  ;reset the workspace and release the non-standard turtles (ufos)
  
  stars:drawstars
  ;draw a starfield using the stars turtle
  
  nokeyclick
  ;don't click keys
  
  noaudiowait
  ;don't pause on audio playback
  
  snappy:newworker [
    pullout 100
    forever [
      repeat 45 [orbitleft 1 wait 5]
      repeat 90 [orbitright 1 wait 5]
      repeat 45 [orbitleft 1 wait 5]
      repeat 45 [orbitup 1 wait 5]
      repeat 90 [orbitdown 1 wait 5]
      repeat 45 [orbitup 1 wait 5]
    ]
  ]
  ;create a new 'worker' to move the camera around the scene
  ;independently of other activity
  
  make "fleet []
  share "fleet
  ;the fleet container holds the list of UFO turtle names
  ;it needs to be shared so all the UFO turtles can edit it
  
  make "score 0
  share "score
  ;the score cotnainer holds the score, the number of UFOs destroyed
  ;the UFOs themselves increment the score, and so it must be shared
  
  repeat 3 [
    repeat 5 [
      newtu word repcount repabove 1
      ;create a new turtle with the name column + row
      ;note: newtu is NOT the same as newturtle, newturtle is a
      ;declaration used in the workspace file (this), while newtu
      ;creates a new turtle during execution and selects it
      ;(we should change its name to instatiate?)
      
      noaudiowait
      
      makemodel
      ;create its UFO model
      
      queue turtle "fleet
      ;add the turtle name to the fleet container
      
      make "shootcount 0
      ;reset its shootcounter to 0
      
      penup
      ;don't draw anything!
      
      setposition {-225 + repcount * 75 -25 + (repabove 1) * 50}
      ;set its position based on its row and column number
      ;curly braces indicate a 'soft list', a list that is evaluated
      ;at runtime. repabove gets the loop count of the repeat enclosing
      ;the current repeat
      
      showturtle
      ;show the UFO
    ]
  ]
  ;create three rows of 5 UFOs each
  
  setturtle myrtle
  ;reselect myrtle
  
  setmodel "spaceship
  ;change myrtle's model to the built-in spaceship model
  
  setmodelscale 2
  ;set the model's scale (size) to a factor of 2
  
  penup back 150
  ;position Myrtle
  
  newworker [
    forever [
      movefleet
      ;run the movefleet procedure
      
      toot 200 + random 1000 2
      ;make a random beep
      
      wait 10
    ]
  ]
  ;create a new 'worker' to move the UFOs 'forever'
  
  make "delay 0
  ;stores the delay between player shots
  
  forever [
    if :delay > 0 [dec "delay]
    ;decrease the delay value by one, if it is above zero
    
    if keyp [
      ;if a key has been pressed:
      
      make "key uppercase readchar
      ;store the uppercase value of the key in the key container
      
      switch "key
      ;use the value of the key value for the following case comparisons:
      
      case "J [repeat 10 [repeat 10 [sl .1]]]
      case "K [repeat 10 [repeat 10 [sr .1]]]
      case "I [repeat 10 [repeat 10 [ra .1]]]
      case "M [repeat 10 [repeat 10 [lo .1]]]
      ;move Myrtle as required
      
      case "A [
        ;should I shoot?
        if :delay = 0 [
          ;yes, I should shoot!
          
          ;we're going to 'hatch' a missile BUT if we just
          ;do that, our detection routine will think we've been shot.
          ;to work around this, we're going to stop rendering our
          ;spaceship while we launch our missile from a position
          ;slghtly forward of our current position.
          
          notrackrender
          ;don't render this turtle's graphics
          
          forward 31
          updatestate
          ;update the state of the turtle. This is usually done while
          ;rendering, so we need to do this manually while rendering is
          ;off
          
          hatch [
            showturtle
            repeat 300 [fd 1]
          ]
          ;hatch our 'missile', which goes forward 300 steps and then 'dies'
          
          back 31
          ;move back to where we were before
          
          trackrender
          ;turn track rendering back on
          
          playsound "pew
          ;play a 'pew' sound
          
          make "delay 20
          ;set the delay container to 20, which causes us to have to wait
          ;before firing again
        ]
      ]
    ]
    
    if nearp 30 [
      ;check to see if we've been hit. If we have:
      
      playsound "explosion
      hideturtle
      print |Boom! Game over!|
      repeat 50 [
        randomfillcolor
        icosphere repcount
        wait 1
      ]
      ;explosion animation
      
      clean finish
      ;finish stops all execution
    ]
    
    wait 1
    ;wait 1/60ths of a second between loops
  ]
  ;do it again
  
END

TO movefleet
  ;this procedure moves the UFOs
  
  if :fleet = emptylist [
    ;if there are no UFOs left:
    print |Great work!|
    playsound "applause
    wait 120
    finish
  ]
  
  foreach "ufo :fleet [
    ;for each of the UFOs remaining:
    
    setturtle :ufo
    ;select the ufo turtle
    
    rollright 10
    ;roll right 10 degrees
    
    if 1 = last :ufo [slideright 5]
    if 1 = first :ufo [lower 5]
    if 2 = last :ufo [slideleft 5]
    if 2 = first :ufo [raise 5]
    if 3 = last :ufo [slideright 5]
    if 3 = first :ufo [lower 5]
    ;do a merry dance
    
    if :shootcount > 0 [dec "shootcount]
    if and 1 = random 20 :shootcount = 0 [
      make "shootcount 20
      hatch [
        right 180 showturtle
        repeat 300 [fd 1 rr 1]
      ]
    ]
    ;fire!
    
    foreach "i near position 50 [
      if "m = second :i [ht
        newworker [repeat 50 [ico repcount wait 1] clean]
        ;boom
        
        make "fleet without turtle :fleet
        ;remove ufo turtle from fleet list
        
        inc "score (print |Score:| :score)
        ;increase the score and tell the user
        
        playsound "explosion
        ;play an 'explosion' sound
        
        myrtle:try [halt hatchlingworker :i] []
        ;stop the missle that shot me (if it still exists)
        
      ]
    ]
    ;you got me!
  ]
  
  tu myrtle
  ;reselect Myrtle
END

NEWTURTLE "snappy


NEWTURTLE "libby

TO makemodel
  ;make the UFO model for the calling turtle
  
  begintag turtle
  makeufo
  endtag
  newmodel word "ufo turtle turtle
  
  clearscreen
  ;this applies only to the calling turtle
  
  setmodel word "ufo turtle
END

TO makeufo
  ;create the UFO itself
  
  penup
  randomfillcolor
  
  make "sides 3 + random 6
  down 90
  rollright 180
  
  torus 10 25 3 :sides
  ;make the outer ring
  
  switch {fc}
  case 1 [setfc 9]
  case 2 [setfc 7]
  case 3 [setfc 11]
  case 4 [setfc 12]
  case 5 [setfc 10]
  case 6 [setfc 2]
  case 7 [setfc 6]
  case 8 [setfc 13]
  case 9 [setfc 1]
  case 10 [setfc 15]
  case 11 [setfc 3]
  case 12 [setfc 14]
  case 13 [setfc 8]
  case 14 [setfc 12]
  case 15 [setfc 5]
  ; select a complementary fill color
  
  dome 25 3 :sides
  ;make the top dome
  
  polyspot 25 :sides
  ;make the bottom spot
  
END

NEWTURTLE "stars

TO drawstars
  ;draw the starfield
  penup
  
  repeat 100 [
    ;do 100 times:
    home
    ;go home
    
    randomvectors
    ;pick a random vector orientation
    
    forward 1000 + random 1000
    up 90
    randomfillcolor
    spot 5 + random 10
    ;create a randomly colored 'star'
  ]
  
END