Skip to content

Instantly share code, notes, and snippets.

@sebnozzi
Last active March 18, 2023 00:11
Show Gist options
  • Save sebnozzi/3e47b5b08ceaa6b8a207f881eac00a3f to your computer and use it in GitHub Desktop.
Save sebnozzi/3e47b5b08ceaa6b8a207f881eac00a3f to your computer and use it in GitHub Desktop.
Snake movement
// Animation class
// It can animate a Sprite from its current position to
// a target position in the desired amount of time, in a
// series of steps.
//
// This last point is important, since it means other things
// can happen at the same time while animating (e.g. processing
// user input, animating other sprites, etc.)
//
// Basic usage:
//
// a = new Animation
// a.init someSprite, targetX, targetY, durationInSeconds
//
// while a.isRunning
// a.update
// yield
// end while
Animation = {}
Animation.sprite = null
Animation.durationSecs = 1
Animation.startX = 0
Animation.startY = 0
Animation.endX = 0
Animation.endY = 0
Animation.startTime = null
Animation.endTime = null
Animation.init = function(sprite, endX, endY, durationSecs=1)
self.sprite = sprite
self.startX = sprite.x
self.startY = sprite.y
self.endX = endX
self.endY = endY
self.deltaX = endX - self.startX
self.deltaY = endY - self.startY
self.startTime = time
self.durationSecs = durationSecs
self.endTime = self.startTime + durationSecs
end function
Animation.update = function
if self.hasTime then
elapsedTime = time - self.startTime
newX = self.startX + self.deltaX * elapsedTime / self.durationSecs
newY = self.startY + self.deltaY * elapsedTime / self.durationSecs
self.sprite.x = newX
self.sprite.y = newY
else
self.sprite.x = self.endX
self.sprite.y = self.endY
end if
end function
Animation.hasTime = function
return time < self.endTime
end function
Animation.endPositionReached = function
endXreached = self.sprite.x == self.endX
endYreached = self.sprite.y == self.endY
return endXreached and endYreached
end function
Animation.isRunning = function
return not self.endPositionReached
end function
// Example
if globals == locals then
s = new Sprite
s.image = file.loadImage("/sys/pics/Wumpus.png")
s.x = 20
s.y = 300
clear
sprDisp = display(4)
sprDisp.sprites.push s
while true
a1 = new Animation
a1.init(s, 800, s.y, 2)
while a1.isRunning
a1.update
yield
end while
a2 = new Animation
a2.init(s, 20, s.y, 2)
while a2.isRunning
a2.update
yield
end while
end while
end if
import "animation"
Animation = animation.Animation
// == CONSTANTS
cellSize = 40
KEY_LEFT = 17
KEY_RIGHT = 18
KEY_UP = 19
KEY_DOWN = 20
LEFT = {"dx": -1, "dy": 0}
RIGHT = {"dx": 1, "dy": 0}
UP = {"dx": 0, "dy": 1}
DOWN = {"dx": 0, "dy": -1}
ADD_AND_REMOVE = "add & remove"
PULL = "pull"
MOVE_TAIL = "move tail to head"
modes = [PULL, ADD_AND_REMOVE, MOVE_TAIL]
// GLOBALS
spriteImg = null
sprd = null
snake = null
mode = null
// == MAIN
main = function
setupDisplays
snake = new Snake
snake.init
modeIdx = 2
globals.mode = modes[modeIdx]
print "Mode: " + mode
while true
print "Step"
if mode == PULL then
snake.animatePull
else if mode == ADD_AND_REMOVE then
snake.animateGrowAndShrink
else if mode == MOVE_TAIL then
snake.animateTailToHead
else
print "Invalid mode: " + mode
exit
end if
if key.available then
k = key.get
if k.code == KEY_LEFT then
print "Going LEFT"
snake.move(LEFT)
else if k.code == KEY_RIGHT then
print "Going RIGHT"
snake.move(RIGHT)
else if k.code == KEY_UP then
print "Going UP"
snake.move(UP)
else if k.code == KEY_DOWN then
print "Going DOWN"
snake.move(DOWN)
else if k == "m" then
modeIdx = (modeIdx + 1) % modes.len
globals.mode = modes[modeIdx]
text.clear
text.row = 25
print "Mode: " + mode
end if
end if
end while
end function
// == SETUP DISPLAYS
setupDisplays = function
clear
display(6).mode = displayMode.sprite
sprd = display(6)
gfx.clear color.clear
sprd.clear
globals["sprd"] = sprd
end function
// == ANIMATE SNAKE
Snake = {}
Snake.init = function
offx = 6
offy = 3
snakePositions = [
{"x": offx+5, "y": offy+5},
{"x": offx+6, "y": offy+5},
{"x": offx+7, "y": offy+5},
{"x": offx+7, "y": offy+4},
{"x": offx+7, "y": offy+3},
{"x": offx+8, "y": offy+3},
{"x": offx+9, "y": offy+3}]
dx = -1
dy = 0
headPos = snakePositions[0]
restPoss = snakePositions[1:]
parts = []
s = newBodyPart(headPos.x,headPos.y,color.lime)
parts.push s
for part in restPoss
x = part.x
y = part.y
s = newBodyPart(x,y)
parts.push s
end for
self.parts = parts
self.dx = dx
self.dy = dy
end function
Snake.animateTailToHead = function
oldHead = self.parts[0]
// Draw trunk border
trunk = self.parts[0:-1]
for part in trunk
drawBorder part.col,part.row
end for
oldHead.tint = color.green
wait 0.8
print "Move tail to head"
// Remove tail
tail = self.parts[-1]
self.parts.pop
// Add to head
newHead = tail
newHead.col = oldHead.col + self.dx
newHead.row = oldHead.row + self.dy
// Animate
newHead.move newHead.col,newHead.row
newHead.tint = color.lime
self.parts.insert 0,newHead
wait 0.8
clearBorders
end function
Snake.animateGrowAndShrink = function
tailSpr = self.parts[-1]
oldHead = self.parts[0]
// Draw trunk border
trunk = self.parts[0:-1]
for part in trunk
drawBorder part.col,part.row
end for
oldHead.tint = color.green
wait 0.8
// Show new head
head = self.parts[0]
nextHeadPosition = {}
nextHeadPosition.col = head.col + self.dx
nextHeadPosition.row = head.row + self.dy
nhp = nextHeadPosition
newHead = newBodyPart(nhp.col,nhp.row,color.black)
self.parts.insert 0,newHead
print "Add head"
for t in range(0,100)
newHead.tint = color.lerp(color.black,color.lime,t/100)
yield
end for
print "Remove tail"
// Fade out tail
for t in range(0,100)
tailSpr.tint = color.lerp(color.green,color.black,t/100)
yield
end for
// Remove snake tail sprite
sprd.sprites.remove sprd.sprites.indexOf(tailSpr)
wait 0.2
clearBorders
// Remove tail from model
self.parts.pop
end function
Snake.animatePull = function
// Draw trunk border
trunk = self.parts[0:-1]
for part in trunk
drawBorder part.col,part.row
end for
wait 0.8
// Insert phantom tail
tail = self.parts[-1]
tailSpr = newBodyPart(tail.col,tail.row,color.gray)
sprd.sprites.remove sprd.sprites.indexOf(tailSpr)
sprd.sprites.insert 0,tailSpr
// Move snake ...
head = self.parts[0]
previous = {}
previous.col = head.col
previous.row = head.row
nextHeadPosition = {}
nextHeadPosition.col = head.col + self.dx
nextHeadPosition.row = head.row + self.dy
head.move nextHeadPosition.col,nextHeadPosition.row
for idx in range(1,self.parts.len-1)
tempCol = self.parts[idx].col
tempRow = self.parts[idx].row
self.parts[idx].move previous.col,previous.row
previous.col = tempCol
previous.row = tempRow
end for
for t in range(0,100)
tailSpr.tint = color.lerp(color.gray,color.black,t/100)
yield
end for
wait 0.2
clearBorders
// Remove tail sprite
sprd.sprites.remove 0
end function
Snake.move = function(dir)
self.dx = dir.dx
self.dy = dir.dy
end function
// == BODY PART
newBodyPart = function(col=null,row=null,c=null)
if col == null then col = 0
if row == null then row = 0
if c == null then c = color.green
s = new BodyPart
s.init col,row,c
sprd.sprites.push s
return s
end function
BodyPart = new Sprite
BodyPart.init = function(col,row,c)
if globals.spriteImg == null then
globals.spriteImg = makeSpriteImg
end if
self.image = spriteImg
self.tint = c
self.setXY col,row
self.col = col
self.row = row
end function
BodyPart.setXY = function(col,row)
self.x = col * cellSize + cellSize / 2
self.y = row * cellSize + cellSize / 2
end function
BodyPart.move = function(col,row)
a1 = new Animation
tx = col * cellSize + cellSize / 2
ty = row * cellSize + cellSize / 2
a1.init self, tx, ty, 0.25
while a1.isRunning
a1.update
yield
end while
self.col = col
self.row = row
end function
// == DRAWING
drawBorder = function(x,y,c=null)
if c == null then
c = color.red
if globals.mode == ADD_AND_REMOVE then c = color.fuchsia
end if
cs = cellSize
gfx.drawRect x*cs,y*cs,cs,cs,c,2
end function
clearBorders = function
gfx.clear
end function
makeSpriteImg = function
tileMap = new PixelDisplay
tileMap.clear color.clear
tileMap.fillRect 0,0,cellSize,cellSize,color.white
tileMap.drawRect 0,0,cellSize,cellSize,color.black
img = tileMap.getImage(0,0,cellSize,cellSize)
return img
end function
// == MAIN INVOCATION
main
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment