开发者

Sliding Response after a Point-Square Collision

In general terms and pseudo-code, what would be the best way to have a collision response of sliding along a wall if the wall is actually just a part of an entire square that a point is colliding into? The collision test method used is a test to see if the point lies in the square.

Should I divide the square into four lines and just calculate the开发者_如何转开发 shortest distance to the line and then move the point back that distance?If so, then how can I determine which edge of the square the point is closest to after collision?


Detect the collision point by testing the movement vector against the wall. If you know things about your surface (e.g. as you say it's part of a box) you might be able to test multiple walls at once.

The solution can be slightly different between 2D and 3D. I'll go with 2D since you're talking "square" rather than "cube" or "box".

Once you know where your point is hitting, you take the remainder of your movement vector, dot it against the wall direction (subtract one point on the wall from another then normalize) then scale the wall direction by that amount. This is the amount of motion that would be parallel to the wall, assuming no friction.

Edit added the following code:

boilerplate:

import math

class Vector2d:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, rhs):
        return Vector2d(self.x + rhs.x, self.y + rhs.y)

    def __sub__(self, rhs):
        return Vector2d(self.x - rhs.x, self.y - rhs.y)

    def GetScaled(self, scale):
        return Vector2d(self.x * scale, self.y * scale)

    def GetLength(self):
        return math.sqrt((self.x * self.x) + (self.y * self.y))

    def GetNormalized(self):
        return self.GetScaled(1.0 / self.GetLength())

def DotProduct(v0, v1):
    return (v0.x * v1.x) + (v0.y * v1.y)

The real business:

class Wall2d:
    def init(self, point0, point1):
        """point0, point1 are Vector2ds"""
        self.p0 = point0
        self.p1 = point1

        # these three steps could be combined to optimize, but
        # for demonstration are left explicit
        self.dir = self.p1 - self.p0
        self.length = self.dir.GetLength()
        self.dir = self.dir.GetNormalized()

        # computing the normal in 3D would require three points
        # on the face and a cross product
        self.normal = Vector2d(self.length.y, -self.length.x)

    def LineSegmentCollides(self, pointStart, pointEnd):
        startDot = DotProduct(pointStart - self.p0, self.normal)
        endDot = DotProduct(pointEnd - self.p0, self.normal)
        if startDot * endDot < 0:
            # the only way a collision can occur is if the start
            # and end are on opposite sides of the wall, so their
            # dot product results will have opposite signs, so
            # the result of the multiplication is negative
            moveVector = pointEnd - pointStart

            # scale the movement vector by the ratio of the move
            # vector on the "start" side versus the total length
            # of the movement in the axis of the normal
            collisionDelta = moveVector.GetScaled(startDot /
                                                  (startDot + endDot))
            collisionPoint = pointStart + collisionDelta

            collisionDot = DotProduct(collisionPoint - self.p0, self.dir)
            if (collisionDot > 0) && (collisionDot < self.length):
                # we've hit the wall between p0 and p1 (other
                # values of collisionDot mean we missed on one
                # end or the other)

                # now, collision response is up to you.  In this
                # case, we'll just zero out the movement in the
                # direction of the wall after the collision
                # (sorry about the poor naming)
                # note that we don't actually care about the actual
                # point of collision here.
                collisionPushBack = moveVector.GetScaled(
                                         endDot / (startDot + endDot))
                endPoint = pointEnd + collisionPushBack

                return True
        return False

I hope that is useful.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