How to start a snake game?

Blitz3D Forums/Blitz3D Beginners Area/How to start a snake game?

RXArt(Posted 2004) [#1]
I've been thinking of this for quite some time (3 hours), and I can't seem to understand how it work. I've managed to come up with an algorithm that only allows logical movements(you can only turn left or right from your current direction). Here're what I need help with:

1) how to keep generating blocks inside the game loop that allows the snake to swallow?
2) how to add the block to the end of the snake, and make it follow it when it moves?

These are my current problem. I believe this would be pretty useful, as it seems pretty similiar to creating 2d or 3d trails. :)

Please help, and thanks in advance!


semar(Posted 2004) [#2]
1) what do you mean with swallow ?

2) Basically, you can think at your snake like a collection of type elements.

Start for example with three elements, which represents an head, the body, and the tail.

Each element should have a direction field, and when the snake walks, each element should take the new direction from the next one, while the first one (the head), changes its direction with the player control.

;snake structure example
type t_snake
   field direction ;an integer indicating the direction
   field x ;x pos
   field y ;y pos
end type


In other words, suppose the snake goes up. At this moment, the head, the body and the tail have the same direction.

Now you turn left; the head direction changes, so in the next loop:
- the tail will move towards its direction, and takes the body's position and direction

- the body will move toward its direction, and takes the head's position and direction

- the head will move toward its direction, and takes the new direction from the player

And so on. When you want to make the snake longer, just add a new element to the list, and manage its movement as well.

This is a very rough example, but should put you in the right path; plus, if you try this on paper, drawing some simple square with an arrow which indicates the direction, you will get what I mean..

Sergio.


