Log In  

Hi guys.
I'm making a top-down style action game.
But, my implementation of the diagonal movement and the collision detection does not work as desired.

Cart #kisihurake-0 | 2022-05-12 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
1

  • 1st, the diagonal movement seems too slow.
  • 2nd, after a few try, it may be got stuck into the solid block.

Any help about those ?

English is not my tongue, I hope my English get through to you.

Thank you.

P#111730 2022-05-12 14:22

:: merwok
2

I normalize diagonal speed thanks to this very good guide: https://demoman.net/?a=normalizing-input

 local dx,dy=0,0
 if (btn(⬅️)) dx=-player.spd
 if (btn(➡️)) dx=player.spd
 if (btn(⬆️)) dy=-player.spd
 if (btn(⬇️)) dy=player.spd

 if dx*dy!=0 then
  dx*=.7071
  dy*=.7071
 end

Your division by √2 is the same as multiplication by 0.7071, so it should give the same result.
Maybe it’s some of the other code that causes the unwanted effect?

As a player, the diagonal movement in your game seems like a direct effect of my inputs, whereas straight moves feel that they go too far.

P#111734 2022-05-12 14:45 ( Edited 2022-05-12 14:52)
1

The reason the diagonal movement is slow is because you applied the diagonal correction too broadly. The result is that the correction ramps up the friction. I don't know how to explain what's needed in simple words, so here's the code changes in their entirety:

Remove this part in the code:

 -- correct diagonal speed
 if (obj.dx * obj.dy) ~= 0 then
  obj.dx = obj.dx / sqrt(2)
  obj.dy = obj.dy / sqrt(2)
 end

Then change this part

    if btn(0) then
        p.dx-=p.acc
    end
    if btn(1) then
        p.dx+=p.acc
    end
    if btn(2) then
        p.dy-=p.acc
    end
    if btn(3) then
        p.dy+=p.acc
    end

    -- limit speed
    p.dx=mid(-p.maxd,p.dx,p.maxd)
    p.dy=mid(-p.maxd,p.dy,p.maxd)

to this

    local accx,accy,maxd=0,0,p.maxd
    if btn(0) then
        accx-=p.acc
    end
    if btn(1) then
        accx+=p.acc
    end
    if btn(2) then
        accy-=p.acc
    end
    if btn(3) then
        accy+=p.acc
    end
    -- correct for diagonals
    if accx*accy~=0 then
      accx=accx/sqrt(2)
      accy=accy/sqrt(2)
      maxd=maxd/sqrt(2)
    end
    p.dx+=accx
    p.dy+=accy

    -- limit speed
    p.dx=mid(-maxd,p.dx,maxd)
    p.dy=mid(-maxd,p.dy,maxd)

That does about the same thing as merwok's suggestion, but it does it to the specific vectors in use rather than the overall situation. That said, the movement won't look smooth because of the lack of sub-pixel drawing and also because your movement is implicitly rounded by your apply_move() function.

Regarding getting stuck sometimes, that one's harder to explain or fix. The problem is really subtle.

Here's the typical case:
-- player need to move 1.4 pixels to the left.
-- code finds that 1.4 is more than 0.
-- code finds that spot 0 pixels to the left doesn't have a wall
-- code finds that 1.4 is more than 1.
-- code finds that spot 1 pixel to the left doesn't have a wall
-- code finds that 1.4 is less than 2.
-- code moves player to the left 2.

The problem occurs when the wall is a fraction number of pixels away from where the player would theoretically end up. In that case, the code misses the part that checks for the wall in the last run of the loop.

The easiest way I can think of to fix it is to raise v until it hits a solid or is too high and then always move it back.

Replace this code

 if obj.dx > 0 then
  if not is_solid(obj, "right", 1) then
   local rv = 0
   while rv < obj.dx do
    if is_solid(obj, "right", rv) then
     obj.dx = 0
     rv = rv - 1
     break
    end
    rv = rv + 1
   end
   obj.x = obj.x + rv
  end
 end

