物理模擬介紹
如果你很熟悉物理規律,并且想實現它,這篇文章很適合你。
在這篇教程里,你會創建一個非常簡單的物理引擎,我們將創建以下類:
內容:
位置類
* class Vector3D ---> 用來記錄物體的三維坐標的類
力和運動
* class Mass ---> 表示一個物體的物理屬性
模擬類
* class Simulation ---> 模擬物理規律
模擬勻速運動
* class ConstantVelocity : public Simulation ---> 模擬勻速運動
模擬在力的作用下運動
* class MotionUnderGravitation : public Simulation ---> 模擬在引力的作用下運動
* class MassConnectedWithSpring : public Simulation ---> 模擬在彈簧的作用下運動
class Mass
{
public:
float m; // 質量
Vector3D pos; // 位置
Vector3D vel; // 速度
Vector3D force; // 力
Mass(float m) // 構造函數
{
this->m = m;
}
...
下面的代碼給物體增加一個力,在初始時這個力為0
void applyForce(Vector3D force)
{
this->force += force; // 增加一個力
}
void init() // 初始時設為0
{
force.x = 0;
force.y = 0;
force.z = 0;
}
...
下面的步驟完成一個模擬:
1.設置力
2.應用外力
3.根據力的時間,計算物體的位置和速度
void simulate(float dt)
{
vel += (force / m) * dt; // 更新速度
pos += vel * dt; // 更新位置
}
模擬類怎樣運作:
在一個物理模擬中,我們按以下規律進行模擬,設置力,更新物體的位置和速度,按時間一次又一次的進行模擬。下面是它的實現代碼:
class Simulation
{
public:
int numOfMasses; // 物體的個數
Mass** masses; // 指向物體結構的指針
Simulation(int numOfMasses, float m) // 構造函數
{
this->numOfMasses = numOfMasses;
masses = new Mass*[numOfMasses];
for (int a = 0; a < numOfMasses; ++a)
masses[a] = new Mass(m);
}
virtual void release() // 釋放所有的物體
{
for (int a = 0; a < numOfMasses; ++a)
{
delete(masses[a]);
masses[a] = NULL;
}
delete(masses);
masses = NULL;
}
Mass* getMass(int index)
{
if (index < 0 || index >= numOfMasses) // 返回第i個物體
return NULL;
return masses[index];
}
...
(class Simulation continued)
virtual void init() // 初始化所有的物體
{
for (int a = 0; a < numOfMasses; ++a)
masses[a]->init();
}
virtual void solve() //虛函數,在具體的應用中設置各個施加給各個物體的力
{
}
virtual void simulate(float dt) //讓所有的物體模擬一步
{
for (int a = 0; a < numOfMasses; ++a)
masses[a]->simulate(dt);
}
...
整個模擬的部分被封裝到下面的函數中
(class Simulation continued)
virtual void operate(float dt) // 完整的模擬過程
{
init(); // 設置力為0
solve(); // 應用力
simulate(dt); // 模擬
}
};
現在我們已經有了一個簡單的物理模擬引擎了,它包含有物體和模擬兩個類,下面我們基于它們創建三個具體的模擬對象:
1. 具有恒定速度的物體
2. 具有恒定加速度的物體
3. 具有與距離成反比的力的物體
在程序中控制一個模擬對象:
在我們寫一個具體的模擬類之前,讓我們看看如何在程序中模擬一個對象,在這個教程里,模擬引擎和操作模擬的程序在兩個文件里,在程序中我們使用如下的函數,操作模擬:
void Update (DWORD milliseconds) // 執行模擬
這個函數在每一幀的開始更新,參數為相隔的時間。
void Update (DWORD milliseconds)
{
...
...
...
float dt = milliseconds / 1000.0f; // 轉化為秒
dt /= slowMotionRatio; // 除以模擬系數
timeElapsed += dt; // 更新流失的時間
...
在下面的代碼中,我們定義一個處理間隔,沒隔這么長時間,讓物理引擎模擬一次。
...
float maxPossible_dt = 0.1f; // 設置模擬間隔
int numOfIterations = (int)(dt / maxPossible_dt) + 1; //計算在流失的時間里模擬的次數
if (numOfIterations != 0)
dt = dt / numOfIterations;
for (int a = 0; a < numOfIterations; ++a) // 模擬它們
{
constantVelocity->operate(dt);
motionUnderGravitation->operate(dt);
massConnectedWithSpring->operate(dt);
}
}
下面讓我們來寫著兩個具體的模擬類:
1. 具有恒定速度的物體
* class ConstantVelocity : public Simulation ---> 模擬一個勻速運動的物體
class ConstantVelocity : public Simulation
{
public:
ConstantVelocity() : Simulation(1, 1.0f)
{
masses[0]->pos = Vector3D(0.0f, 0.0f, 0.0f); // 初始位置為0
masses[0]->vel = Vector3D(1.0f, 0.0f, 0.0f); // 向右運動
}
};
下面我們來創建一個具有恒定加速的物體:
class MotionUnderGravitation : public Simulation
{
Vector3D gravitation; // 加速度
MotionUnderGravitation(Vector3D gravitation) : Simulation(1, 1.0f) // 構造函數
{
this->gravitation = gravitation; // 設置加速度
masses[0]->pos = Vector3D(-10.0f, 0.0f, 0.0f); // 設置位置為左邊-10處
masses[0]->vel = Vector3D(10.0f, 15.0f, 0.0f); // 設置速度為右上
}
...
下面的函數設置施加給物體的力
virtual void solve() // 設置當前的力
{
for (int a = 0; a < numOfMasses; ++a)
masses[a]->applyForce(gravitation * masses[a]->m);
}
下面的類創建一個受到與距離成正比的力的物體:
class MassConnectedWithSpring : public Simulation
{
public:
float springConstant; // 彈性系數
Vector3D connectionPos; // 連接方向
MassConnectedWithSpring(float springConstant) : Simulation(1, 1.0f) // 構造函數
{
this->springConstant = springConstant;
connectionPos = Vector3D(0.0f, -5.0f, 0.0f);
masses[0]->pos = connectionPos + Vector3D(10.0f, 0.0f, 0.0f);
masses[0]->vel = Vector3D(0.0f, 0.0f, 0.0f);
}
...
下面的函數設置當前物體所受到的力:
virtual void solve() // 設置當前的力
{
for (int a = 0; a < numOfMasses; ++a)
{
Vector3D springVector = masses[a]->pos - connectionPos;
masses[a]->applyForce(-springVector * springConstant);
}
}
好了上面就是一個簡單的物理模擬,希望你能喜歡:)