2
\$\begingroup\$

Got a new project recently where I don't use any physics engine, but it requires some basic phyiscs. Basically it's a 2d "top-down" game type. I would like to add a smooth wall collision which feels better than those "jumping" ones. So here is how bad it looks like, I easily calulated the collision point in this way:

  1. Added the velocity to the player's position.
  2. Checked whether the new position is in the wall or not.
  3. Went backwards at that vector where it "came from" until reached the first non-colliding position.

Now that I have the blue position, I would like to modify the player's vector's angle with that purple angle (I guess). Because I want the next thing to happen when the player reaches that position:

  • The player gets the wall's angle and walks next to it (of course only if the player still presses the key to move upwards) like in most AAA 3D games. For example TESV:Skyrim. If you are moving toward a wall or an object in a non 90° angle, then the player keeps moving but next to the wall/object.

I have already searched for solutions on Google and here aswell, but none of them fit my needs.
Would appreciate any help. Thanks!

EDIT:

  • Green Line: Wall (Top one, the bottom one just for help)
  • Purple Point: Player old position
  • Blue Point: Player new position (next to the wall)
  • Red Line: Vector between new and old position

    EDIT 2:
    Can't get it to work properly, at the edges of the walls the calculations fail. The player keeps teleporting, at the program freezes sometimes. (due the while cycles which are checking for the edges)



_

\$\endgroup\$
5
  • \$\begingroup\$ is the blue p the player's head and the pink one his legs ?? \$\endgroup\$
    – concept3d
    Commented Jan 6, 2014 at 19:30
  • \$\begingroup\$ No, the pink one is the current position, the blue one is the calculated position next to the wall. Did not seperate the player's parts. \$\endgroup\$
    – Zhafur
    Commented Jan 6, 2014 at 19:30
  • \$\begingroup\$ so where is the wall then? TBH the sketch isn't clear enough at least for me. \$\endgroup\$
    – concept3d
    Commented Jan 6, 2014 at 19:33
  • \$\begingroup\$ ` I would like to modify the player's vector's angle with that purple angle ` what do you mean by this ? Why do you assume that's the angle you want to modify ? \$\endgroup\$
    – concept3d
    Commented Jan 6, 2014 at 20:56
  • \$\begingroup\$ If I multiply the xVelocity by Math.cos(purpleAngle) and yVelocity by Math.sin(purpleAngle) then the player will move in that angle. \$\endgroup\$
    – Zhafur
    Commented Jan 6, 2014 at 21:03

2 Answers 2

3
\$\begingroup\$

How many games do this is not with fancy changing of the velocity after the collision has been resolved, but instead the way they resolve the collisions or in your scenario, how you found the the player new position (blue P).

You explained that you simply retraced your steps along the vector to come up with that position. Instead of doing what you did, what is a fairly common way to resolve collisions and achieve this sliding effect is through projection. This and other collision detection / resolution approaches are discussed in great detail here.

By using projection you will end up with a sliding effect like you are looking for because you are resolving for a position that is along the wall using the shortest distance possible instead of simply backtracing your steps as shown in the following image:

Collision resolution via projection

To quote the above linked article on resolving collisions via projection, below is how you can find the projection vector and an interactive example can be found here.

The formula for projecting vector a onto vector b is:

proj.x = ( dp / (b.xb.x + b.yb.y) ) * b.x; proj.y = ( dp / (b.xb.x + b.yb.y) ) * b.y;

where dp is the dotprod of a and b: dp = (a.xb.x + a.yb.y)

Note that the result is a vector; also, (b.xb.x + b.yb.y) is simply the length of b squared.

If b is a unit vector, (b.xb.x + b.yb.y) = 1, and thus a projected onto b reduces to:

proj.x = dp*b.x;

proj.y = dp*b.y;

To get the vector for the wall you just need to know two points on the wall. It could be two corners, the collision point and a corner, etc. Once you have two points you subtract the coordinates to find the resulting vector.

e.g. - Given P <1,2> and Q <3,5> then PQ = Q - P

= PQ <3-1, 5-2>

= PQ <2,3>;

If you have a library that can subtract the vectors this is even easier for you. I don't believe direction is important in this calculation but if I'm wrong and you find you are getting a weird behavior, try subtracting in the opposite direction and see if it helps.

\$\endgroup\$
5
  • \$\begingroup\$ This is exactly what I'm looking for, though I don't know the vector of the wall, therefore I can't project the velocity vector onto that... or am I just that dumb that I don't realize the solution? \$\endgroup\$
    – Zhafur
    Commented Jan 6, 2014 at 23:45
  • \$\begingroup\$ See my update - you may also find this resource helpful for basic vector math and its uses - natureofcode.com/book/chapter-1-vectors \$\endgroup\$ Commented Jan 7, 2014 at 1:03
  • \$\begingroup\$ The problem is getting that second point on the wall. \$\endgroup\$
    – Zhafur
    Commented Jan 7, 2014 at 1:46
  • \$\begingroup\$ You probably have the information since you were able to determine that you intersected with the wall in the first place, though it's likely specific to the library/engine/framework/whatever that you used. \$\endgroup\$ Commented Jan 7, 2014 at 1:56
  • \$\begingroup\$ Hey, this is a really good answer, I tried to implement what you said, along with help from some other question, but I got stuck at the corners (literally) stackoverflow.com/questions/61518457/… basically how do u calculate for the outside of any mesh, at the corner of it? \$\endgroup\$ Commented Apr 30, 2020 at 8:10
