[Solved][Includes Example] Checking collisions and jumping around

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
nikneym
Citizen
Posts: 56
Joined: Sat Mar 09, 2013 1:22 pm
Contact:

[Solved][Includes Example] Checking collisions and jumping around

Post by nikneym »

Solved! Here is a simple collision check in tile-based universe.

Code: Select all

local map = {
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0},
	{0, 0, 0, 1, 0},
	{0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1},
}

local TILE_WIDTH = 8
local TILE_HEIGHT = 8

local player = {
	x = 10,
	y = 10,
	w = 8,
	h = 8,

	speed = 50,
}

player.last = {
	x = player.x,
	y = player.y,
}

player.update = function(dt)
	player.last.x, player.last.y = player.x, player.y

	if love.keyboard.isDown("left") then
		player.x = player.x - player.speed * dt
	elseif love.keyboard.isDown("right") then
		player.x = player.x + player.speed * dt
	end

	if love.keyboard.isDown("up") then
		player.y = player.y - 100 * dt
	end

	player.y = player.y + 30 * dt
end

local col = function(x, y)
	return player.x < x + TILE_WIDTH
	and    x < player.x + player.w
	and    player.y < y + TILE_HEIGHT
	and    y < player.y + player.h
end

local verticalAligned = function(y)
	return player.last.y < y + TILE_HEIGHT and player.last.y + player.h > y
end

local horizontalAligned = function(x)
	return player.last.x < x + TILE_WIDTH and player.last.x + player.w > x
end

map.update = function()
	for y=1, #map do
		for x=1, #map[1] do
			local t = map[y][x]
			local tileX = TILE_WIDTH * x - TILE_WIDTH
			local tileY = TILE_HEIGHT * y - TILE_HEIGHT

			if t == 1 then
				if col(tileX, tileY) then
					if verticalAligned(tileY) then
						if player.x + player.w / 2 < tileX + TILE_WIDTH / 2 then
							local pushback = player.x + player.w - tileX
							player.x = player.x - pushback
						else
							local pushback = tileX + TILE_WIDTH - player.x
							player.x = player.x + pushback
						end
					elseif horizontalAligned(tileX) then
						if player.y + player.h / 2 < tileY + TILE_HEIGHT / 2 then
							local pushback = player.y + player.h - tileY
							player.y = player.y - pushback
						else
							local pushback = tileY + TILE_HEIGHT - player.y
							player.y = player.y + pushback
						end
					end
				end
			end
		end
	end
end

function love.update(dt)
	player.update(dt)
	map.update()
end

function love.draw()
	love.graphics.push()
	love.graphics.scale(8)

	for y=1, #map do
		for x=1, #map[1] do
			local t = map[y][x]

			local tileX = TILE_WIDTH * x - TILE_WIDTH
			local tileY = TILE_HEIGHT * y - TILE_HEIGHT 

			if t == 0 then
				love.graphics.setColor(40, 40, 160)
			elseif t == 1 then
				love.graphics.setColor(40, 160, 40)
			end

			love.graphics.rectangle("fill", tileX, tileY, TILE_WIDTH, TILE_HEIGHT)
		end
	end

	love.graphics.setColor(255, 255, 255)
	love.graphics.rectangle("fill", player.x, player.y, player.w, player.h)

	love.graphics.pop()
end
Last edited by nikneym on Sun May 10, 2020 5:50 am, edited 2 times in total.
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: Checking collisions and jumping around

Post by pgimeno »

Collisions are difficult to handle. I suggest you use a library. bump.lua is a good choice for AABBs.

As for Vector2, it sounds like a 2D vector, a vector with 2 components. No idea what libraries you've found and what they do.
User avatar
nikneym
Citizen
Posts: 56
Joined: Sat Mar 09, 2013 1:22 pm
Contact:

Re: Checking collisions and jumping around

Post by nikneym »

I tried bump.lua before and I'm sure it can create what I want but I want to make my own platformer collision detection. It don't have to be something super-realistic, just usual NES games-style platformer collisions. Are there any examples or sources about it?
sphyrth
Party member
Posts: 260
Joined: Mon Jul 07, 2014 11:04 am
Contact:

Re: Checking collisions and jumping around

Post by sphyrth »

Sheepolution's Chapter 23 for the collision sample. You can also follow-up with Chapter 24 of that.
Astorek86
Prole
Posts: 20
Joined: Fri Jul 13, 2018 7:35 pm

Re: Checking collisions and jumping around

Post by Astorek86 »

