How to use bump.lua

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.
crystalheartstudios
Prole
Posts: 10
Joined: Thu Sep 04, 2014 7:38 pm

How to use bump.lua

Post by crystalheartstudios »

I have been reading guides and documentation, and frankly it makes no sense how to use bump.lua. I know how to declare two objects with colliders, but I don't really understand how they detect collision. I am making a top down (zelda styled) rpg, and I want the character to stop moving when they collide with the edges of the map (along with props in the world). How would this be done? I'm seriously confused.
User avatar
Lacotemale
Citizen
Posts: 75
Joined: Sat Mar 08, 2014 9:01 pm

Re: How to use bump.lua

Post by Lacotemale »

I also posted in my own thread about this. I don't know how to use the damn thing and I will have to consider using something else soon. T_T
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: How to use bump.lua

Post by kikito »

Hi there!
Lacotemale wrote:I also posted in my own thread about this.
I didn't see it, sorry. Can you give me a link? EDIT: Found it. For the record, I never browse "kitchen sink" threads that ask about everything and anything. Next time, consider creating a new thread per question/subject, with a descriptive title, like crystalheartstudios did; you will get more attention.

------

Let me try to explain how bump works in steps. I recommend you to follow each step and test it in an empty main.lua. I am not trying all the tests myself so I might make typos.

STEP 1:

The first thing you have to do is downloading the bump.lua file and putting it somewhere in your game folder (people usually put it inside a lib/ folder).

Then, require it wherever you use it. For example, if you put bump.lua next to main.lua, you can require it like this from main.lua:

Code: Select all

local bump = require 'bump'
STEP 2:

The next thing you must do is creating a world with bump.newWorld. The simplest think I can think of is making it local in main.lua:

Code: Select all

local bump = require 'bump'

local world
function love.load()
  world = bump.newWorld()
end
STEP 3:
In bump, a world is just a place where you put items. Items are everything which participates in collisions in your game: the player, the blocks, bullets, powerups, etc. Every item will have an associated rectangle. Generally, items are tables.

In order to add items to a world, you use world:add. Let's start with the player. Imagine that our player character is a 32x32 image that starts in {100, 100}. You need to create a table for it, and then tell the world about it.

- Note that for simplicity I am going to use x and y on this example, but in bump those variables are called l (for left) and t (for top). This is because sometimes x,y is the center of the rectangle, not its left-top corner -

Code: Select all

local bump = require 'bump'

local world
local player
function love.load()
  world = bump.newWorld()
  player = {x=100, y=100, w=32, h=32}
  world:add(player, player.x, player.y, player.w, player.h)
end

function love.draw()
  love.graphics.rectangle(player.x, player.y, player.w, player.h)
end
STEP 4:

Now, when you move the player with the arrow keys, you must do two things: You must change the values of x and y in the player table, and also update the bounding box associated with the player in the world. You do this with world:move. This is one possible way to do it:

Code: Select all

...

function love.update(dt)
  local player_speed = 60
  -- update the player if the keys are pressed
  if love.keyboard.isDown('up') then
    player.y = player.y - player_speed * dt
  elseif love.keyboard.isDown('down') then
    ... (repeat with down, left and right)
  end
  -- update the player associated bounding box in the world
  world:move(player, player.x, player.y) -- player.w and player.h remain the same if not passed
end
STEP 5:

We need to add some blocks now. Blocks are easy because they don't move. I am going to store them in a variable called blocks. To make things a bit easier, I am going to use a function called addBlock. I am going to create only two blocks; you can create more if you need.

Code: Select all

local bump = require 'bump'

local world
local player
local blocks