with this

 if obj.dx > 0 then
  if not is_solid(obj, "right", 1) then
   local rv = 0
   while rv < obj.dx and not is_solid(obj,"right",rv) do
    rv = rv + 1
   end
   if is_solid(obj, "right", rv) then
    obj.dx = 0
    rv = rv - 1
   end
   obj.x = obj.x + rv
  end
 end

Then, do the same for the other directions.

P#111745 2022-05-12 18:19
1

About the Collision-Thing:
Out of curiosity, I wanted to implement my own "is_solid"-function to this which might be useful.
https://www.lexaloffle.com/bbs/?tid=46181

I did the following:

  • Replaced the "is_solid"-function with my own.

function is_solid(opt,f,ox,oy,flags,debug)
 local collist={}
 ox = ox or 0
 oy = oy or 0
 flags=flags or {0}
 if(type(flags)!="table")flags={flags}
 local ix=f.x+f.ox+ox
 local iy=f.y+f.oy+oy
 for x=ix,ix+f.w+7,8 do
  for y=iy,iy+f.h+7,8 do
   if opt==nil
   or opt=="full"
   or opt=="left" and x==ix
   or opt=="right" and x>=ix+f.w
   or opt=="up" and y==iy
   or opt=="down" and y>=iy+f.h
   or opt=="rdown" and y>=iy+f.h and x>=ix+f.w
   or opt=="ldown" and x==ix and y>=iy+f.h
   or opt=="rup" and y==iy and x>=ix+f.w
   or opt=="lup" and y==iy and x==ix
   then
    add(collist, {x=min(x,ix+f.w), y=min(y,iy+f.h)})
   end
  end
 end
 if(debug)print(#collist)
 for c in all(collist) do
  if(debug)pset(c.x,c.y,8)
  for v in all(flags) do
   if(fget(mget(c.x/8,c.y/8),v))return true
  end
 end
 return false
end

  • Added and modified the Player-Table, like:
p = {
 w=7,  -- modified
 h=7,  -- modified
 ox=0, -- added
 oy=0, -- added
 [...]
}
  • Added a Function that allows reducing Variables to zero, like:
function step_to_zero(ret,value)
 if(ret>0)return max(0,ret-value)
 return min(0,ret+value)
end
  • After that, I replaced the "apply_move"-function with this:
function apply_move(obj)
 local dx=obj.dx
 local dy=obj.dy

 while is_solid("full",obj,dx) do
  dx=step_to_zero(dx,1)
 end
 obj.x+=dx

 while is_solid("full",obj,0,dy) do
  dy=step_to_zero(dy,1)
 end
 obj.y+=dy
end

The "step_to_zero" is needed because the Player is able to move more than one Pixel per Frame. If collision happens, then the code tries to reduce the "Speed" (dx or dy) of the Player to 1 Pixel and checks again, until there's basically no collision possible (because dx/dy are set to zero, which have to be collision-free because the Player's already on that Position...). I have to admit that this is a bit dirty, because it doesn't smooth out exacly 100% of the Values (everything that's between .0001 and .9999 are missing), but at least it's enough to "visually connect" to the Collision...

P#111756 2022-05-13 00:49

Thank you, merwok, kimiyoribaka, Astorek86 !!!
Thanks to your great advice, I will be able to proceed with my development.
Someday I would like to show you the finished version.
Thank you.

P#111785 2022-05-13 13:20

Hi guys.
I improved my code with your advice.
Astorek86's "is_solid"-function is awesome, but a little too difficult for me to understand...
So, I made a simplified version.
Thank you.

Cart #nohehugara-0 | 2022-05-15 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA

P#111861 2022-05-15 23:23

[Please log in to post a comment]

Follow Lexaloffle:        
Generated 2022-05-24 03:52:02 | 0.009s | Q:24