07-19-2024, 05:48 PM
I have a question about the snake game. The snake's body is divided into small pieces and they follow the head. How can I make the pieces that make up the body follow the head of the snake when I move it?
' Port of n6 example game.
SNAKE_MAX_LENGTH = 30*40
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
snake = dim(SNAKE_MAX_LENGTH, 2)
level = dim(40, 30)
set window "Smape", 320, 240, false, 2
set redraw off
do
snakeLength = 1
snakeHead = 0
snakeDirection = 0
speed = 10
speedCounter = speed
snake[snakeHead][0] = 20
snake[snakeHead][1] = 15
for y = 0 to 29 for x = 0 to 39 level[x][y] = 0
gameOver = false
nextPlupTime = clock()
do
if clock() >= nextPlupTime
if rnd(3) = 0 level[rnd(40)][rnd(30)] = 2
else level[rnd(40)][rnd(30)] = 1
nextPlupTime = clock() + 2000 + rnd(2000)
endif
if keydown(38) and snakeDirection <> DOWN snakeDirection = UP
if keydown(40) and snakeDirection <> UP snakeDirection = DOWN
if keydown(37) and snakeDirection <> RIGHT snakeDirection = LEFT
if keydown(39) and snakeDirection <> LEFT snakeDirection = RIGHT
speedCounter = speedCounter - 1
if speedCounter = 0
prev = snakeHead
if snakeDirection > 0 snakeHead = (snakeHead + 1)%SNAKE_MAX_LENGTH
speedCounter = speed
if snakeDirection = UP
snake[snakeHead][0] = snake[prev][0]
snake[snakeHead][1] = snake[prev][1] - 1
elseif snakeDirection = DOWN
snake[snakeHead][0] = snake[prev][0]
snake[snakeHead][1] = snake[prev][1] + 1
elseif snakeDirection = LEFT
snake[snakeHead][0] = snake[prev][0] - 1
snake[snakeHead][1] = snake[prev][1]
elseif snakeDirection = RIGHT
snake[snakeHead][0] = snake[prev][0] + 1
snake[snakeHead][1] = snake[prev][1]
endif
x = snake[snakeHead][0]
y = snake[snakeHead][1]
if x < 0 or x >= 40 or y < 0 or y >= 30
gameOver = true
else
if level[x][y] > 0
snakeLength = snakeLength + 1
if level[x][y] = 2 speed = max(speed - 1, 0)
level[x][y] = 0
endif
endif
endif
set color 0, 0, 0
cls
for y = 0 to 29
for x = 0 to 39
if level[x][y] = 1
set color 0, 255, 0
draw rect x*8, y*8, 8, 8 , true
elseif level[x][y] = 2
set color 255, 0, 0
draw rect x*8, y*8, 8, 8, true
endif
next
next
set color 255, 255, 255
set caret 0, 0
x = snake[snakeHead][0]
y = snake[snakeHead][1]
for i = snakeHead to snakeHead - snakeLength + 1
j = i
if j < 0 then j = j + SNAKE_MAX_LENGTH
draw rect snake[j][0]*8, snake[j][1]*8, 8, 8, true
if i <> snakeHead
if snake[j][0] = x and snake[j][1] = y then gameOver = true
endif
next
wait 10
redraw
until gameOver
loop
(07-20-2024, 10:46 AM)johnno56 Wrote: [ -> ]Love the "classics"...
Could not help myself... Added a few minor upgrades: Little bit of colour; Score/Hiscore; Quit level and restart (ESC); Quit game (Q). Note: No external hiscore.
Scoring has been modified. "Green" food is the normal 10 points. "Red" food is worth 100 points. Note: The speed of the snake will still increase... Moo Ha Ha Ha...
Challenge: Sound and messages?
J
(07-20-2024, 09:23 AM)Marcus Wrote: [ -> ]Here's a port of an n6 example game. Hopefully you can find some clues by looking at the code I don't remember why I implemented the game like this, and probably there are smarter ways.I'll see if I can understand the code where the body follows the head, thank you.
Code:' Port of n6 example game.
SNAKE_MAX_LENGTH = 30*40
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
snake = dim(SNAKE_MAX_LENGTH, 2)
level = dim(40, 30)
set window "Smape", 320, 240, false, 2
set redraw off
do
snakeLength = 1
snakeHead = 0
snakeDirection = 0
speed = 10
speedCounter = speed
snake[snakeHead][0] = 20
snake[snakeHead][1] = 15
for y = 0 to 29 for x = 0 to 39 level[x][y] = 0
gameOver = false
nextPlupTime = clock()
do
if clock() >= nextPlupTime
if rnd(3) = 0 level[rnd(40)][rnd(30)] = 2
else level[rnd(40)][rnd(30)] = 1
nextPlupTime = clock() + 2000 + rnd(2000)
endif
if keydown(38) and snakeDirection <> DOWN snakeDirection = UP
if keydown(40) and snakeDirection <> UP snakeDirection = DOWN
if keydown(37) and snakeDirection <> RIGHT snakeDirection = LEFT
if keydown(39) and snakeDirection <> LEFT snakeDirection = RIGHT
speedCounter = speedCounter - 1
if speedCounter = 0
prev = snakeHead
if snakeDirection > 0 snakeHead = (snakeHead + 1)%SNAKE_MAX_LENGTH
speedCounter = speed
if snakeDirection = UP
snake[snakeHead][0] = snake[prev][0]
snake[snakeHead][1] = snake[prev][1] - 1
elseif snakeDirection = DOWN
snake[snakeHead][0] = snake[prev][0]
snake[snakeHead][1] = snake[prev][1] + 1
elseif snakeDirection = LEFT
snake[snakeHead][0] = snake[prev][0] - 1
snake[snakeHead][1] = snake[prev][1]
elseif snakeDirection = RIGHT
snake[snakeHead][0] = snake[prev][0] + 1
snake[snakeHead][1] = snake[prev][1]
endif
x = snake[snakeHead][0]
y = snake[snakeHead][1]
if x < 0 or x >= 40 or y < 0 or y >= 30
gameOver = true
else
if level[x][y] > 0
snakeLength = snakeLength + 1
if level[x][y] = 2 speed = max(speed - 1, 0)
level[x][y] = 0
endif
endif
endif
set color 0, 0, 0
cls
for y = 0 to 29
for x = 0 to 39
if level[x][y] = 1
set color 0, 255, 0
draw rect x*8, y*8, 8, 8 , true
elseif level[x][y] = 2
set color 255, 0, 0
draw rect x*8, y*8, 8, 8, true
endif
next
next
set color 255, 255, 255
set caret 0, 0
x = snake[snakeHead][0]
y = snake[snakeHead][1]
for i = snakeHead to snakeHead - snakeLength + 1
j = i
if j < 0 then j = j + SNAKE_MAX_LENGTH
draw rect snake[j][0]*8, snake[j][1]*8, 8, 8, true
if i <> snakeHead
if snake[j][0] = x and snake[j][1] = y then gameOver = true
endif
next
wait 10
redraw
until gameOver
loop
(07-20-2024, 06:50 PM)aliensoldier Wrote: [ -> ](07-20-2024, 09:23 AM)Marcus Wrote: [ -> ]Here's a port of an n6 example game. Hopefully you can find some clues by looking at the code I don't remember why I implemented the game like this, and probably there are smarter ways.I'll see if I can understand the code where the body follows the head, thank you.
Code:' Port of n6 example game.
SNAKE_MAX_LENGTH = 30*40
UP = 1
DOWN = 2
LEFT = 3
RIGHT = 4
snake = dim(SNAKE_MAX_LENGTH, 2)
level = dim(40, 30)
set window "Smape", 320, 240, false, 2
set redraw off
do
snakeLength = 1
snakeHead = 0
snakeDirection = 0
speed = 10
speedCounter = speed
snake[snakeHead][0] = 20
snake[snakeHead][1] = 15
for y = 0 to 29 for x = 0 to 39 level[x][y] = 0
gameOver = false
nextPlupTime = clock()
do
if clock() >= nextPlupTime
if rnd(3) = 0 level[rnd(40)][rnd(30)] = 2
else level[rnd(40)][rnd(30)] = 1
nextPlupTime = clock() + 2000 + rnd(2000)
endif
if keydown(38) and snakeDirection <> DOWN snakeDirection = UP
if keydown(40) and snakeDirection <> UP snakeDirection = DOWN
if keydown(37) and snakeDirection <> RIGHT snakeDirection = LEFT
if keydown(39) and snakeDirection <> LEFT snakeDirection = RIGHT
speedCounter = speedCounter - 1
if speedCounter = 0
prev = snakeHead
if snakeDirection > 0 snakeHead = (snakeHead + 1)%SNAKE_MAX_LENGTH
speedCounter = speed
if snakeDirection = UP
snake[snakeHead][0] = snake[prev][0]
snake[snakeHead][1] = snake[prev][1] - 1
elseif snakeDirection = DOWN
snake[snakeHead][0] = snake[prev][0]
snake[snakeHead][1] = snake[prev][1] + 1
elseif snakeDirection = LEFT
snake[snakeHead][0] = snake[prev][0] - 1
snake[snakeHead][1] = snake[prev][1]
elseif snakeDirection = RIGHT
snake[snakeHead][0] = snake[prev][0] + 1
snake[snakeHead][1] = snake[prev][1]
endif
x = snake[snakeHead][0]
y = snake[snakeHead][1]
if x < 0 or x >= 40 or y < 0 or y >= 30
gameOver = true
else
if level[x][y] > 0
snakeLength = snakeLength + 1
if level[x][y] = 2 speed = max(speed - 1, 0)
level[x][y] = 0
endif
endif
endif
set color 0, 0, 0
cls
for y = 0 to 29
for x = 0 to 39
if level[x][y] = 1
set color 0, 255, 0
draw rect x*8, y*8, 8, 8 , true
elseif level[x][y] = 2
set color 255, 0, 0
draw rect x*8, y*8, 8, 8, true
endif
next
next
set color 255, 255, 255
set caret 0, 0
x = snake[snakeHead][0]
y = snake[snakeHead][1]
for i = snakeHead to snakeHead - snakeLength + 1
j = i
if j < 0 then j = j + SNAKE_MAX_LENGTH
draw rect snake[j][0]*8, snake[j][1]*8, 8, 8, true
if i <> snakeHead
if snake[j][0] = x and snake[j][1] = y then gameOver = true
endif
next
wait 10
redraw
until gameOver
loop
'prueba de cola de serpiente
'variables-----------------------
visible player,list_body
set window "serpiente",640,480,false
set redraw off
'player-----------------------------
function Player()
obj = []
obj.radius = 14
obj.x = 320 - obj.radius/2
obj.y = 240 - obj.radius/2
obj.speedX = 0
obj.speedY = 0
obj.Active = true
obj.update = function()
if this.Active = true
this.move()
this.Draw()
endif
endfunc
obj.Draw = function()
set color 255,204,0
draw ellipse this.x,this.y,this.radius,this.radius,true
endfunc
obj.move = function()
if keydown(KEY_RIGHT,false)
this.speedX = 2
this.speedY = 0
endif
if keydown(KEY_LEFT,false)
this.speedX = -2
this.speedY = 0
endif
if keydown(KEY_UP,false)
this.speedX = 0
this.speedY = -2
endif
if keydown(KEY_DOWN,false)
this.speedX = 0
this.speedY = 2
endif
this.x = this.x + this.speedX
this.y = this.y + this.speedY
endfunc
return obj
endfunc
'end player--------------------------
'body--------------------------------
function Body(x,y)
obj = []
obj.radius = 10
obj.x = x - obj.radius/2
obj.y = y - obj.radius/2
obj.update = function()
this.Draw()
endfunc
obj.Draw = function()
set color 255,0,204
draw ellipse this.x,this.y,this.radius,this.radius,true
endfunc
return obj
endfunc
'end body----------------------------
'iniciar variables-------------------
player = Player()
list_body = []
list_body[sizeof(list_body)] = Body(320,64)
list_body[sizeof(list_body)] = Body(320-32,64)
list_body[sizeof(list_body)] = Body(320-64,64)
while not keydown(KEY_ESCAPE,true)
set color 0,0,0
cls
'actualizar objetos del juego-----------
player.update()
if sizeof(list_body)
for i = 0 to sizeof(list_body)-1
list_body[i].update()
next
endif
'---------------------------------------
fwait 60
redraw
wend
(07-21-2024, 05:39 PM)aliensoldier Wrote: [ -> ]I don't understand the code very well, so I have prepared a small example to use as a basis for testing.
In the example I have the player who is the head of the snake and I have the body which is the tail, the player moves with the keys and the body I have added to a list and I show three bodies at the moment. How do I make the body is in the player's position and moves with him like the tail of the snake.
Code:'prueba de cola de serpiente
'variables-----------------------
visible player,list_body
set window "serpiente",640,480,false
set redraw off
'player-----------------------------
function Player()
obj = []
obj.radius = 14
obj.x = 320 - obj.radius/2
obj.y = 240 - obj.radius/2
obj.speedX = 0
obj.speedY = 0
obj.Active = true
obj.update = function()
if this.Active = true
this.move()
this.Draw()
endif
endfunc
obj.Draw = function()
set color 255,204,0
draw ellipse this.x,this.y,this.radius,this.radius,true
endfunc
obj.move = function()
if keydown(KEY_RIGHT,false)
this.speedX = 2
this.speedY = 0
endif
if keydown(KEY_LEFT,false)
this.speedX = -2
this.speedY = 0
endif
if keydown(KEY_UP,false)
this.speedX = 0
this.speedY = -2
endif
if keydown(KEY_DOWN,false)
this.speedX = 0
this.speedY = 2
endif
this.x = this.x + this.speedX
this.y = this.y + this.speedY
endfunc
return obj
endfunc
'end player--------------------------
'body--------------------------------
function Body(x,y)
obj = []
obj.radius = 10
obj.x = x - obj.radius/2
obj.y = y - obj.radius/2
obj.update = function()
this.Draw()
endfunc
obj.Draw = function()
set color 255,0,204
draw ellipse this.x,this.y,this.radius,this.radius,true
endfunc
return obj
endfunc
'end body----------------------------
'iniciar variables-------------------
player = Player()
list_body = []
list_body[sizeof(list_body)] = Body(320,64)
list_body[sizeof(list_body)] = Body(320-32,64)
list_body[sizeof(list_body)] = Body(320-64,64)
while not keydown(KEY_ESCAPE,true)
set color 0,0,0
cls
'actualizar objetos del juego-----------
player.update()
if sizeof(list_body)
for i = 0 to sizeof(list_body)-1
list_body[i].update()
next
endif
'---------------------------------------
fwait 60
redraw
wend
'prueba de cola de serpiente
' Marcus: You can remove this, just checking some things.
#dbg
'variables-----------------------
' Marcus: I think list_body should belong to player ================================================
'visible player,list_body
visible player
' ==================================================================================================
set window "serpiente",640,480,false
set redraw off
'player-----------------------------
function Player()
obj = []
obj.radius = 14
obj.x = 320 - obj.radius/2
obj.y = 240 - obj.radius/2
obj.speedX = 0
obj.speedY = 0
obj.Active = true
' Marcus: Putting body list here and a list of recorded head positions =========================
obj.bodyList = []
obj.positions = []
' ==============================================================================================
' Marcus: Add one body part to player ==========================================================
obj.AddBody = function()
body = Body(0, 0)
this.bodyList[sizeof(this.bodyList)] = body
' Not sure when AddBody might be called, so calling UpdateBody here too.
this.UpdateBody()
endfunc
' ==============================================================================================
obj.update = function()
if this.Active = true
this.move()
' Marcus: Reposition body parts ========================================================
this.UpdateBody()
' ======================================================================================
this.Draw()
endif
endfunc
' Marcus: Set body parts' positions ============================================================
obj.UpdateBody = function()
if sizeof(this.bodyList)
' We position the body parts one after one, starting with the one closest to the head.
' dist is the calculated travel distance between the head and the current body part.
' Initiate it to the radius of the head.
dist = this.radius
for i = 0 to sizeof(this.bodyList) - 1
b = this.bodyList[i]
' Add radius of body part to dist.
dist = dist + b.radius
' Divide dist by the speed of the snake to convert the traveled distance to an
' index in the position list. Use 'min' to make sure it's not outside the list.
index = min(int(dist/2), sizeof(this.positions) - 1)
if index >= 0
' Convert single number coordinates to x and y.
'b.y = int(this.positions[index]/width(primary))
'b.x = int(this.positions[index]%width(primary))
b.x = this.positions[index][0]
b.y = this.positions[index][1]
else
' No recorded positions, put at head position.
b.x = this.x
b.y = this.y
endif
' Add radius of body part to dist.
dist = dist + b.radius
next
endif
endfunc
' ==============================================================================================
obj.Draw = function()
' Marcus: Draw body parts ==================================================================
if sizeof(this.bodyList)
for i = 0 to sizeof(this.bodyList) - 1 this.bodyList[i].Draw()
endif
' ==========================================================================================
set color 255,204,0
draw ellipse this.x,this.y,this.radius,this.radius,true
endfunc
obj.move = function()
if keydown(KEY_RIGHT,false)
this.speedX = 2
this.speedY = 0
endif
if keydown(KEY_LEFT,false)
this.speedX = -2
this.speedY = 0
endif
if keydown(KEY_UP,false)
this.speedX = 0
this.speedY = -2
endif
if keydown(KEY_DOWN,false)
this.speedX = 0
this.speedY = 2
endif
' Marcus: Save previous position ===========================================================
if this.speedX or this.speedY
' I store the position, x and y, as a single number.
'insert this.positions, 0, this.y*width(primary) + this.x
insert this.positions, 0, [this.x, this.y]
' Store only 5000 positions for now, might have to increase it depending on how long
' the snake can become.
if sizeof(this.positions) > 5000 free key this.positions, 5000
endif
' ==========================================================================================
this.x = this.x + this.speedX
this.y = this.y + this.speedY
endfunc
return obj
endfunc
'end player--------------------------
'body--------------------------------
function Body(x,y)
obj = []
obj.radius = 10
obj.x = x - obj.radius/2
obj.y = y - obj.radius/2
obj.update = function()
this.Draw()
endfunc
obj.Draw = function()
set color 255,0,204
draw ellipse this.x,this.y,this.radius,this.radius,true
endfunc
return obj
endfunc
'end body----------------------------
'iniciar variables-------------------
player = Player()
list_body = []
list_body[sizeof(list_body)] = Body(320,64)
list_body[sizeof(list_body)] = Body(320-32,64)
list_body[sizeof(list_body)] = Body(320-64,64)
while not keydown(KEY_ESCAPE,true)
set color 0,0,0
cls
if keydown(KEY_SPACE, true) player.AddBody()
'actualizar objetos del juego-----------
player.update()
if sizeof(list_body)
for i = 0 to sizeof(list_body)-1
list_body[i].update()
next
endif
'---------------------------------------
' Marcus: Some debug output ====================================================================
set color 255, 255, 255
set caret 0, 0
wln "PRESS SPACE BAR TO ADD BODY PARTS"
wln
wln "bodyparts: " + sizeof(player.bodyList)
wln "recorded positions: " + sizeof(player.positions)
' ==============================================================================================
fwait 60
redraw
wend
(08-08-2024, 02:56 PM)Marcus Wrote: [ -> ]Code:'prueba de cola de serpiente
' Marcus: You can remove this, just checking some things.
#dbg
'variables-----------------------
' Marcus: I think list_body should belong to player ================================================
'visible player,list_body
visible player
' ==================================================================================================
set window "serpiente",640,480,false
set redraw off
'player-----------------------------
function Player()
obj = []
obj.radius = 14
obj.x = 320 - obj.radius/2
obj.y = 240 - obj.radius/2
obj.speedX = 0
obj.speedY = 0
obj.Active = true
' Marcus: Putting body list here and a list of recorded head positions =========================
obj.bodyList = []
obj.positions = []
' ==============================================================================================
' Marcus: Add one body part to player ==========================================================
obj.AddBody = function()
body = Body(0, 0)
this.bodyList[sizeof(this.bodyList)] = body
' Not sure when AddBody might be called, so calling UpdateBody here too.
this.UpdateBody()
endfunc
' ==============================================================================================
obj.update = function()
if this.Active = true
this.move()
' Marcus: Reposition body parts ========================================================
this.UpdateBody()
' ======================================================================================
this.Draw()
endif
endfunc
' Marcus: Set body parts' positions ============================================================
obj.UpdateBody = function()
if sizeof(this.bodyList)
' We position the body parts one after one, starting with the one closest to the head.
' dist is the calculated travel distance between the head and the current body part.
' Initiate it to the radius of the head.
dist = this.radius
for i = 0 to sizeof(this.bodyList) - 1
b = this.bodyList[i]
' Add radius of body part to dist.
dist = dist + b.radius
' Divide dist by the speed of the snake to convert the traveled distance to an
' index in the position list. Use 'min' to make sure it's not outside the list.
index = min(int(dist/2), sizeof(this.positions) - 1)
if index >= 0
' Convert single number coordinates to x and y.
'b.y = int(this.positions[index]/width(primary))
'b.x = int(this.positions[index]%width(primary))
b.x = this.positions[index][0]
b.y = this.positions[index][1]
else
' No recorded positions, put at head position.
b.x = this.x
b.y = this.y
endif
' Add radius of body part to dist.
dist = dist + b.radius
next
endif
endfunc
' ==============================================================================================
obj.Draw = function()
' Marcus: Draw body parts ==================================================================
if sizeof(this.bodyList)
for i = 0 to sizeof(this.bodyList) - 1 this.bodyList[i].Draw()
endif
' ==========================================================================================
set color 255,204,0
draw ellipse this.x,this.y,this.radius,this.radius,true
endfunc
obj.move = function()
if keydown(KEY_RIGHT,false)
this.speedX = 2
this.speedY = 0
endif
if keydown(KEY_LEFT,false)
this.speedX = -2
this.speedY = 0
endif
if keydown(KEY_UP,false)
this.speedX = 0
this.speedY = -2
endif
if keydown(KEY_DOWN,false)
this.speedX = 0
this.speedY = 2
endif
' Marcus: Save previous position ===========================================================
if this.speedX or this.speedY
' I store the position, x and y, as a single number.
'insert this.positions, 0, this.y*width(primary) + this.x
insert this.positions, 0, [this.x, this.y]
' Store only 5000 positions for now, might have to increase it depending on how long
' the snake can become.
if sizeof(this.positions) > 5000 free key this.positions, 5000
endif
' ==========================================================================================
this.x = this.x + this.speedX
this.y = this.y + this.speedY
endfunc
return obj
endfunc
'end player--------------------------
'body--------------------------------
function Body(x,y)
obj = []
obj.radius = 10
obj.x = x - obj.radius/2
obj.y = y - obj.radius/2
obj.update = function()
this.Draw()
endfunc
obj.Draw = function()
set color 255,0,204
draw ellipse this.x,this.y,this.radius,this.radius,true
endfunc
return obj
endfunc
'end body----------------------------
'iniciar variables-------------------
player = Player()
list_body = []
list_body[sizeof(list_body)] = Body(320,64)
list_body[sizeof(list_body)] = Body(320-32,64)
list_body[sizeof(list_body)] = Body(320-64,64)
while not keydown(KEY_ESCAPE,true)
set color 0,0,0
cls
if keydown(KEY_SPACE, true) player.AddBody()
'actualizar objetos del juego-----------
player.update()
if sizeof(list_body)
for i = 0 to sizeof(list_body)-1
list_body[i].update()
next
endif
'---------------------------------------
' Marcus: Some debug output ====================================================================
set color 255, 255, 255
set caret 0, 0
wln "PRESS SPACE BAR TO ADD BODY PARTS"
wln
wln "bodyparts: " + sizeof(player.bodyList)
wln "recorded positions: " + sizeof(player.positions)
' ==============================================================================================
fwait 60
redraw
wend
I've marked my changes with "Marcus"
This solution is probably not very good. The head's position is always recorded into a list, player.positions. Currently I store a maximum of 5000 positions, which covers a snake length of 10,000 pixels (because the speed of the snake is 2). Then I "simply" position the body parts, using the list of recorded positions, in player.UpdateBody.
Press space bar to add body parts.
Edit But as with the other snake code I posted, this is NOT a very smart solution. It should be enough to store the positions where the player turns, and then use the distance between the head and these points to calculate the positions of the body parts.