Floaty Turtle: A Flappy Bird clone in Logo

Floaty Turtle is a simple clone of Flappy Bird written in the Logo programming language that turtleSpaces uses.

Open in the integrated development environment (IDE)

NEWTURTLE "myrtle

TO floaty
  ;here is yet another example of a relatively complex
  ;game that can be rather simply implemented in Logo:
  ;a flappy bird clone!

  ;to make it work we use two turtles, one is the player
  ;and the other creates, moves and erases the pipes
  ;these are two seperate 'workers' or threads

  ;this procedure kicks off the game, displays a title
  ;screen, and then manages the player, processing
  ;input and moving the player appropriately

  ;the pipemaker turtle and its pipes procedure
  ;manages the pipes and checks for collisions

  ;the turtle does not move horizontally,
  ;instead the pipes do!

  reset
  cleartext
  setbackgroundcolor pick [6 7 14 5 10 11 14 15]
  ;select a random background color from the given list

  noaudiowait
  ;don't wait for audio to finish playing

  setmodel [penup back 7 stamp "myrtle]
  ;we need to offset Myrtle's actual position more
  ;toward her center for the purposes of this game

  setmodelscale 5
  ;make myrtle big!

  playsound "doodoo
  ;startup sound

  pipemaker:newworker [pipes]
  ;'kicks off' the pipemaker turtle, who
  ;creates and moves the pipes

  ;create the title screen:

  penup
  randomfillcolor
  slideleft 180 forward 5
  settypesize 60
  pushturtle
  ;'push' the turtle's state (position etc) on
  ;to a stack, from which it can be 'popped'
  ;off later

  typeset "Floaty
  popturtle
  ;restore the previously pushed state

  back 100
  randfc
  ;short for randomfillcolor

  pushturtle
  typeset "Turtle
  popturtle
  bk 10 sr 70
  ;back and slideright

  settypesize 10
  randfc
  typeset |Press any key to float!|
  ;title screen complete!

  home
  dn 90 rt 90
  ;down and right

  make "raise 0
  ;the :raise container holds the current
  ;value remaining to float Myrtle upwards

  forever [

    if loopcount = 1 [say "Ready!]
    if loopcount = 16 [say "Set!]
    if loopcount = 31 [say "Go!]
    ;the loopcount is the number of times the forever
    ;loop has executed. Based on that count, say the
    ;appropriate ready set go component

    if divisorp 100 loopcount [
      ;every 100 loops pick a random background
      setbg pick [6 7 14 5 10 11 14 15]
      ;from the given list
    ]
    ;setbg short for setbackground

    dosleep [50] [
      ;try to maintain an average of 50ms to do
      ;the following:

      if loopcount > 30 [
        ;if the loopcount is greater than 30:

        if :raise = 0 [
          ;if the value inside the :raise container
          ;is 0, then lower the turtle 2.5 turtle-units:

          lower 2.5
        ]
        [
          ;otherwise raise the turtle 2.5 turtle-units
          ;and decrease the value inside the :raise
          ;container by 2.5
          raise 2.5
          make "raise :raise - 2.5
        ]

        if keyp [
          ;if a key is pressed, play a sound,
          ;remove the key from the keyboard buffer
          ;and increase the value of the :raise
          ;container by 20:

          playsound "air
          clearchar
          make "raise :raise + 20
        ]

        clean
        ;remove the turtle's 'track' -- it's not
        ;drawing or creating anything so we don't
        ;need to have it piling up

      ]
    ]
  ]
END
NEWTURTLE "pipemaker

TO pipes
  ;the pipemaker turtle's job is to
  ;create the pipes, shift them to the left
  ;and check if they've hit the turtle

  clearscreen
  noaudiowait
  penup
  home
  slideright 250
  back 120
  up 90
  ;position the turtle appropriately for
  ;creating the pipes offscreen to the right

  begintag "move
  endtag
  ;this tag is used to shift the pipes,
  ;by replacing its contents with an ever-
  ;increasing slideleft directive

  make "height 100
  ;this is the starting height of the pipes

  make "heights [0 0 0 0 0]
  ;initialize an 'zeroes' list of pipe heights

  make "score 0
  make "count 0
  ;initialize the score and the pipe count

  settypesize 20
  ;set the type size. Type is the text you
  ;create inside the 3D world

  forever [
    ;do this forever:

    sleep [50]
    ;try to do this stuff in an average of
    ;50ms -- less or more as needed to keep
    ;that average:

    if divisorp 20 loopcount [
      ;every 20 loops:

      inc "count
      ;increase the contents of the :count container
      ;by one

      if loopcount > 60 [
        ;if the loopcount is greater than 60:
        playsound "clang
        inc "score
        say :score
        ;make a sound, increment :score and say it
      ]

      slideright 100

      begintag loopcount
      ;create a new 'tag' for the pipe
      ;so we can remove it after it passes the
      ;left side of the screen

      randomfillshade
      ;select a random fill shade

      make "col 1 + random 5
      ;select a random number and put it in :col

      setfillcolor item :col [12 9 7 6 11 14 10 13]
      ;set that color based on :col's index in the
      ;provided list

      make "height :height + (-60 + random 120)
      ;increase or decrease :height based on a
      ;random number

      if :height < 20 [make "height 30 + random 20] if :height > 120 [make "height 110 - random 20]
      ;if too high or low, pick a new value higher
      ;or lower as needed

      queue :height "heights
      ;add the new height to the list of heights

      ;create the lower pipe:
      cylinder 20 :height 50
      lower :height
      setfillcolor item :col [4 8 6 2 1 12 5 3]
      ;select the complementary color for the pipe

      cylinder 25 20 50
      lower 50

      ;type the pipe number:
      down 90 right 90 back 10
      if :count > 9 [bk 10]
      inscribe :count
      if :count > 9 [fd 10]
      forward 10 left 90 up 90
      ;if gameplay is too slow, comment out
      ;the above five lines

      ;create the upper pipe:
      lower 50
      ;remember, the turtle is upside down!
      cylinder 25 20 50
      lower 20
      setfillcolor item :col [12 9 7 6 11 14 10 13]
      cylinder 20 120 - ypos 50
      raise :height + 120

      endtag
      ;close the pipe 'tag'

      if loopcount > 119 [erasetag loopcount - 100]
      ;erase the pipe that has left the screen

      ignore pop "heights
      ;remove its height from the :heights container
      ;and just throw it away (ignore it)
    ]

    if loopcount > 60 [
      ;check for crash:
      if (or
        myrtle:ypos > (-120 + (90 + item 3 :heights))
        myrtle:ypos < (-120 + (20 + item 3 :heights))
        ;the third item in the :heights list is the
        ;height of the pipe around where the turtle
        ;is, so we can use that to see if we've hit
        ;anything
      ) [
        (print |Crash! Final Score: | :score)
        print |Press flag icon to play again!|

        calm "myrtle
        myrtle:run [setfillcolor 1 icosphere 30]
        ;red ball

        audiowait
        playsound "crack
        playsound "aw

        myrtle:clean
        ;gets rid of the red ball

        noaudiowait
        playsound "fall
        say |too bad|

        myrtle:repeat 90 [
          forward 3 lower 3 wait 1
        ]
        ;fall to the ground

        audiowait
        playsound "crash
        finish
        ;that's all folks

      ]
    ]

    replacetag "move [slideleft loopcount * 5]
    ;increase the value of the slideleft
    ;command in the move tag, shifting the
    ;pipes to the left

  ]

  ;and that's it! Not really much code, is it?

END