Log In  

So I'm working on a tile-based movement engine, and I was having some trouble figuring out how to handle collisions with objects that are "solid." Here is what I have so far for the collision detection:

function getPreviousPos()  --This is ran before moving positions, at keypress
  player.xprevious = player.x
  player.yprevious = player.y
end

function isCollide()
  if (player.x == solid.x and player.y == solid.y) then 
    player.x = player.xprevious
    player.y = player.yprevious
  end
end

This is just a rough markup since I'm away from my computer. But I'm basically trying to find out how I can replaced solid.x and solid.y with a reference for a specific sprite. (So that this check is not just for a single, individual sprite, but that sprite as a whole.)
I hope I'm being clear enough, thanks!

P#52230 2018-05-01 13:39 ( Edited 2018-05-04 23:20)

Have a good look at the collide demo (shipped with pico-8).
(it should be somewhere in the pico banner :)

install_demos

Unless your player can only move by whole increment, your code won't work.
You need to separate axis and accomodate for player width.

P#52236 2018-05-01 15:27 ( Edited 2018-05-01 19:27)

So you're currently checking if the player is EXACTLY at the same position as the solid. But what you want to check for is if the solid and player overlap. So you need to take the with and height into consideration.

I made a video that walks you through what you need to do here:

P#52237 2018-05-01 15:28 ( Edited 2018-05-01 19:28)

@freds72 Yeah the movement is grid-based, so the player moves 8 pixels per keypress, using btnp(). I'll check that out once I get home

@Krystman Yeah it was a quick and easy way to check collision, so if the player is on the same exact spot as the solid, it just moves the player back. It actually looks quite good in practice, you don't even see the player move. But I'll check out your video for sure!

P#52239 2018-05-01 15:33 ( Edited 2018-05-01 19:33)

But more to what I'm looking for is how I can check if the player is on top of a particular sprite (i.e 002). I read about using flags possibly, but I'm stumped on how to implement that.

P#52241 2018-05-01 15:40 ( Edited 2018-05-01 19:40)

@HoofedEar Well then it sounds like it really depends on the rest of your code. It's unclear if x and y are pixel coordinates or map coordinates.

If you're working with a map, here is a small sample code showing off a basic way of how to do it:
https://www.lexaloffle.com/bbs/?tid=28277

P#52242 2018-05-01 15:42 ( Edited 2018-05-01 19:42)

@Krystman yeah I apologize about that, I'll post the source as soon as I get home (about 6 more hours.) So far all I have done to get the sprite on the screen is use SPR() and set the solid.x and solid.y which is used by SPR(). I haven't got much farther than that. But thank you for the example! I'll check that out as well.

P#52243 2018-05-01 15:46 ( Edited 2018-05-01 19:46)

Okay so here is the code for my engine:

function _init()
    player = {}
    player.x = 32
    player.y = 32
    player.xprevious = 1
    player.yprevious = 1
    solid = {}
    solid.x = 16
    solid.y = 16
end

function _update()
    function isCollide() --Check if the player is on top of something.
        if (player.x == solid.x and player.y == solid.y) then
            player.x = player.xprevious
            player.y = player.yprevious
        end
    end

    function getPrevPos() --Get the previous coordinates before moving
        player.xprevious = player.x
        player.yprevious = player.y
    end

    if (btnp(0) and player.x > 0) getPrevPos(); player.x -= 8 --getPrevPos() is ran before movement.
    if (btnp(1) and player.x < 120) getPrevPos(); player.x += 8
    if (btnp(2) and player.y > 0) getPrevPos(); player.y -= 8
    if (btnp(3) and player.y < 120) getPrevPos(); player.y += 8

    isCollide() --At the end of the step, check if the player is on top of the object.
end

function _draw()
    cls()
    spr(1,player.x,player.y)
    spr(2,solid.x,solid.y)
    spr(2,solid.x+64,solid.y+64)
    print(player.xprevious)
end

I'd like to figure out how I can check if the player is on top of a kind of sprite, rather than just checking an arbitrary variable I created for just one sprite on the screen.

P#52268 2018-05-02 09:14 ( Edited 2018-05-02 13:14)

@Krystman it looks like your maze collision example is probably the best place for me to start, by using flags and such. Thank you for that!

P#52270 2018-05-02 09:25 ( Edited 2018-05-02 13:25)

Yeah, since your game is tile-based, it's pretty easy. Either check the sprite number with mget(player.x/8, player.y/8) or the flags with fget(mget(player.x/8, player.y/8)

P#52271 2018-05-02 09:36 ( Edited 2018-05-02 15:21)

@tobiasvl Exactly what I was looking for, thank you! That's neat that I can check for either flags or sprite numbers. I think I'll use flags, more flexibility.

P#52278 2018-05-02 11:02 ( Edited 2018-05-02 15:02)

From what I've read, it looks like you're trying to make a rogue-like engine. I've made a few in the past, this is pretty much the basis of what I've done:

function _init()
  player = {}
  player.x = 3
  player.y = 3
  player.s = 1
  player.hp = 100

  m = {}
  for i = 0, 15 do
  m[i] = {}
    for j = 0, 15 do
      m[i][j] = {}
      m[i][j].id = 0
    end
  end

  m[3][4].id = 1
  m[3][4].s = 2
  m[4][4].id = 1
  m[4][4].s = 2
  m[5][4].id = 1
  m[5][4].s = 2

  m[6][6].id = 2
  m[6][6].s = 3
end

function _update()
  local dx = player.x
  local dy = player.y

  local press_left = btnp(0)
  local press_right = btnp(1)
  local press_up = btnp(2)
  local press_up = btnp(3)

  if press_left then
    dx = dx - 1
  elseif press_right then
    dx = dx + 1
  elseif press_up then
    dy = dy - 1
  elseif press_down then
    dy = dy + 1
  end

  o = m[dx][dy]

  if o.id == 0 then
    player.x = dx
    player.y = dy
  elseif o.id == 0 then
    player.x = dx
    player.y = dy
    player.hp = player.hp + 10
  end
end

function _draw()
  cls()
  for i = 0, 15 do
    for j = 0, 15 do
      if m[i][j].id > 0 then
        spr(m[i][j].s, i * 8, j * 8)
      end
    end
  end

  spr(player.s, player.x * 8, player.y * 8)
end

To put it in a nutshell, there's 2 main objects: player and m (the map). The player object stores all the information for the player including stuff like its x and y position, sprite information and hp. In the update, it checks if a move button has been pressed. If it has, it then sets "destination x" and "destination y" positions. It then checks the map to see if there's something in the position the player wants to move to. If there is nothing (id 0) then it simply moves there, if there's a potion (id 2) then it'll move there and increase the hp of the player. But, if there's a wall (id 1) it won't move.

I know that this little engine is inefficient, if I was making an actual game there would be more that I would do but I think this is a fairly easy to read example that's scalable for whatever project you need.

And yes, there is a reason why I didn't use the in-built map. It's because my map object can contain a lot more information than just x, y and a flag. Also in roguelikes, it's pretty easy to make your own camera. You can simple set an offset and only draw from the offset to offset + 15 tiles. If you don't know what I mean, I'm happy to provide an example of how I've handled it in the past.

P#52383 2018-05-04 19:20 ( Edited 2018-05-04 23:21)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-29 00:32:46 | 0.033s | Q:26