C++ 软体引擎

AvZ*_*AvZ 5 c++ physics game-physics sdl-2 softbody

我正在尝试使用 SDL2 用 C++ 制作一个基本的软体引擎。它的工作原理是考虑软体的所有顶点都由相同长度和刚度的弹簧互连(具有相同的弹簧常数k和长度natural_length)。为了使其更加真实,我还引入了阻尼常数c
然而,我遇到了一个令人沮丧的问题。过去 6-7 个小时我一直在尝试调试它,但没有成功。软体遇到很多奇怪的我不明白的bug

  • 首先,“软体”一点也不“软”。每次它都会变成一团皱巴巴的乱七八糟的点。我尝试只计算相邻点的力,但它仍然变得一团糟。
  • 即使我没有施加任何外力,软体每次都会飞到顶角(原点)。

这两个错误在此图像中都可见 - 漏洞

以下 2 个函数(它们与所有变量属于同一类,因此不需要接受任何参数)是代码的实际模拟部分。(我省略了其余的代码,因为这是不必要的。)
我使用vectorofSDL_Points来存储每个点,并使用vectorofVector来存储它们的速度。如果您想知道它Vector是什么,它只是一个structI 创建的,它只有 2 个float成员xy
acceleratePoints()函数为每个点分配速度和位置,并checkCollision()很好地检查与窗口墙壁的碰撞,窗口的宽度scr_w和高度为scr_h

    void acceleratePoints()
    {
        vector<SDL_Point> soft_body_copy=soft_body;
        vector<Vector> velocity_copy=velocity;
        for(int i=0;i<soft_body.size();++i)
        {
            for(int j=0;j<soft_body.size();++j)
            {
                if(i!=j)
                {
                    Vector d={(soft_body[j].x-soft_body[i].x)/100.0,(soft_body[j].y-soft_body[i].y)/100.0};
                    float t=atan2(d.y,d.x);
                    float disp=fabs(magnitude(d))-natural_length/100.0;
                    velocity_copy[i].x+=(k*disp*cos(t))/10000.0;
                    velocity_copy[i].y+=(k*disp*sin(t))/10000.0;
                    velocity_copy[i].x-=c*velocity_copy[i].x/100.0;
                    velocity_copy[i].y-=c*velocity_copy[i].y/100.0;
                    soft_body_copy[i].x+=velocity_copy[i].x;
                    soft_body_copy[i].y+=velocity_copy[i].y;
                }
            }
            soft_body=soft_body_copy;
            velocity=velocity_copy;
        }
    }
    void checkCollision()
    {
        for(int k=0;k<soft_body.size();++k)
        {
            if(soft_body[k].x>=scr_w||soft_body[k].x<=0)
            {
                velocity[k].x*=e;
                soft_body[k].x=soft_body[k].x>scr_w/2?scr_w-1:1;
            }
            if(soft_body[k].y>=scr_h||soft_body[k].y<=0)
            {
                velocity[k].y*=e;
                soft_body[k].y=soft_body[k].y>scr_h/2?scr_h-1:1;
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

magnitude()函数返回 a 的大小Vector。我在图像中使用的恢复系数、阻尼常数和弹簧常数
的值分别为 0.5、10 和 100。感谢您抽出时间来阅读!帮助将不胜感激。eck

编辑

如果有人想测试的话,这是完整的代码。您将需要 SDL 和文件夹“img”,其中“img/point.bmp”中有一个“.bmp”文件。

def*_*ube 4

基于弹簧的软体模拟需要“静止配置”,其中没有重力或其他外力,身体将保持内部静止。

模拟中的每个弹簧都需要有自己的长度。共享natural_length将导致弹簧在体内施加力,其中连接点之间的输入间隔不同,这似乎是导致您描述的问题的原因。

从一组点中生成软体(或“点斑点”)的典型方法如下所示:

  • 生成点集的 Delaunay 三角剖分(或 3D 中的四面体化)。
  • 在三角剖分的每条边上创建弹簧,使用边的长度作为弹簧的静止长度。
  • 模拟“重力”等外力!

为简单起见,您可以仅连接所有点对,而不是生成三角测量。

模拟本身可以分三个步骤进行:

  • 清除顶点力累积器
  • 将弹簧力添加到其连接的顶点
  • 更新每个顶点的速度和位置

本着您发布的代码的精神,aSpring可能看起来像:

struct Spring
{
    int point_index[2];
    float rest_length;
};
Run Code Online (Sandbox Code Playgroud)

在旁边

在您的代码中,您可以将t = atan2(d.y, d.x)和 以下内容替换sin(t) cos(t)为 的单位长度图像d

float mag_d = magnitude(d); // should not become negative...
float r = (0.0f != mag_d) ? 1.0f/mag_d : 0.0f;
Vector d_hat{d.x*r, d.y*r};
float w = k * (mag_d - spring[i].rest_length);
velocity_copy[i].x += w*d_hat.x;
velocity_copy[i].y += w*d_hat.y;
Run Code Online (Sandbox Code Playgroud)

没有完全优化,只是更稳定一点。

编辑

我还注意到您正在缩放剩余长度(将其除以 100)。不要那样做。

另一个编辑

改变这个:

    }
    soft_body=soft_body_copy;
    velocity=velocity_copy;
}
Run Code Online (Sandbox Code Playgroud)

对此:

    }
}
soft_body = move(soft_body_copy);
velocity = move(velocity_copy);
Run Code Online (Sandbox Code Playgroud)

您在计算力时改变了顶点位置。

  • 利用它们之间的距离。你需要一个单独的弹簧结构集合;每个都存储其端点的索引及其长度。当主体更新时,最简单的事情是为每个现有点添加一个新弹簧,所有弹簧都连接到新点(首先插入新点以获取其索引,然后构造新弹簧)。 (2认同)
  • @AvZ 在处理模拟代码时不要担心单位或比例。可以使用单独的变换来缩放屏幕空间坐标。 (2认同)