Last active
March 18, 2023 00:11
-
-
Save sebnozzi/3e47b5b08ceaa6b8a207f881eac00a3f to your computer and use it in GitHub Desktop.
Snake movement
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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