darklordz(Posted 2004) [#3]
A default game frameworks is a sutch
Graphics 800,600,32,2
SetBuffer BackBuffer()

While Not KeyHit(1)

	Flip

Wend
End


inside you could generate a "meal" by using X=Rnd(10,600) and Y=Rnd(10,400) and then drawing a rect X,Y,10,10

If SERVED = 0
		Z=Rnd(GraphicsWidth()-10)
		C=Rnd(GraphicsHeight()-10)
		SERVED = 1
	EndIf
	
	Rect Z,C,10,10


thats how you generate meals.

Now On to player movement.

first you determine how the snake moves....ther are 4 directions so we assign 4 keys to set 4 directions

If KeyHit(208)
		DIR=1
	ElseIf KeyHit(200)
		DIR=2
	ElseIf KeyHit(205)
		DIR=3
	ElseIf KeyHit(203)
		DIR=4
	EndIf
	
	If DIR=1
		X=X+0
		Y=Y+5
		Rect X,Y,10,10
	ElseIf DIR=2
		X=X+0
		Y=Y-5
		Rect X,Y,10,10
	ElseIf DIR=3
		X=X+5
		Y=Y+0
		Rect X,Y,10,10
	ElseIf DIR=4
		X=X-5
		Y=Y+0
		Rect X,Y,10,10
	EndIf


now we set up collisions & score

If RectsOverlap(X,Y,10,10,Z,C,10,10)
		SERVED=0
		SCORE = SCORE + 10
	EndIf
	
	Text 20,20,SCORE



My Samples entire code :

Graphics 800,600,32,2
SetBuffer BackBuffer()

LENG = 10
DIR  = 1

While Not KeyHit(1)
	Cls
	
	If SERVED = 0
		Z=Rnd(GraphicsWidth()-10)
		C=Rnd(GraphicsHeight()-10)
		SERVED = 1
	EndIf
	
	Rect Z,C,10,10
	
	If KeyHit(208)
		DIR=1
	ElseIf KeyHit(200)
		DIR=2
	ElseIf KeyHit(205)
		DIR=3
	ElseIf KeyHit(203)
		DIR=4
	EndIf
	
	If DIR=1
		X=X+0
		Y=Y+5
		Rect X,Y,10,10
	ElseIf DIR=2
		X=X+0
		Y=Y-5
		Rect X,Y,10,10
	ElseIf DIR=3
		X=X+5
		Y=Y+0
		Rect X,Y,10,10
	ElseIf DIR=4
		X=X-5
		Y=Y+0
		Rect X,Y,10,10
	EndIf
	
	If RectsOverlap(X,Y,10,10,Z,C,10,10)
		SERVED=0
		SCORE = SCORE + 10
	EndIf
	
	Text 20,20,SCORE
	Flip

Wend
End



RXArt(Posted 2004) [#4]
thanks for the reply, but I still have some problem implementing it. Right now, I need help on cloning more and more "body blocks" as the game progresses. I only know the copyimage command. Is there a way I can use an array instead, to keep track of my blocks? or do I have to do something like this:

type tSnake
field direction
field xpos
field ypos
field imgName
end type

? Please help. Thanks :)


darklordz(Posted 2004) [#5]
First when i started writing my sample i tough types was teh way togo but tehn i tough wait he's a beginner why make things harder....... just try my last piece of code. the only thing i didn't implement was making the snake longer. ther are hints tough, have fun!


semar(Posted 2004) [#6]
@darklordz,
yes could be harder, but in the long term would be also a good occasion to learn types.

If you have different images for the snake, for example one for the head, one for the body, and one for the tail, just store it in an array of images, and then set the index in each snake element, when you create it:
dim arr_img(3) ;array of images
arr_img(1) = loadimage("snake_head.png")
arr_img(2) = loadimage("snake_body.png")
arr_img(3) = loadimage("snake_tail.png")

you can also use three constants to make a more readable code:
const s_head = 1
const s_body = 2
const s_tail = 3

for n = 1 to 3
snake.t_snake = new t_snake
snake\img = n
next

when you need to make the snake longer, you have to change the tail into a body, and add a new tail.
So you have to go to the last element in your snake collection:
snake.t_snake = last snake

and change the image to a body one
snake\img = s_body

then add a new tail
snake.t_snake = new t_snake ;this should be the tail, because will be added at the end
snake\img = s_tail


Hope this helps,
Sergio.


RXArt(Posted 2004) [#7]
Thanks for all the advices!! Here's what I have came up with, based on darklordz's suggestion. I'll try implementing yours in a moment, sergio. I'll let you know how it goes.

Graphics 640,480,16,2

Global snakespeed = 5
Global foodx = Rnd(640)
Global foody = Rnd(480)
Global score = 0

Type snakeinfo
	Field xpos
	Field ypos
	Field direction ;1=up,2=right,3=down,4=right
End Type

snake.snakeinfo = New snakeinfo

snake\xpos = 320
snake\ypos = 240
snake\direction = 1

block = CreateImage(10,10)
SetBuffer ImageBuffer(block)
	Rect 0,0,10,10,1
food = CopyImage(block)

SetBuffer BackBuffer()

While Not KeyHit(1)
	Cls
	
	Text 0,0,score
	Select snake\direction
	Case 1 
		snake\ypos = snake\ypos - snakespeed
	Case 2	
		snake\xpos = snake\xpos + snakespeed
	Case 3
		snake\ypos = snake\ypos + snakespeed
	Case 4
		snake\xpos = snake\xpos - snakespeed
	End Select
	
	DrawImage block, snake\xpos, snake\ypos
	DrawImage food, foodx, foody
	
	If ImagesCollide(block,snake\xpos,snake\ypos,0,food,foodx,foody,0) Then
		score = score + 10
		foodx = Rnd(640)
		foody = Rnd(480)
	EndIf
	
	If KeyHit(200) Then
		If snake\direction = 2 Or snake\direction = 4 Then snake\direction = 1
	EndIf
	
	If KeyHit(203) Then
		If snake\direction = 1 Or snake\direction = 3 Then snake\direction = 4
	EndIf

	If KeyHit(205) Then
		If snake\direction = 1 Or snake\direction = 3 Then snake\direction = 2
	EndIf

	If KeyHit(208) Then
		If snake\direction = 2 Or snake\direction = 4 Then snake\direction = 3
	EndIf
	
		
	Flip	
Wend	




WolRon(Posted 2004) [#8]
Type tSnake 
  Field xpos 
  Field ypos 
  Field snaketype
End Type

Const head = 1
Const body = 2
Const tail = 3

Snake.tSnake = New tSnake
Snake\xpos = 10
Snake\ypos = 10
Snake\snaketype = head

Snake.tSnake = New tSnake
Snake\xpos = 10
Snake\ypos = 11
Snake\snaketype = body

Snake.tSnake = New tSnake
Snake\xpos = 10
Snake\ypos = 12
Snake\snaketype = tail

Dim body(3)
body(1) = LoadImage "Head.bmp"
body(2) = LoadImage "Body.bmp"
body(3) = LoadImage "Tail.bmp"

Graphics 640, 480, 0, 2
SetBuffer BackBuffer()

While Not KeyHit(1)
  
  ;input section
  ;blah, blah, blah
  newXpos = ??
  newYpos = ??
  
  ;logic section
  lastXpos = newXpos
  lastYpos = newYpos
  For Snake.tSnake = Each tSnake
    tempXpos = Snake\xpos
    tempYpos = Snake\ypos
    Snake\xpos = lastXpos
    Snake\ypos = lastYpos
    lastXpos = tempXpos
    lastYpos = tempYpos
  Next
  If FoodEaten = TRUE
    Snake.tSnake = Last tSnake
    Snake\snaketype = body
    Snake.tSnake = New tSnake
    Snake\xpos = lastXpos
    Snake\ypos = lastYpos
    Snake\snaketype = tail
  EndIf

  ;drawing section
  CLS
  For Snake.tSnake = each tSnake
    DrawImage body(Snake\snaketype), Snake\xpos * 32, Snake\ypos * 32
  Next

  Flip

Wend
End
I just realized that my code will always draw the images the same way regardless of the direction of the next body part. I'm sure you can figure out how to make that work though.


RXArt(Posted 2004) [#9]
Help again... All of a sudden, I just can't seem to think about it logically anymore. I can't quite understand your code there, wolron. Here's what I have:

Graphics 640,480,16,2

Type tSnake
	Field xPos
	Field yPos
	Field SnakeType
	Field Direction
End Type

Const head = 1
Const body = 2
Const tail = 3

Const up	= 1
Const rgt	= 2
Const dwn	= 3
Const lft	= 4

Snake.tSnake = New tSnake
	Snake\xPos 		= 320
	Snake\yPos 		= 240
	Snake\SnakeType	= Head
	Snake\Direction	= up
	
Snake.tSnake = New tSnake
	Snake\xPos 		= 320
	Snake\yPos 		= 241
	Snake\SnakeType	= Body
	Snake\Direction	= up
		
Snake.tSnake = New tSnake
	Snake\xPos 		= 320
	Snake\yPos 		= 242
	Snake\SnakeType	= Tail
	Snake\Direction	= up
	
Dim SnakeBody(3)

Block = CreateImage(24,24)
SetBuffer ImageBuffer(Block)
	Oval 0,0,24,24
For count = 1 To 3
	SnakeBody(count) = CopyImage(Block)
Next

SetBuffer BackBuffer()

While Not KeyHit(1)
	Cls
	
	
	If KeyHit(200) Then
		If snake\direction = lft Or snake\direction = rgt Then snake\direction = up
	EndIf
	
	If KeyHit(203) Then
		If snake\direction = up Or snake\direction = dwn Then snake\direction = lft		
	EndIf
	
	If KeyHit(205) Then
		If snake\direction = up Or snake\direction = dwn Then snake\direction = rgt		
	EndIf
	
	If KeyHit(208) Then
		If snake\direction = lft Or snake\direction = rgt Then snake\direction = dwn		
	EndIf
	For Snake.tSnake = Each tSnake
		If snake\direction
	Next
	
		
	Flip
Wend


As you can see, once inside the game loop, my codes are totally messed up. I'm really having a hard time with types. Here're some questions:

1) snake.tSnake = New tSnake 4 times? As you can see from my previous codes, I only change the direction of the "head", and the rest of the body parts are suppose to follow. But how do I access the "Head" record, and set it's x and y values?

2) None yet. I'm so frustrated, I can't put it in words. Perhaps I'll remember em later.

Well, help again, I suppose. And thanks to all of you for your patience and help!


RXArt(Posted 2004) [#10]
I've got it working at last, after blindly tweaking and modifying wolron's code. I really have no idea how and what made it work, but I'm going to find out first thing tomorrow morning. Here's the code:

Graphics 640,480,16,2

Type tSnake
	Field xPos
	Field yPos
	Field SnakeType
End Type

Global foodx = Rnd(640)
Global foody = Rnd(480)

Const head = 1
Const body = 2
Const tail = 3

Const up	= 1
Const rgt	= 2
Const dwn	= 3
Const lft	= 4

Global direction = up

Snake.tSnake = New tSnake
	Snake\xPos 		= 320
	Snake\yPos 		= 160
	Snake\SnakeType	= Head
	direction	= up
	
Snake.tSnake = New tSnake
	Snake\xPos 		= 320
	Snake\yPos 		= 264
	Snake\SnakeType	= Body
	direction	= up
		
Snake.tSnake = New tSnake
	Snake\xPos 		= 320
	Snake\yPos 		= 288
	Snake\SnakeType	= Tail
	direction	= up
	
Dim SnakeBody(3)

Block = CreateImage(16,16)
SetBuffer ImageBuffer(Block)
	Oval 0,0,16,16
For count = 1 To 3
	SnakeBody(count) = CopyImage(Block)
Next

food = CopyImage(block)

SetBuffer BackBuffer()

While Not KeyHit(1)
	Cls
	
	DrawImage food,foodx, foody
	
	

	If KeyDown(200) Then direction = up
	If KeyDown(203) Then direction = lft
	If KeyDown(205) Then direction = rgt
	If KeyDown(208) Then direction = dwn
	
	If direction = up Then
		snake.tSnake = First tsnake
			newXpos = snake\xPos
			newYpos = snake\yPos - 3
	EndIf
	
	If direction = dwn Then
		snake.tSnake = First tsnake
			newXpos = snake\xPos
			newYpos = snake\yPos +3
	EndIf
	
	If direction = lft Then
		snake.tSnake = First tsnake
			newXpos = snake\xPos -3
			newYpos = snake\yPos 
	EndIf
	
	If direction = rgt Then
		snake.tSnake = First tsnake
			newXpos = snake\xPos +3
			newYpos = snake\yPos 
	EndIf
	
	lastXpos = newXpos
	lastYpos = newYpos

  For Snake.tSnake = Each tSnake
    tempXpos = Snake\xpos
    tempYpos = Snake\ypos
    Snake\xpos = lastXpos
    Snake\ypos = lastYpos
    lastXpos = tempXpos
    lastYpos = tempYpos
  Next

	If ImagesCollide(snakebody(1),newxpos, newypos,0,food,foodx,foody,0) Then
		foodx = Rnd(620)
		foody = Rnd(460)
	    Snake.tSnake = Last tSnake
	    Snake\snaketype = body
		Snake.tSnake = New tSnake
		Snake\xpos = lastXpos
	    Snake\ypos = lastYpos
	    Snake\snaketype = tail
	EndIf
	

	For Snake.tSnake = Each tSnake
    	DrawImage snakebody(Snake\snaketype), Snake\xpos +16, Snake\ypos +16
	Next

		
	Flip
Wend


If you test this out, you'll realise the following glitches:
1) I want the parts of the snake to follow one another, not overlap each other slightly.
2) I want the snake to move within a grid, say 24x24 or 32x32. But the snake in the code moves/turns accordingly to my controls
3) there're some problems when the snake collides with the food. Not very accurate.

Anyway, thanks guys! I'll still be working on this one, until I've fully understand every command in it. And I hope that you'll all continue to support and help me. :) cheers


WolRon(Posted 2004) [#11]
2) I want the snake to move within a grid, say 24x24 or 32x32. But the snake in the code moves/turns accordingly to my controls

The code I wrote does make it follow a grid. You changed the drawing code from:
DrawImage body(Snake\snaketype), Snake\xpos * 32, Snake\ypos * 32
to
DrawImage snakebody(Snake\snaketype), Snake\xpos +16, Snake\ypos +16
Notice that I was mulitplying the x & y values by 32. This was to make the snake align to a 20x15 grid of 32x32 squares. The snake moves in exact increments (gridsquare1 to gridsquare2 to ...). In your code you are making the snake move by 3 PIXELS each time. Change them to 1's (so that your snake moves 1 GRIDSQUARE at a time) and change your snake drawing line to match mine.
You will most likely have to add a timer to slow the snake down, otherwise it will travel at the refresh rate of your monitor (60 Hz or more).

If you prefer it not to move in this fashion (incrementally from gridsquare to gridsquare) but more like how PacMan moves (smoothly from intersection to intersection) then you would have to change a lot. You have to do things like storing the last direction pressed by the player but not actually changing direction until you reach the next gridsquare. This can be done, but it's best to try doing it the way I showed above first.


RXArt(Posted 2004) [#12]
thanks for the info wolron. I's just so desperate to get it working last night, I tried changing everything. I'll try to correct it. Just one thing I do not understand, why 10,10 10,11 10,12 for head body and tail? shouldn't they be 32(ooer, 16) pixels apart? Just need to understand that bit.

Thanks!


WolRon(Posted 2004) [#13]
Those were just some random numbers I threw in there to start at. It's not 10 pixels, it's 10 gridsquares, which at 32 pixels per square would be 320 pixels, or center of the screen.

So "11" is 32 pixels below "10" and "12" is 32 pixels below "11". Get it?

You certainly could change it to 16 pixels per gridsquare if you wanted to, or even 8. You decide what you want. 32 pixels is a bit big isn't it?


Newbunkle(Posted 2004) [#14]
I'm new to Blitz, but I remember making something like this on AMOS Basic in the olden days! A snake game is a good project.

Just a though though... when the snake changes direction, won't you need 'corner pieces' for the body to make it look as if it's turning? I haven't seen your images, but if they're the way I imagine them you'll need a body segment that doesn't always lie horizontal or vertical. Am I over-complicating this now? Sorry!


RXArt(Posted 2004) [#15]
hi again everyone. I'm halting the snake game project at the momment to complete my pong game. I thought that it would be silly to just create a game halfway and leave it. Therefore, I'll be adding some finishing touches to it. I've customized my pong game to feature levels of difficulties as well as support skins/themes. Although I don't really expect it to be commercial, I's hoping that it'd look and feel like a commercial game.

Cheers everyone! I'd start working on the snake game again once I'm done with the pong thingy! Thank you all for your help