Weird Snake behavior

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
vSxo.lua
Prole
Posts: 5
Joined: Fri Jun 08, 2018 3:08 pm
Location: Poland

Weird Snake behavior

Post by vSxo.lua »

Hi, its me again. This time I have some problems with game logic. Its a snake-type game, when you move, you paint floor with blue-ish color.
The problem is that when snake direction is "top" or "left" everything is ok, painting is smooth. But when snake is facing "right" or "down" firstly head of the snake moves to desired destination and only then floor is painted. It causes some breaks in snake body. I do not know how to fix it. I tried many solutions but they were no right solutions at all.

Function that is responsible for this logic is Game:update(dt). snake.realX and snake.realY are real pixel coordinates. snake.newX and snake.newY are grid based coordinates. snake.x and snake.y are coordinates from last snake position. Im checking if snake[1].newX or snake[1].newY is different from old coordinates. If it is Im creating snake object at old coordinates and setting old coordinates to new coordinates. But unfortunately it is not working. And I dont see where is the logic error.

If you can tell me how to make floor painting smooth in every direction I would very appreciate it. :)

main.lua

Code: Select all

Object = require "classic"
require "game"

WINDOW_WIDTH = 960
WINDOW_HEIGHT = 640

function love.load()
	-- window
	love.window.setMode(WINDOW_WIDTH, WINDOW_HEIGHT, {vsync = true, resizable = false, fullscreen = false})
	love.window.setTitle("Snake Expansion")
	love.graphics.setDefaultFilter('nearest', 'nearest')
	
	-- sounds 
	winSound = love.audio.newSource("win.wav", "static")
	menuSound = love.audio.newSource("menu.wav", "static")
	moveSound = love.audio.newSource("move.wav", "static")
	itemSound = love.audio.newSource("item.wav", "static")
	collisionSound = love.audio.newSource("collision.wav", "static")
	
	-- graphics
	background = love.graphics.newImage("background.png")
	ring = love.graphics.newImage("ring.png")
	smallFont = love.graphics.newFont("liquid.ttf", 30)
	normalFont = love.graphics.newFont("liquid.ttf", 40)
	bigFont = love.graphics.newFont("liquid.ttf", 50)
	wallTile = love.graphics.newImage("wall.png")
	floorTile = love.graphics.newImage("floor.png")
	
	-- varibles
	angle = 0
	gameState = "start"
	titleVisible = 0
end

function love.update(dt)
	angle = angle + (2 * dt)
	if gameState == "start" then
		titleVisible = math.min(255, titleVisible + (100 * dt))
	end
	if titleVisible >= 255 and gameState == "start" then
		gameState = "menu"
	end
	if gameState == "play" then
		game:update(dt)
	end
	
end

function love.mousepressed(mouseX, mouseY, button, isTouch)
	if button == 1 and gameState == "menu" then
		if mouseX > WINDOW_WIDTH/2 - 100 and mouseX < WINDOW_WIDTH/2 + 100 and mouseY >  WINDOW_HEIGHT/2 and mouseY < WINDOW_HEIGHT/2 + 50 then
			print("Play clicked")
			game = Game(1)
			gameState = "play"
		end
		if mouseX > WINDOW_WIDTH/2 - 100 and mouseX < WINDOW_WIDTH/2 + 100 and mouseY >  WINDOW_HEIGHT/2 + 100 and mouseY < WINDOW_HEIGHT/2 + 150 then
			print("Options clicked")
			gameState = "options"
		end
		if mouseX > WINDOW_WIDTH/2 - 100 and mouseX < WINDOW_WIDTH/2 + 100 and mouseY >  WINDOW_HEIGHT/2 + 200 and mouseY < WINDOW_HEIGHT/2 + 250 then
			print("Exit clicked")
			love.event.quit()
		end
	end
end

