以射线碰撞的方式来检测碰撞,当CXPhyOBJ的UpDate方法调用时根据物体的方向和传进来的时间计算出一个新的位置,如果不考虑任何情况新的位置就是物体应该更新的位置,可实际中有很多中情况产生,首先要分,物体是否在地面上,如果物体不在地面,还要受到重力的影响,有一个向下的速度,这样产生的预期位置就不一样了。在CXPhyOBJ中的Update方法如下:
void CXPhyOBJ::Update( float fPass )
{
if (mNeedInitGPoint)
{
InitGPoint();
mNeedInitGPoint=false;
return;
}
D3DXVECTOR3 Speed(mXPos.mSpeed);
D3DXVECTOR3 Pos(mXPos.mPos);
D3DXVECTOR3 NewPos;
//不在地面上
if (!mIsAtBlock)
{
Speed+=G*D3DXVECTOR3(0,-1,0)*fPass/1000.0f;
//本次要移动到的地方
NewPos=Pos+Speed*fPass/1000.0f;
bool bCanMove=CheckCanMove(&NewPos);
if (!bCanMove)
{
Speed=D3DXVECTOR3(0,0,0);
mIsAtBlock=true;
SetState(osOneLoopDone);
}else
{
}
mXPos.SetSpeed(Speed);
}else
{
if (D3DXVec3Length(&Speed)>0)
{
NewPos=Pos+Speed*fPass/1000.0f;
bool bCanMove=CheckCanMove(&NewPos);
D3DXVECTOR3 GP(GetGPoint());
D3DXVECTOR3 Dist=GP-NewPos;
if (D3DXVec3Length(&Dist)>0.1f) //判断按此方向前进后是否处于掉下去(不在地面)的状态
{
mIsAtBlock=false;
}else
{
mXPos.mSpeed=Vector(0,0,0);
}
}
}
}
当物体确定了将要移动到的位置上时,是否真正能移动的到还不一定,有可能物体从高处掉下到地面时就不能继续移动,有可能物体前面是高坡所以不能沿着高坡继续前进所以从物体现在的方位到要移动到的方位看这条线段和地图以及地图上的物体是否有产生碰撞,如果产生碰撞则把预计到达的点,改为碰撞到的点,由于物体的位置是和地面有接触的所以无论怎么碰撞都会相交,所以我们把位置提高一点点。调用方法为上面写的update中的CheckCanMove(D3DVECTOR* newPos),代码如下:
bool CXWorldOBJ::CheckCanMove( D3DXVECTOR3* NewPos )
{
bool bCan=true;
D3DXVECTOR3 Pos(mXPos.mPos);
D3DXVECTOR3 Normal(0,0,0);
if (mForcedMap)
{
Pos.y+=1.0f;
bCan=!((CMapCell*)mForcedMap)->GetBlockPoint(&Pos,NewPos,&Normal);
}
if (mObjState==osFalling)
{
mXPos.mPos=(D3DVECTOR)*NewPos;
mXPos.SetUponWithDir(D3DVECTOR(Normal));
return bCan;
}
D3DXVECTOR3 UP(0,1,0),Dir,DIR;
Dir=*NewPos-Pos;
D3DXVec3Normalize(&Dir,&Dir);
DIR=Dir;
DIR.y=0;
D3DXVec3Normalize(&DIR,&DIR);
if (Dir.y>0)
{
//D3DXVec3Cross(&DIR,&Right,&UP);
float angle=D3DXVec3Dot(&DIR,&Dir);
if (angle>0 && angle<cos(ANGLEFOROBJMOVE*D3DX_PI/180))
{
return false;
}
D3DXVECTOR3 Right(mXPos.mRight),RIGHT(mXPos.mRight);
RIGHT.y=0;
D3DXVec3Normalize(&RIGHT,&RIGHT);
angle=D3DXVec3Dot(&RIGHT,&Right);
if (angle>0 && angle<cos(ANGLEFOROBJMOVE*D3DX_PI/180))
{
return false;
}
}
if (mUWM==uwmMap)
{
mXPos.SetUponWithDir(D3DVECTOR(Normal));
}
mXPos.mPos=(D3DVECTOR)*NewPos;
return bCan;
}
此函数中的重点就是GetBlockPoint(D3DXVECTOR* targetPos, D3DXVECTOR* newPos, D3DXVECTOR* normal),他以targetPos和newPos的线段做射线碰撞看是否和地图以及地图上的物体有相交,以达到和场景中的所有物体检测碰撞,这都归功于合理的类的设计,本是核心的方法,却用了很少的代码和清晰的机构得以解决,因为无论是MapCell还是MapCell上的MeshObj都继承于BaseMeshOBJ,所以只需要在父类中设置此方法就ok了,而正是BaseMeshOBJ中包含了射线碰撞的方法CMeshBaseOBJ::CheckInterSect( D3DXVECTOR4 Pos,D3DXVECTOR4 Dir,bool CanComputerNormal,CInterSect *pInterSect )。GetBlockPoint函数代码如下:
bool CMeshBaseOBJ::GetBlockPoint( D3DXVECTOR3* Pos,D3DXVECTOR3* NewPos,D3DXVECTOR3* Normal)
{
bool bBlock=false;
D3DXVECTOR3 Dist=*NewPos-*Pos;
D3DXVECTOR4 fPos(*Pos),fDir;
fPos.w=1.0f;
D3DXVec3Normalize((D3DXVECTOR3*)&fDir,&Dist);
fDir.w=0.0f;
CInterSect Inter;
bBlock=CheckInterSect(fPos,fDir,Normal,&Inter);
if (bBlock)
{
float fLen=D3DXVec3Length(&Dist);
if (fLen>Inter.Dist)
{
bBlock=true;
*NewPos=*Pos+(D3DXVECTOR3)fDir*Inter.Dist;
if (Normal)
{
*Normal=Inter.FaceNormal;
}
}else
{
bBlock=false;
}
}
return bBlock;
}
现在的结构是每次都和地图上的所有物体做射线碰撞,之后还要完善,判断碰撞首先是用包围合来过滤,之后还要判断骨骼动画的碰撞,首先要把骨骼动画的缓存完成,这样使碰撞检测的效率大大提高。
浙公网安备 33010602011771号