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.

- 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.

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.

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.

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.

- 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...

[Please log in to post a comment]