local function addBlock(x,y,w,h)
  local block = {x=x,y=y,w=w,h=h}
  blocks[#blocks + 1] = block
  world:add(block, x,y,w,h)
end

function love.load()
  world = bump.newWorld()
  player = {x=100, y=100, w=32, h=32}
  world:add(player, player.x, player.y, player.w, player.h)
  addBlock(50, 100, 200, 32)
  addBlock(150, 100, 100, 32)
end

function love.draw()
  love.graphics.rectangle(player.x, player.y, player.w, player.h)
  for i=1, #blocks do
    local b = blocks[i]
    love.graphics.rectangle(b.x, b.y, b.w, b.h)
  end
end
STEP 6:

But now the player does not interact with the blocks at all; it moves through them. We must check for collisions. The way we check for collisions in bump is by calling world:check. It works as follows: we pass it an item (in this case, the player) and the coordinates where it wants to go. world:check returns a list of collisions and its length. If the way is free, then the list of collisions is empty, and its length will be 0.

So we can do a rough collision detection phase like this:

Code: Select all

...

function love.update(dt)
  local player_speed = 60
  local future_x, future_y = player.x, player.y
  -- update future_x and future_y variables if the keys are pressed
  if love.keyboard.isDown('up') then
    future_y = future_y - player_speed * dt
  elseif love.keyboard.isDown('down') then
    ... (repeat with down, left and right)
  end

  -- get the collisions
  local collisions, len = world:check(player, future_x, future_y)

  -- If there where no collisions, we can move the player safely
  if len == 0 then
    player.x, player.y = future_x, future_y
    world:move(player, player.x, player.y)
  end
end
STEP 7:

The thing is, this is not enough. There are two problems:
  • It is not possible to do "partial steps". If the player goes fast enough, it will seem that he stops before he touches the blocks - there will be "phantom borders" around blocks sometimes. Plus, this depends on lots of things (like the speed of the computer)
  • The player doesn't "slide over blocks" when going diagonally; the moment a collision is detected, it stops.
These problems happen because we are detecting the collisions, but not resolving then correctly. bump has several ways to help calculating the resolution, because different items in games behave differently - for example, Super Mario "slides" over tiles and walls, the bubbles in Supper Pang "bounce", while bullets tend to "get stuck" and not bounce at all.

In our case, we're going to use the "slide" resolution, so our player can "move diagonally and still go up" when facing a wall. collision:getSlide returns "the coordinates of the player who wanted to go to future_x and future_y after he collided with a block, sliding over it" (plus more things, like the coordinates where the player started touching the block, and the contact normal).

Here is a better (but still not perfect) approach to resolution, using the first collision:

Code: Select all

function love.update(dt)
  ... (same as before)

  -- get the collisions
  local collisions, len = world:check(player, future_x, future_y)

  -- If there where no collisions, we can move the player safely
  if len == 0 then
    player.x, player.y = future_x, future_y
    world:move(player, player.x, player.y)
  elseif len == 1 then
    local _,_,_,_,slide_x, slide_y = collisions[i]:getSlide()
    player.x, player.y = slide_x, slide_y
    world:move(player, player.x, player.y)
  end
end
STEP 8:

This solves the first problem (there is no invisible barrier any more). But it makes some things worse. There are some occasions when the player collides simultaneously with more than 1 block (for example, when moving diagonally up-right to the up-right corner of a room). This code handles collisions with 1 block only, so when the player hits more than 1, it only collides with the nearest one, ignoring the rest.

Since the number of colliding blocks is unknown, we can't solve it with if-elses; we must use a loop, so we collide with the first block, then slide over it, then collide with the second, etc. There is a problem though: after sliding over the first block, the other collisions are not valid and must be recalculated. Also, sliding over more than 1 block will give bad results - we must slide only on the "last block colliding". For the rest, the player must act as a bullet, "getting stuck".

This all gives us the following code:

Code: Select all

function love.update(dt)
  ... (same as before)

  -- get the collisions
  local collisions, len = world:check(player, future_x, future_y)

  -- If there where no collisions, we can move the player safely
  if len == 0 then
    player.x, player.y = future_x, future_y
    world:move(player, player.x, player.y)
  else
     local col, tx, ty, sx, sy
     while len > 0 do
       col = cols[1]
       tx,ty,_,_,sx,sy = col:getSlide()
       -- Move the player so that it "touches" the block
       player.l, player.t = tx, ty
       world:move(player, tx, ty)
       -- See if it collisions on anything else while "sliding" over the block
       cols, len = world:check(player, sx, sy)
       if len == 0 then -- If not colliding with anything else, slide. Otherwise, touch the next block and try sliding over it
         player.l, player.t = sx, sy
         world:move(player, sx, sy)
       end
    end
  end
end
That's it. Now it should work more or less like Zelda.

Incidentally, this code is almost a copy from bump's simple demo. Launch it up if you want to see it in action (the only thing that demo adds is debugging info).

There are more advanced stuff to do from here: filtering collisions, colliding with different kinds of items, and spatial queries. But I hope this explanation gets you started.
When I write def I mean function.
crystalheartstudios
Prole
Posts: 10
Joined: Thu Sep 04, 2014 7:38 pm

Re: How to use bump.lua

Post by crystalheartstudios »

Well the information is nice, but what I'm trying to do doesn't seem to fit in what you are describing. For example, I am making a zelda/pokemon-esque game and I need to set up colliders around the edge of the map so that my character cannot move out of the boundaries of the map, along with not being able to pass through different objects (say trees). I just feel like there has to be a simpler way.
User avatar
Lacotemale
Citizen
Posts: 75
Joined: Sat Mar 08, 2014 9:01 pm

Re: How to use bump.lua

Post by Lacotemale »

I have attached a love file here of my work in progress (or not very good progress you might say) x)

I think my main problem with bump is the coords of the player collision box are not actually the coords of the player. I don't know how to explain what is going on except that the box isn't following the player but something else.

Any ideas how I can fix this?
Attachments
WhiteGuardian.L2D.love
(1.13 MiB) Downloaded 339 times
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: How to use bump.lua

Post by kikito »

crystalheartstudios wrote:I need to set up colliders around the edge of the map so that my character cannot move out of the boundaries of the map, along with not being able to pass through different objects (say trees).
The simplest way to do that is: create one block per border, and one block per unwalkable object. Make sure that the basic rectangles work as expected. Then, draw nice graphics instead of drawing rectangles. To do the first part, you just need to call addblock more times, with different parameters. The basic demo is a zelda like with squares instead of graphics.\\

