📜  2d 矢量推回 - C++ (1)

📅  最后修改于: 2023-12-03 14:59:04.643000             🧑  作者: Mango

2D 矢量推回 - C++

简介

矢量推回是一种常见的2D游戏开发技术,用于处理游戏中物体的运动方向。在游戏中,物体的位置通常表示为x和y坐标。在每一帧中,物体都会移动一定的距离,这个距离通常表示为速度向量。但是,我们还需要处理物体的碰撞和反弹的情况。为此,我们需要实现一个函数来处理矢量推回。

在本文中,我们将介绍如何在C++中实现2D矢量推回算法。我们将从基本的向量和点操作开始,并介绍3种常用的碰撞检测算法。最后,我们将把所有这些概念结合起来,编写一个完整的矢量推回函数。

基本的向量和点操作

在开始矢量推回前,我们需要了解一些基本的向量和点操作。在C++中,我们通常使用类表示向量和点。以下是Vector和Point类的典型实现:

class Vector {
public:
    double x, y;

    Vector(double x = 0, double y = 0) : x(x), y(y) {}

    // 向量加法
    Vector operator +(const Vector& rhs) const {
        return Vector(x + rhs.x, y + rhs.y);
    }

    // 向量减法
    Vector operator -(const Vector& rhs) const {
        return Vector(x - rhs.x, y - rhs.y);
    }

    // 向量数乘
    Vector operator *(double k) const {
        return Vector(x * k, y * k);
    }

    // 向量点乘
    double operator *(const Vector& rhs) const {
        return x * rhs.x + y * rhs.y;
    }

    // 向量叉乘
    double operator ^(const Vector& rhs) const {
        return x * rhs.y - y * rhs.x;
    }

    // 向量长度
    double length() const {
        return sqrt(x * x + y * y);
    }

    // 向量单位化
    Vector normalize() const {
        double len = length();
        return Vector(x / len, y / len);
    }

    // 向量旋转
    Vector rotate(double angle) const {
        double c = cos(angle), s = sin(angle);
        return Vector(x * c - y * s, x * s + y * c);
    }
};

class Point {
public:
    double x, y;

    Point(double x = 0, double y = 0) : x(x), y(y) {}

    // 点加向量
    Point operator +(const Vector& rhs) const {
        return Point(x + rhs.x, y + rhs.y);
    }

    // 点减向量
    Point operator -(const Vector& rhs) const {
        return Point(x - rhs.x, y - rhs.y);
    }

    // 点减点
    Vector operator -(const Point& rhs) const {
        return Vector(x - rhs.x, y - rhs.y);
    }
};

这些操作包括向量加法、向量减法、向量数乘、向量点乘、向量叉乘、向量长度、向量单位化和向量旋转。此外,我们还实现了点加向量、点减向量和点减点的操作。这些操作将在接下来的章节中用于碰撞检测算法。

碰撞检测算法

现在我们来介绍3个常用的碰撞检测算法。这些算法可以检测不同类型的碰撞,包括点与线段的碰撞、点与圆的碰撞和圆与圆的碰撞。

点与线段的碰撞

点与线段的碰撞可以用以下函数来判断:

bool PointOnSegment(const Point& p, const Point& a, const Point& b) {
    return dcmp((p - a) ^ (b - a)) == 0 && dcmp((p - a) * (p - b)) <= 0;
}

其中,Point表示点,a和b表示线段的两个端点,dcmp函数用于比较两个double类型的数值是否相等。这个函数的实现见后面的代码。

该函数的流程如下:如果点p在直线ab上,则向量AP和向量AB共线,即它们的叉乘为0,因此判断(p-a)^(b-a)是否等于0。然后,我们需要判断点p是否在线段ab上,即向量AP和向量AB的点积是否小于等于0。如果这两个条件都成立,则表示点p在线段ab上。

点与圆的碰撞

点与圆的碰撞可以用以下函数来判断:

bool PointInCircle(const Point& p, const Point& o, double r) {
    return dcmp((p - o).length() - r) < 0;
}

其中,Point表示点,o表示圆心,r表示半径。如果点p到圆心的距离小于半径,则表示点p在圆内。

圆与圆的碰撞

圆与圆的碰撞可以用以下函数来判断:

bool CircleCircleIntersection(const Point& c1, double r1, const Point& c2, double r2) {
    return dcmp((c1 - c2).length() - (r1 + r2)) <= 0;
}

其中,c1和c2表示两个圆心,r1和r2表示两个圆的半径。如果两个圆心之间的距离小于等于两个半径之和,则表示两个圆相交。

矢量推回

上述的基本向量和点操作以及碰撞检测算法为我们提供了实现2D矢量推回算法所需的所有工具。下面,我们将这些概念结合起来,编写一个完整的矢量推回函数:

void VectorPushBack(const Point& p1, const Point& p2, double r1, double r2, Vector& v) {
    Vector n = (p2 - p1).normalize(); // 求线段方向
    Vector w = n.rotate(PI / 2); // 求法向量
    Point c1 = p1 + w * r1; // 求圆心
    Point c2 = p2 + w * (-r2); // 求圆心

    if (CircleCircleIntersection(c1, r1, c2, r2)) {
        // 两个圆相交,需要进行矢量推回
        double d = (c2 - c1).length(); // 两个圆心之间的距离
        double alpha = acos((r1 + r2) / d); // 碰撞角
        Vector u = (c2 - c1).normalize().rotate(alpha); // 碰撞方向

        v = v - 2 * (v * u) * u; // 矢量推回
    }
}

其中,p1和p2表示两个攻击范围,r1和r2表示两个攻击范围的半径,v表示速度矢量。此函数的流程如下:

  1. 计算出攻击范围与速度矢量的交点,并求出法向量w和两圆心c1和c2。
  2. 判断两个圆是否相交,如果不相交则无需进行矢量推回。
  3. 计算出碰撞角alpha和碰撞方向u。
  4. 进行矢量推回,计算v' = v - 2*(v*u)*u。

这样,当速度矢量与攻击范围相交时,我们将会通过矢量推回算法,使其反弹回去。

总结

本文介绍了2D矢量推回算法及其实现方式。我们从基本的向量和点操作开始,介绍了3个常用的碰撞检测算法,并将它们应用于矢量推回算法中。希望本文能帮助您更好地掌握这一技术,并在游戏开发中得到实践。