由于3D引擎平台的限制,还没办法在场景中检测所有的碰撞检测。只能使用之前的小车碰撞检测的场景,这项工作总算告一段落,闲来无事,将过程记录下来~
1、由于工具的限制,无法获取到当前选取模型投影在二维平面上的矩形长宽值(PS:这里所做的碰撞检测均采取的是三维模型投影到二维平面上,并使用框选模型得到所有的投影均为矩形区域),这里只能采取手动测量矩形的长宽值。
2、针对场景中的静止不变的物体,如厂房的墙壁、柱子等,也是采取手动测量出墙壁、柱子在二维平面上的投影矩形,针对场景中其他的模型,采取如1相同的方法测量出投影矩形区域。将这些矩形区域都设置为碰撞矩形区域。通过检测模型矩形的四个顶点是否进入碰撞矩形区域来判断模型是否发生碰撞。
3、编写模型移动代码(按下键盘'1'、'2'键来对模型进行前后两个方向移动)代码如下:
#define FORWARDKEY '1'
#define BACKWARDKEY '2'
bool isFowardKeyDown = false, isBackWardKeyDown = false;
按键前的操作:
void CXX::OnSelect() //此按钮为按下键盘上'1'、'2'键前的准备工作
{
CMDIFrameWnd *pFrame=(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
CMDIChildWnd *pChild=pFrame->MDIGetActive();
CXXXView *pView=(CXXXView*)pChild->GetActiveView();
//必不可少,因为这个问题困扰了好长时间,如果没有下面两条语句,将无法进入PreTranslateMessage函数
CWnd *pwnd = GetDlgItem(IDC_SELECT);
pwnd->SetFocus();
pView->m_conver.SwitchCamera(DRIVECAMERA, 0);
truckNowPosX = pView->m_conver.GetObjectPosX("truck");
truckNowPosZ = pView->m_conver.GetObjectPosZ("truck");
angle_Z = pView->m_conver.GetYawAngle("truck");//得到相对于z轴反方向的角度
//相机追踪
cameraFallow = true;
if(cameraFallow)
pView->m_conver.SetCurMVCameraParam(truckNowPosX, 0, truckNowPosZ,DRIVECAMERAPANGLE,
(float)(180-(angle_Z*180/3.14)),DRIVECAMERARADIU, 0);
isCameraArrayInited = false;//点击开始驾驶,数组没有初始化
i = 0;
j = 0;
SetTimer(1, timerEclapse, 0);
}
void CXX::OnTimer(UINT nIDEvent)
{
CMDIFrameWnd *pFrame=(CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
CMDIChildWnd *pChild=pFrame->MDIGetActive();
CXXXView *pView=(CXXXView*)pChild->GetActiveView();
if(isFowardKeyDown && nowdx < dx)
{
nowdx += ACCELERATION;//加速
if(nowdx > dx)
nowdx = dx;
}
if(isBackWardKeyDown && nowdx > -dx)
{
nowdx -= ACCELERATION;
if(nowdx < -dx)
nowdx = -dx;
}
if(!isFowardKeyDown && !isBackWardKeyDown && nowdx != 0)//前后键都没按下,则减速
{
if(nowdx > 0)
{
nowdx -= ACCELERATION;
if(nowdx < 0)
nowdx = 0;
}
else
if(nowdx < 0)
{
nowdx += ACCELERATION;
if(nowdx > 0)
nowdx = 0;
}
}
angle_Z = pView->m_conver.GetYawAngle("truck");//得到相对于z轴反方向的角度
angleX = angle_Z - half_PI;//angleX是相对与x轴正方向的角度
truckNowPosX = pView->m_conver.GetObjectPosX("truck");
truckNowPosZ = pView->m_conver.GetObjectPosZ("truck");
truckPrePosX = truckNowPosX;
truckPrePosZ = truckNowPosZ;
//下2句计算新位置
truckNowPosZ += (float)(nowdx*sin(angleX));
truckNowPosX += (float)(-nowdx*cos(angleX));
if(i < TRUCKSHAKEFREQUENCY/2)//制造小车抖动效果
{
truckHight = -20;
i++;
}
else
{
truckHight = -20.2f;
i++;
if(i >= TRUCKSHAKEFREQUENCY)
i = 0;
}
//下面做碰撞检测,小车的前后左右4点
br.x = (int)(truckPrePosX + HALFTRUCKWIDTH*sin(angleX));
br.y = (int)(truckPrePosZ - HALFTRUCKWIDTH*cos(angleX));
bl.x = (int)(truckPrePosX - HALFTRUCKWIDTH*sin(angleX));
bl.y = (int)(truckPrePosZ + HALFTRUCKWIDTH*cos(angleX));
fr.x = (int)(br.x - TRUCKLEN*cos(angleX));
fr.y = (int)(br.y + TRUCKLEN*sin(angleX));
fl.x = (int)(bl.x - TRUCKLEN*cos(angleX));
fl.y = (int)(bl.y + TRUCKLEN*sin(angleX));
//向前移动则检测前2点,向后移动则检测后2点,以免转弯导致后部卡主,不能前移
moveFlag = (nowdx > 0 && !IsCollided(fr) && !IsCollided(fl))
|| (nowdx < 0 && !IsCollided(br) && !IsCollided(bl));
if(moveFlag)//没有碰撞小车才能继续移动
{
pView->m_conver.SetObjectPosition("truck", truckNowPosX, truckHight, truckNowPosZ);
if(isCameraArrayInited && cameraFallow)//如果初始化完毕且相机追踪打开则相机跟踪运动
pView->m_conver.SetCurMVCameraParam(truckNowPosX, 0, truckNowPosZ,
DRIVECAMERAPANGLE,
turn?(float)(180-(truckCameraArray[0][j]*180/3.14)) : (180-angle_Z),
DRIVECAMERARADIU, 0);
}
truckCameraArray[0][j] = angle_Z;//相机视角延迟,制造转弯效果
j++;
if(j >= 5)//镜头延迟效果
{
j = 0;
if(!isCameraArrayInited)
{
isCameraArrayInited = true;
}
}
dangle = (float)(nowdx*sin(angle)/WHEELDISTANCE/2);//角度计算
if(moveFlag)//有移动才能转弯
pView->m_conver.YawObject("truck", dangle);
CFormView::OnTimer(nIDEvent);
}
BOOL CXX::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN) //按下前进或后退键
{
switch(pMsg->wParam)
{
case FORWARDKEY:
isFowardKeyDown = true;
break;
case BACKWARDKEY:
isBackWardKeyDown = true;
break;
case MOVELEFT:
isLeftKeyDown = true;
case MOVERIGHT:
isRightKeyDown = true;
}
}
else
{
if (pMsg->message == WM_KEYUP) //松开前进或后退键
{
switch(pMsg->wParam)
{
case FORWARDKEY:
isFowardKeyDown = false;
break;
case BACKWARDKEY:
isBackWardKeyDown = false;
break;
case MOVELEFT:
isLeftKeyDown = true;
case MOVERIGHT:
isRightKeyDown = true;
}
}
}
return CFormView::PreTranslateMessage(pMsg);
}
4、设置碰撞区域和判定碰撞函数:
bool moveFlag;
const float half_PI = 1.57f;
const int areaCenter = (int)(WORKSHOPSIDELEN/2);
bool flagCollision[WORKSHOPSIDELEN][WORKSHOPSIDELEN];//以[750][750]为厂房中心,记录每平方米内是否有物体
float truckNowPosX, truckNowPosY, truckNowPosZ;//小车当前位置
float truckPrePosX, truckPrePosY, truckPrePosZ, angle_Z, angleX;//angle_Z是车头相对于z轴负方向的角度,也是sdk获得的角度
CPoint fl, fr, bl, br;//小车的四个角的点,用做碰撞检测
//在初始化函数中初始化碰撞矩阵
memset(flagCollision, 0, sizeof(flagCollision));
//设置碰撞矩阵区域
void CXX::SetCollisionArea(int x, int y, int dx, int dz)
{
int i, j;
for(i = x + areaCenter; i < x + areaCenter + dx; i++)
for(j = y + areaCenter; j < y + areaCenter + dz; j++)
if(i >= 0 && j >= 0 && i < WORKSHOPSIDELEN && j < WORKSHOPSIDELEN)//防止越界
flagCollision[i][j] = true;//给定区域的flag置为真
}
//检测是否碰撞
bool CXX::IsCollided(CPoint p)
{
return flagCollision[p.x + areaCenter][p.y + areaCenter];//检测是否矩阵中的点是否为真来判断碰撞
}