Lacotemale wrote:Any ideas how I can fix this?
I gave this a quick look so I am not sure I found all the issues.

1- You are moving the player before checking for collisions.

Code: Select all

	-- see if A is colliding with anything
	world:move(A, player.x, player.y)
	local collisions, len = world:check(B)
Instead, calculate where the player will be (future_x, future_y), get the collisions first, and then move him there only if he doesn't collide with anything. And if he collides, resolve the collisions with the code I gave you in step 9 above.

2- Use the real objects of your game in world:add, don't create "extra objects with a name"

Instead of this:

Code: Select all

	-- create two rectangles
	A = {name="A"}
	B = {name="B"}

	-- insert both rectangles into bump
	world:add(A,   player.x, player.y, 100, 100) -- left, top, width, height
	world:add(B, item.x, item.y, 100, 100)

...
	-- see if A is colliding with anything
	world:move(A, player.x, player.y)
Do this:

Code: Select all

	-- insert both rectangles into bump
	world:add(player,   player.x, player.y, 100, 100) -- left, top, width, height
	world:add(item, item.x, item.y, 100, 100)

...
	-- see if player is colliding with anything
	world:move(player, player.x, player.y)
[/code]

3- You are moving item instead of moving the player when keys are pressed:

Code: Select all

      if down("w") or down("up")	then
			item.y =  item.y + speed * dt
			sprite.y = sprite.y - speed * dt
			walkDown:stop()
			walkUp:play()
			walkUp:update(dt)
		end
That should be player.y = player.y + speed *dt (I think)

That's all I could find in a quick look.

I recommend you to remove the graphical part and start with drawing just rectangles. Once you get the rectangles working, get the graphics on top of them. Doing it the other way around makes it more difficult to find bugs like this.
When I write def I mean function.
Bindie
Party member
Posts: 151
Joined: Fri Jan 23, 2015 1:29 pm

Re: How to use bump.lua

Post by Bindie »

Hey, I have been trying to learn bump since yesterday, I'm starting to understand :getSlide() and :move, :check. I have followed the examples up until step 7 where local _,_,_,_, is and I don't understand what I should put there. Up until step 6 I followed as well, the player doesn't move, why?

I have attached .love file.

Bindie
Attachments
Bump.love
(7.04 KiB) Downloaded 287 times
User avatar
Kingdaro
Party member
Posts: 395
Joined: Sun Jul 18, 2010 3:08 am

Re: How to use bump.lua

Post by Kingdaro »

Bindie wrote:Hey, I have been trying to learn bump since yesterday, I'm starting to understand :getSlide() and :move, :check. I have followed the examples up until step 7 where local _,_,_,_, is and I don't understand what I should put there. Up until step 6 I followed as well, the player doesn't move, why?

I have attached .love file.

Bindie
The example in this thread is old. Follow the example / documentation on the github instead.

Basically, what you'll want here for the player's collision detection is this:

Code: Select all

	-- update the player associated bounding box in the world
	local newX, newY, cols, len = world:move(player, player.x, player.y)
	player.x, player.y = newX, newY
The new bump version's move function returns the new x and y coordinates in the world, instead of having the user resolve all of the collisions manually. Another problem I found was the use of a future x and future y variable in the player, which isn't necessary, and it made it so that you were resetting the player's x and y every frame. Here's a fixed main.lua:

Code: Select all

local bump = require 'bump'

local world
local player
local blocks = {}

local function addBlock(x,y,w,h)
	local block = {x=x, y=y, w=w, h=h}
	blocks[#blocks+1] = block
	world:add(block, x,y,w,h)
end

function love.load(arg)
	world = bump.newWorld()
	player = {x=100, y=200, w=32, h=32, speed = 60, col = 0, len = 0}
	world:add(player, player.x,player.y, player.w, player.h)
	addBlock(50,100,200,32)
	addBlock(150,100,100,32)
end

function love.update(dt)
	-- update the player.x and player.y when arrow keys are pressed
	if love.keyboard.isDown('up') then
		player.y = player.y - player.speed * dt
	elseif love.keyboard.isDown('down') then
		player.y = player.y + player.speed * dt
	elseif 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

	-- update the player associated bounding box in the world
	local newX, newY, cols, len = world:move(player, player.x, player.y)
	player.x, player.y = newX, newY
end

function love.draw(dt)
	love.graphics.rectangle('fill', player.x, player.y, player.w, player.h)
	for i=1, #blocks do
		local b = blocks[i]
		love.graphics.rectangle('line', b.x, b.y, b.w, b.h)
	end
end
Bindie
Party member
Posts: 151
Joined: Fri Jan 23, 2015 1:29 pm

Re: How to use bump.lua

Post by Bindie »

Thanks, awesome. :awesome:
User avatar
kikito
Inner party member
Posts: 3153
Joined: Sat Oct 03, 2009 5:22 pm
Location: Madrid, Spain
Contact:

Re: How to use bump.lua

Post by kikito »

Hey, I got here but Kingdaro had already answered. Thanks a lot, buddy! :3
When I write def I mean function.
Post Reply

Who is online

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