function love.draw()
	love.graphics.setColor(255,255,255,255)
	love.graphics.draw(background, 0, 0)
	love.graphics.setColor(255,255,255,100)
	love.graphics.setBlendMode("add")
	love.graphics.draw(ring, WINDOW_WIDTH/2, WINDOW_HEIGHT/2, angle, 1, 1, 100, 100)
	love.graphics.setBlendMode("alpha")
	if gameState == "start" or gameState == "menu" then
		love.graphics.setFont(bigFont)
		love.graphics.setColor(255,0,255,titleVisible)
		love.graphics.printf("Snake Expansion", 0, WINDOW_HEIGHT/4, WINDOW_WIDTH, "center")
		love.graphics.setFont(normalFont)
		love.graphics.printf("Play", 0, WINDOW_HEIGHT/2, WINDOW_WIDTH, "center")
		love.graphics.printf("Options", 0, WINDOW_HEIGHT/2 + 100, WINDOW_WIDTH, "center")
		love.graphics.printf("Exit", 0, WINDOW_HEIGHT/2 + 200, WINDOW_WIDTH, "center")
	end
	love.graphics.setColor(255, 255, 255, 255)
	if gameState == "play" then
		game:draw()
	end
end
snake.lua

Code: Select all

Snake = Object:extend()

segmentsCount = 0
dir = 2

function Snake:new(x, y, speed)
	self.speed = speed or 0
	segmentsCount = segmentsCount + 1
	self.x = x
	self.y = y
	self.width = 32
	self.height = 32
	self.realX = self.x * 32
	self.realY = self.y * 32
end

function Snake:collision(object) 
	if self.x > object.x and self.x + self.width < object.x and self.y > object.y and self.y + self.height < object.y then
		return true
	else
		return false
	end
end

function Snake:update(dt)
	if self.speed ~= 0 then	
		if dir == 1 then
			self.realY = self.realY - self.speed * dt
		elseif dir == 2 then
			self.realX = self.realX + self.speed * dt
		elseif dir == 3 then
			self.realY = self.realY + self.speed * dt
		elseif dir == 4 then
			self.realX = self.realX - self.speed * dt
		end
	end
end

function Snake:draw()
	love.graphics.rectangle("fill", self.realX, self.realY, self.width, self.height)
end
game.lua

Code: Select all

require "snake"
require "tile"

Game = Object:extend()

map = {}
objects = {}
snake = {}
direction = 2
local canMove = true

function Game:new(level)
	self.level = level
	snake = {}
	self.loadMap(self)
	self.visible = 0
end

function Game:loadMap()
	-- loading level from file
	for line in love.filesystem.lines("level" .. tostring(self.level) .. ".lst")	do 
		table.insert(map, line)
	end
	
	-- instantiate objects on map
	for index, str in ipairs(map) do
		for i = 1, #str do
			local letter = str:sub(i, i)
			if letter == "X" then
				table.insert(objects, Tile(i, index, "wall"))
			elseif letter == "S" then
				table.insert(objects, Tile(i, index, "floor"))
				snake[1] = Snake(i, index, 10)
			elseif letter == "_" then 
				table.insert(objects, Tile(i, index, "floor"))
			end
		end
	end
end

function Game:update(dt)
	self.visible = math.min(255, self.visible + 100 * dt)
	if canMove == false and math.floor(snake[1].realX) % 32 == 0 and math.floor(snake[1].realY) % 32 == 0 then
		dir = direction
		canMove = true
	end
	snake[1]:update(dt)
	snake[1].newX = math.floor(math.floor(snake[1].realX)/32)
	snake[1].newY = math.floor(math.floor(snake[1].realY)/32)
	if snake[1].newX ~= snake[1].x or snake[1].newY ~= snake[1].y then
		table.insert(snake, 2, Snake(snake[1].x, snake[1].y))
		snake[1].x = snake[1].newX
		snake[1].y = snake[1].newY
	end
end

function love.keypressed(key)
	-- adding change direction to the queqe
	if key == "left" and canMove == true then
		if direction == 1 then 
			direction = 4
		else
			direction = direction - 1
		end
		canMove = false
	elseif key == "right" and canMove == true then
		if direction == 4 then
			direction = 1
		else
			direction = direction + 1
		end
		canMove = false
	end
end