2
\$\begingroup\$

This answer will focus on statement #3 in the original post, and is meant to supplement ToddersLegrande's answer

3 . Went backwards at that vector where it "came from" until reached the first non-colliding position.

That idea is prone to failure as you have found out, luckily there is a closed form solution available, I will do in-depth on that here.


Given a set of points on a line, plugging the coordinates into the line equation (y=m*x+b) yields a system of equations which can be solved to find the slope-intercept form of the given line.

Starting with the player's position we assert that:

Then we also assert that the player's position after velocity is on the same line:

With enough information to define the line now available, we can solve for the slope and intercept of the players line of motion to find:

Applying the same technique to finding the slope and intercept of the wall yields:

By solving the system formed by these two line equations, we find a (rather large) formula which directly computes the intersection, I've formatted this as code just in case you aren't familiar with maxima

Vec2 FindIntersection(Vec2 player,Vec2 motion,Vec2 wall1,Vec2 wall2) {
    return Vec2(
        -(motion.x*(wall1.x*wall2.y-wall1.y*wall2.x)
        +motion.x*player.y*(wall2.x-wall1.x)+motion.y*player.x
        *(wall1.x-wall2.x))/(motion.x*(wall1.y-wall2.y)
        +motion.y*(wall2.x-wall1.x)),

        -(motion.y*(wall1.x*wall2.y-wall1.y*wall2.x)
        +motion.x*player.y*(wall2.y-wall1.y)+motion.y*player.x
        *(wall1.y-wall2.y))/(motion.x*(wall1.y-wall2.y)
        +motion.y*(wall2.x-wall1.x))
    );
}

Here's how I did it on Maxima:

(%i) FindIntersection(PlayerX,PlayerY, MotionX,MotionY, WallVert1X,WallVert1Y, WallVert2X,WallVert2Y) :=
    ''(solve(
        subst(solve([PlayerY=pm*PlayerX+pb,(PlayerY+MotionY)=pm*(PlayerX+MotionX)+pb],[pm,pb]),
        subst(solve([WallVert1Y=wm*WallVert1X+wb,WallVert2Y=wm*WallVert2X+wb],[wm,wb]),
            [IntersectionY=pm*IntersectionX+pb,IntersectionY=wm*IntersectionX+wb]
        )),
    [IntersectionX,IntersectionY]));

(%o) FindIntersection(PlayerX,PlayerY,MotionX,MotionY,WallVert1X,WallVert1Y,WallVert2X,WallVert2Y) :=
    [IntersectionX=-(MotionX*(WallVert1X*WallVert2Y-WallVert1Y*WallVert2X)
        +MotionX*PlayerY*(WallVert2X-WallVert1X)+MotionY*PlayerX
        *(WallVert1X-WallVert2X))/(MotionX*(WallVert1Y-WallVert2Y)
        +MotionY*(WallVert2X-WallVert1X)),
    IntersectionY=-(MotionY*(WallVert1X*WallVert2Y-WallVert1Y*WallVert2X)
        +MotionX*PlayerY*(WallVert2Y-WallVert1Y)+MotionY*PlayerX
        *(WallVert1Y-WallVert2Y))/(MotionX*(WallVert1Y-WallVert2Y)
        +MotionY*(WallVert2X-WallVert1X))]
\$\endgroup\$
7
  • \$\begingroup\$ What I understand from this is how to make a line equation from the player's motion, and how to do the same with the wall IF I know two points on the wall... or isn't it, do I miss something? Equations under "Applying the same technique to finding the slope and intercept of the wall yields:" \$\endgroup\$
    – Zhafur
    Commented Jan 7, 2014 at 12:22
  • \$\begingroup\$ Yes, use the vertices that start and end the line. --- The main focus here is that you need to know the exact point where the player hits the wall to make projection work. (This is because you add your projection vector to the intersection point to find your resolved position. And because you need to know exactly how far into the wall to resolve by) \$\endgroup\$
    – MickLH
    Commented Jan 7, 2014 at 14:12
  • \$\begingroup\$ You have formatted it as code which is a function, that function has 4 paremeters: player(the position of the player), motion(player's position + player x & y velocity), wall1(wall's start point), wall2(wall's end point). So at the end of the day, the function asks for 2 points on the wall, even if I found the intersect point, I need another one to make a vector out of it and then project the vector which is "after" the intersection aka in the wall onto the wall's vector. I still don't get how could I get that second point on the wall. \$\endgroup\$
    – Zhafur
    Commented Jan 7, 2014 at 17:16
  • \$\begingroup\$ Actually "motion" is velocity, don't add them up. --- Now, you must know what line you have hit since you have found out that your line of motion intersects with it. Usually people have the vertices of their line, perhaps you have instead the slope and intercept already? --- Another possibility is that you are using planes instead of lines (y <= m*x+b), in this case you still have slope-intercept, or you might have the surface normal. \$\endgroup\$
    – MickLH
    Commented Jan 7, 2014 at 18:36
  • \$\begingroup\$ So you say that I should hardcode every slope's start and end position? \$\endgroup\$
    – Zhafur
    Commented Jan 7, 2014 at 19:33

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .