【问题标题】:C++ game missile collision detectionC++游戏导弹碰撞检测
【发布时间】:2017-09-24 04:18:09
【问题描述】:

我正在重新创建经典游戏 Asteroids,但在让碰撞检测正常工作方面遇到了一些问题。目前,下面的代码运行良好,但由于某种原因,它只会检测到最后发射的导弹上的碰撞。我的意思是,如果我向静止的小行星直线发射 3 枚导弹,前两枚将穿过小行星,而只有最后一枚会与它相撞并按预期行动。我不太确定为什么会发生这种情况,但我认为这与遍历导弹列表有关。

这是用于检测碰撞的部分工作函数:

void checkMissileAsteroidCollision(struct particle *ast, struct particle *mis)
{
        float missileX = 0;
        float missileY = 0;

        float asteroidX = 0;
        float asteroidY = 0;
        float asteroidSize = 0;

    for( ; mis ; mis = mis->next ) 
    {
            missileX = mis->x;
            missileY = mis->y;

        for( ; ast ; ast = ast->next)
        {
            asteroidX = ast->x;
            asteroidY = ast->y;
            asteroidSize = ast->size;

            if(missileX < asteroidX + asteroidSize &&
                 missileX > asteroidX &&
                 missileY < asteroidY + asteroidSize &&
                 missileY > asteroidY && mis->draw == 1 && ast->draw == 1)
            {
                collisionCounter++;
                ast->draw = 0;
                mis->draw = 0;

            }
        }

    }
}

粒子结构;

typedef struct particle{
    float x;
        float y;
    float vx;
        float vy;
        float heading;
    float dur;
        int size;
        int draw;
    struct particle *next;
} node_t;

我在该链接下方包含了两个链接,指向整个源文件及其代码来源的标题,以防需要任何其他信息来找出发生这种情况的原因。

gamestate.cpp:https://gist.github.com/Jakemangan/d286ef1f9159e6682778ec2e0daf516c

gamestate.h:https://gist.github.com/Jakemangan/6c3788a46825a86cd0e2642864fcad68

【问题讨论】:

  • 这样的列表处理在 C++ 中是一种非常糟糕的做法。您应该改用标准容器,例如std::vector。您作为助手引入的变量也应该是 const 并在首次分配时声明,而不是在函数顶部。至于您的实际问题,这里没有足够的信息来重现它。尝试编写一个单元测试来传递一些示例数据并检查结果。
  • @RonE 这需要的不仅仅是一条评论,但是,按照随机顺序 - 它容易出错,难以阅读(正如您刚刚演示的那样),难以维护,难以使用(写),它不是样式 - 它是生成的解决方案的基本功能和正确性。您也无法从任何现有算法中受益。
  • @RonE 按顺序排列-那么您在这里一定不是很活跃,不(这就是您阅读失败的原因,这会有所帮助-请参阅我的回答),不(容器不是“高级”),不。结构体的接口混合了存储和数据模型的关注点是普遍可怕的。
  • 并且说清楚 - 如果我们在谈论 C,我的答案和反应会完全不同。但鉴于现代 C++ 工具集和目标,将这种原始方法强加给初学者是非常有害的。
  • @RonE 我也可以投诉!我目前正在维护一个遗留系统,其中自定义列表不是通用的,只能通过指针链接 - 这意味着调试工具更难用于检查数据,并且不同自定义列表有多个不同版本 - 我*MUCH*(我似乎无法足够强调这个词)宁愿为这些数据结构提供标准容器!

标签: c++ collision detection


【解决方案1】:

这就是更现代的 C++ 版本的样子:

// drop the pointer from the struct

void checkMissileAsteroidCollision(std::vector<particle>& asteroids, std::vector<particle>& missiles)
{
    for(auto & mis : missiles) 
    {
        for(auto & ast : asteroids)
        {
            const auto missileX = mis.x;
            const auto missileY = mis.y;
            const auto asteroidX = ast.x;
            const auto asteroidY = ast.y;
            const auto asteroidSize = ast.size;

            if(missileX < asteroidX + asteroidSize &&
                 missileX > asteroidX &&
                 missileY < asteroidY + asteroidSize &&
                 missileY > asteroidY && mis.draw == 1 && ast.draw == 1)
            {
                collisionCounter++;
                ast.draw = false;
                mis.draw = false;
            }
        }
    }
}

然后实例创建和使用:

std::vector<particle> asteroids { { 0, 0, 0, 0, 0, 0, 10, true } };
std::vector<particle> missiles { { 5, 5, 0, 0, 0, 0, 10, true } };

checkMissileAsteroidCollision(asteroids, missiles);

std::cout << std::boolalpha << asteroids[0].draw;

这里有更多的改进(封装在一个类中,不使用参数来改变,不使用draw作为碰撞标志),但我相信这些属于代码审查,我鼓励你在那里发布你的解决方案一旦你让它工作。

【讨论】:

  • 所有这些自动增加编译时间,并增加误导。这对我来说实际上比原版更难阅读。我还可以创建一个使用迭代器的自定义链表,并利用 foreach 循环,所以没什么特别的。否则是一样的。
  • @RonE 没有数据支持这一点,但如果在这里使用auto 会以任何方式增加编译时间,我会感到非常惊讶。编译器无论如何都需要检查分配,所以它需要知道 RHS 的类型。
  • 如果 自动的类型 很重要,您还需要知道分配右侧的类型(如果您正在调用自定义转换运算符)。因此,如果没有auto,您仍然具有相同的有效间接级别、检查文档或代码以查找该类型的相同要求,但是如果您更改该类型,则会产生额外的耦合。
  • 不,声明了 LHS 类型后,您就知道局部变量是什么类型了。要知道分配的作用,您需要知道 RHS 类型,这仍然意味着查找它。我的意思是,如果局部变量的类型非常重要,那么 RHS 的类型很可能也非常重要,而您并没有从额外的输入中获得任何好处。
  • 同样,即使你声明了 LHS 类型,编译器也需要知道 RHS 类型,然后 it 才能弄清楚赋值的作用。所以,那里也没有保存。您唯一添加的是关于 RHS 是什么类型的假设,这可能是(或变成)错误的 - 如果存在隐式转换,它可能是一个无声的错误。
【解决方案2】:
for( ; ast ; ast = ast->next)

这一行是问题所在。此循环终止后,ast 为 null,但不会为外部 mis 循环的下一次迭代重置。这意味着只有 mis 循环的第一次迭代才能真正看到任何小行星。

要更正此问题,请为此循环声明一个新变量:

for(struct particle *ast2 = ast ; ast2 ; ast2 = ast2->next)

(或者选择一个比ast2 更好的名称。)然后,显然,在循环体中将ast 替换为ast2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多