function Game:draw()
	love.graphics.setColor(255,255,255,255)
	for i, v in ipairs(objects) do
		v:draw()
	end
	colorChange = 255/#snake * 0.5
	for i = #snake, 1, -1 do
		love.graphics.setColor(colorChange * (i - 1), colorChange * (i - 1), 255, 255)
		snake[i]:draw()
	end
	love.graphics.setColor(255,0,255,self.visible)
	love.graphics.setFont(normalFont)
	love.graphics.printf("Level: " .. self.level, 0, 50, WINDOW_WIDTH, "center")
	love.graphics.setColor(255,255,255,255)
	--print("Snake newx: " .. snake[1].newX)
	--print("Snake newy: " .. snake[1].newY)
	--print("Snake x: " .. snake[1].x)
	--print("Snake y: " .. snake[1].y)
end
tile.lua (probably not needed here, but whatever)

Code: Select all

Tile = Object:extend()

function Tile:new(x, y, typeof)
	self.x = x
	self.y = y
	self.tileType = typeof
end

function Tile:update(dt)

end

function Tile:draw()
	if self.tileType == "wall" then
		love.graphics.draw(wallTile, self.x * 32, self.y * 32)
	elseif self.tileType == "floor" then
		love.graphics.draw(floorTile, self.x * 32, self.y * 32)
	end
end
Project is using classic.lua library and it is attached to the post. Thank you in advance :>
Attachments
SnakeExpansion.love
(961.53 KiB) Downloaded 123 times
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Weird Snake behavior

Post by pgimeno »

The reason is pretty clear.

You're using the top left corner of the head as your reference position. That's part of your problem.

While you are, say, half way east, the floor of dividing your X coordinate by 32 is still at the square you're leaving, because your top left corner hasn't left that square yet. Only when your top left corner leaves that square, is when you actually paint it.

It works when you move west, because your top left corner starts at the left edge of the current square, and as soon as you move, it crosses to the next square, which causes the square you're leaving to be painted immediately as you begin to move, and since the head is almost entirely at that square, it partially covers the tail and everything looks fine.

Similarly for the up/down direction, of course.

As for solutions... well, that's more difficult. The easiest solution I can think of is flooring when you're going left or up, and ceiling when you're going right or down. By the way, you don't need the double floor, you can just floor after dividing by 32. When drawing, you should always floor though.

Disclaimer: I have not tested this solution. I can think of a couple other solutions: one is adding a "padding square" specifically for those directions; another one is using the centre of the square as reference, so that you paint always where the head stops.
User avatar
vSxo.lua
Prole
Posts: 5
Joined: Fri Jun 08, 2018 3:08 pm
Location: Poland

Re: Weird Snake behavior

Post by vSxo.lua »

Ceiling is not working :<

I tried to add this padding square but it didnt work out,
How to implement this centre of the square as reference?

Edit:
Okay i have one idea with this centre of the square. I will report if this will succeed.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Weird Snake behavior

Post by pgimeno »

I've tried the ceiling. It works if you update before setting canMove.

Before:

Code: Select all

	if canMove == false and math.floor(snake[1].realX) % 32 == 0 and math.floor(snake[1].realY) % 32 == 0 then
		dir = direction
		canMove = true
	end
	snake[1]:update(dt)
	snake[1].newX = math.floor(math.floor(snake[1].realX)/32)
	snake[1].newY = math.floor(math.floor(snake[1].realY)/32)
After:

Code: Select all

	snake[1]:update(dt)
	if canMove == false and math.floor(snake[1].realX) % 32 == 0 and math.floor(snake[1].realY) % 32 == 0 then
		dir = direction
		canMove = true
	end
	snake[1].newX = dir == 2 and math.ceil(snake[1].realX/32) or math.floor(snake[1].realX/32)
	snake[1].newY = dir == 3 and math.ceil(snake[1].realY/32) or math.floor(snake[1].realY/32)
User avatar
vSxo.lua
Prole
Posts: 5
Joined: Fri Jun 08, 2018 3:08 pm
Location: Poland

Re: Weird Snake behavior

Post by vSxo.lua »

Yep, its working :> Thanks.
Post Reply

Who is online

Users browsing this forum: No registered users and 43 guests