『物體』運動核心是碰撞.你試想下當『物體』高速運動時有可能會穿越牆壁.這因遊戲世界中CPU會輪詢處理所有『物體』. 並只會分到有限『運算/時間』,所以每一次你都需要準確計算碰撞時間.通過不斷遞歸而確定最後移動位置
通過使用基於時間二維方程式進行碰撞檢測.
xf=x0+v0t+(1/2)at2
ax2+bx+c=0
利用係數替換可得到
a=(1/2)*a
b=v0
c=x0-xf
將其擴展可得到三維方程式
xt=cx+bxt+axt2
yt=cy+byt+ayt2
zt=cz+bzt+azt2
如果將三維方程應用於平面方程可得
Axt+Byt+C*zt+D=0
此方程說面如果物體與平面發生碰撞.其碰撞點位於(xt,yt,zt)通過代數運算可得到
(Aax+Bay+Caz)x2+(Abx+Bby+Cbz)x+(Acx+Bcy+Ccz)+D=0
這其實就是平面二次方程式(ax2+bx+c=0) ,其點積形式為:
ax2=(Aax+Bay+Caz)x2=N•(1/2)A
利用點積得到二次方程係數:
加速度係數: a=N•(0.5A)
速度係數: b=N•V
距離係數: c=N•X+D
有上序二次方程係數就可計算碰撞時間
float a = n % (acceleration * 0.5f);// 加速度係數
float b = n % velocity;// 速度係數
float c = n % position + D – radius;// 距離係數
若a=0加速度係數為零,碰撞時間等於距離係數除以速度係數.因為距離係數小於零所以要去負值.
collisionTime = -c/b;
若a!=≠0加速度係數不為零,可以使用二次方程計算碰撞時間:
x=(-b+√(b2-4ac) ) / 2a
在計算碰撞時間之前可先計算D=b2-4ac 若D小於零則無解.若D大於零則有解
float D = b * b – 4 * a*c;
if (D > 0) // 如果判斷式大於等於零
collisionTime = (-b – sqrtf(D)) / (2 * a); // 計算碰撞時間
因為發生碰撞物體會彈開,方向矢量產生返射需要重新計算位置.通過減去碰撞時間進行遞歸.直到『幀時間』為零
下面是『曲棍球』碰撞函式.
puck 是曲棍球.
table 是球臺
deltaTime是幀時間
void Update_Puck(PUCK_PTR puck, TABLE_PTR table, float deltaTime){
float fastestTime = deltaTime;// 最早碰撞時間
float collisionTime;// 碰撞時間
PLANE3D_PTR plane = NULL;// 平面
PLANE3D_PTR planeCollision = NULL;// 碰撞平面
if (deltaTime <= 0.000f)// 遞歸
return;
// 對球臺四個圍邊檢查碰撞
for (int i = 0; i < 4; i++){
plane = &table->wall[i];// 平面
// 點積二次方程
float a = plane->n % (puck->acceleration * 0.5f);// 加速度係數
float b = plane->n % puck->velocity;// 速度係數
float c = plane->n % puck->position + plane->D – puck->radius;// 距離係數
if (a == 0 && b != 0 && c != 0){// 如果無加速度
// 碰撞時間等於距離除以速度
collisionTime = -c/b;// 碰撞時間
if (collisionTime >= 0 && collisionTime < fastestTime){// 發生碰撞
fastestTime = collisionTime;// 保存碰撞時間
planeCollision = plane;// 平面
}
}
else
if(a != 0){
// 加速度a不等於零
// 計算判別式
float D = b * b – 4 * a*c;
if (D > 0) {// 如果判斷式大於等於零
// 計算碰撞時間
collisionTime = (-b – sqrtf(D)) / (2 * a);
if (collisionTime >= 0.0f && collisionTime < fastestTime) {// 發生碰撞
fastestTime = collisionTime;
planeCollision = plane;
}
}
}
}// END FOR
// 速度設上限每秒800米
if (Length_VECTOR3D(&puck->velocity) > 800)
puck->velocity = Normalize_VECTOR3D(&puck->velocity) * 800;
// 如果冰球正移動,則應用磨擦力
if (Length_VECTOR3D(&puck->velocity) > 0)
puck->acceleration = -puck->velocity * 0.2f;// 計算加速度
// 計算當前位置
puck->position = puck->position + puck->velocity * fastestTime + puck->acceleration * (fastestTimefastestTime0.5f);
// 應用磨擦力
puck->velocity = puck->velocity + puck->acceleration * fastestTime;
// 如果發生碰撞,反轉速度
if (planeCollision != NULL)// 碰撞平面
puck->velocity = Reflection_VECTOR3D(&puck->velocity, &planeCollision->n);
// 遞歸調用
Update_Puck(puck, table, deltaTime – fastestTime);
}