I would try a Thing like this:
  • Get Inputs and set Variables to store the Movement, but don't modify Player-Position yet. (For example: A Variable called "player.dx": If "a" is pressed, set Variable to -1; if "d" is pressed, set Variable to 1).
  • Check Collision on "player.x + player.dx"-Position.
    • If Collision is present, set "player.dx" to 0 (< This is some Quick'n Dirty-Solution, but it helps to understand how it works).
    • If no Collision is present, simply do nothing.
  • move Player-Position ("player.x = player.x + player.dx")
User avatar
pgimeno
Party member
Posts: 3548
Joined: Sun Oct 18, 2015 2:58 pm

Re: Checking collisions and jumping around

Post by pgimeno »

Astorek86 wrote: Wed Apr 29, 2020 10:48 pm If Collision is present, set "player.dx" to 0 (< This is some Quick'n Dirty-Solution, but it helps to understand how it works).
Indeed it's quick and dirty. If the player is going faster than 1 pixel per frame, this will show a separation from the collided rectangle. It would be possible to fix that by looping between the old and the new coordinates with a dx/dy step that skips one pixel at a time.

You also need to do the coordinates one by one, e.g. horizontal, and check for collision, and then vertical and check for collision (or vice versa); otherwise you don't know which side collided first as the OP wanted.
User avatar
nikneym
Citizen
Posts: 56
Joined: Sat Mar 09, 2013 1:22 pm
Contact:

Re: [Solved][Includes Example] Checking collisions and jumping around

Post by nikneym »

Well, I went ahead and read all the examples made by Sheepolution. I copied his collision check algorithm into my prototype. He uses classic.lua OOP system and because I'm prototyping, I made a non-OOP tile collision check for everyone who may struggle with this in the future (Jumping in the example should be updated). I generally can't understand someone else's code written in OOP :p

Code: Select all

local map = {
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0},
	{0, 0, 0, 1, 0},
	{0, 0, 0, 0, 0},
	{1, 1, 1, 1, 1},
}

local TILE_WIDTH = 8
local TILE_HEIGHT = 8

local player = {
	x = 10,
	y = 10,
	w = 8,
	h = 8,

	speed = 50,
}

player.last = {
	x = player.x,
	y = player.y,
}

player.update = function(dt)
	player.last.x, player.last.y = player.x, player.y

	if love.keyboard.isDown("left") then
		player.x = player.x - player.speed * dt
	elseif love.keyboard.isDown("right") then
		player.x = player.x + player.speed * dt
	end

	if love.keyboard.isDown("up") then
		player.y = player.y - 100 * dt
	end

	player.y = player.y + 30 * dt
end

local col = function(x, y)
	return player.x < x + TILE_WIDTH
	and    x < player.x + player.w
	and    player.y < y + TILE_HEIGHT
	and    y < player.y + player.h
end

local verticalAligned = function(y)
	return player.last.y < y + TILE_HEIGHT and player.last.y + player.h > y
end

local horizontalAligned = function(x)
	return player.last.x < x + TILE_WIDTH and player.last.x + player.w > x
end

map.update = function()
	for y=1, #map do
		for x=1, #map[1] do
			local t = map[y][x]
			local tileX = TILE_WIDTH * x - TILE_WIDTH
			local tileY = TILE_HEIGHT * y - TILE_HEIGHT

			if t == 1 then
				if col(tileX, tileY) then
					if verticalAligned(tileY) then
						if player.x + player.w / 2 < tileX + TILE_WIDTH / 2 then
							local pushback = player.x + player.w - tileX
							player.x = player.x - pushback
						else
							local pushback = tileX + TILE_WIDTH - player.x
							player.x = player.x + pushback
						end
					elseif horizontalAligned(tileX) then
						if player.y + player.h / 2 < tileY + TILE_HEIGHT / 2 then
							local pushback = player.y + player.h - tileY
							player.y = player.y - pushback
						else
							local pushback = tileY + TILE_HEIGHT - player.y
							player.y = player.y + pushback
						end
					end
				end
			end
		end
	end
end

function love.update(dt)
	player.update(dt)
	map.update()
end

function love.draw()
	love.graphics.push()
	love.graphics.scale(8)

	for y=1, #map do
		for x=1, #map[1] do
			local t = map[y][x]

			local tileX = TILE_WIDTH * x - TILE_WIDTH
			local tileY = TILE_HEIGHT * y - TILE_HEIGHT 

			if t == 0 then
				love.graphics.setColor(40, 40, 160)
			elseif t == 1 then
				love.graphics.setColor(40, 160, 40)
			end

			love.graphics.rectangle("fill", tileX, tileY, TILE_WIDTH, TILE_HEIGHT)
		end
	end

	love.graphics.setColor(255, 255, 255)
	love.graphics.rectangle("fill", player.x, player.y, player.w, player.h)

	love.graphics.pop()
end
User avatar
inJuly
Prole
Posts: 29
Joined: Fri May 01, 2020 9:02 pm
Contact:

Re: [Solved][Includes Example] Checking collisions and jumping around

Post by inJuly »

If you want to learn how to handle physics and collisions, these are a couple of things you should learn:

1. AABB collision detection (just brute force in your first few games)
2. Sweep and prune
3. Fixed Resolution grids
4. Quadtrees or BSP Trees or k-d trees (Personally gonna recommend quadtrees)
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 71 guests