刚终于解决了摄像机的问题,写个文交流交流先~
做法其实蛮简单,场景是PhysX(部分用的NxOgre)的TriangleMesh + Ogre新地形,可视部分建筑物可耻的用了天龙八部的资源,然后判断摄像头的位置用PhysX的raycastClosestShape()。人物移动用的PhysX的CC,可视部分用的Ogre源DEMO-Sinbad那个,改了改~
基本思路也就是一个SceneNode在BodyNode头上面一点(我这儿没有用传说中的五点检测法,感觉太费时,不过一个点的效果也蛮好~),然后SceneNode子节点一个不做任何碰撞检测与不挂接摄像机的节点做射线检测方向,由头上面那个SceneNode引射线至其子节点,检测最近的碰撞点,然后把另一个挂接了Camera的SceneNode移到碰撞点并LookAt头上那个SceneNode。同时,为了保证不穿墙且不看到截面,最好是能附带检测Frustum的四个边点。然后确保当无视线阻挡时能自动恢复,需要判断hit.distance > length,反正就是魔兽那效果啦~ 摄像机位置回复时最好能根据时间插值,看上去舒服点。
需要注意的是:
1.节点的获取位置函数问题,对于无parent的节点,getPosition()是获取的当前帧改变后的位置值,而_getDerivedPosition()需要render一次后才更新;对于有parent的节点,getPosition()获取的是该帧修改后的相对值,而_getDerivedPosition()是获取的未更新的世界坐标。记得要获得子节点的当前帧最新位置,要用_update(true,true);
2.小心转向角的问题,把Pitch和Yaw的角度可得锁好了,不然到时会很乱,最好能像魔兽世界那样,摄像机动,人也动,若处于摄像机拖着浏览人物周身状态,按下行走键时马上恢复到背对状态~
3.又优化了几天,发现一些小问题,比如人物在死角的时候摄像头的位置处理,我仿魔兽的将摄像头坐标直接设在了人物身上,然后朝对面看;比如当离开阻挡物后视角的缓慢恢复;比如有些小穿透的地方。。。看代码吧~很多诡异的小问题,不过现在基本上已经差不多了,就差魔兽的在房子里面的视角缓慢恢复了,这个是蛮好加的,就是有些小地方要注意~
好,上图:
基本代码体系大家参考Ogre的那个SinbadDemo和QtOgitor的Demo,基本都出自那儿,当然,要是只取摄像机部分的话可以看SetupCamera(),SetupBody(),UpdateBody(),updateCamera(),updateCameraGoal(),这些就行~ 祝你好运,有好的建议或不明白的欢迎评论指教哈~
上代码:
.h
04 | #include "OgreTerrainGroup.h" |
09 | class MyCameraController |
20 | SceneNode * mCameraPivot; |
21 | SceneNode * mCameraNode; |
22 | SceneNode * mCameraOriginal; |
23 | SceneNode * mBodyNode; |
25 | NxRaycastHit mHitPoint; |
29 | Ogre::TerrainGroup * mTerrainGroup; |
33 | MyCameraController(Camera * camera,SceneNode * body,NxScene * scene,TerrainGroup * tg) : mCamera(camera), mBodyNode(body), mNxScene(scene), mTerrainGroup(tg), bViewRestore( false ), mPivotPitch(0.0f) |
39 | void update( float deltaTime); |
40 | void updateGoal( float pitchAngle, float yawAngle, float deltaZoom); |
42 | SceneNode * getCameraPivot() |
.cpp
001 | #include "MyCameraController.h" |
003 | void MyCameraController::setup() |
005 | SceneManager * sMgr = mCamera->getSceneManager(); |
006 | mCameraPivot = sMgr->getRootSceneNode()->createChildSceneNode(); |
007 | mCameraOriginal = mCameraPivot->createChildSceneNode(Vector3(0,3,-20)); |
008 | mCameraNode = sMgr->getRootSceneNode()->createChildSceneNode(); |
010 | mCameraNode->attachObject(mCamera); |
012 | mCameraNode->setFixedYawAxis( true ); |
013 | mCameraOriginal->setFixedYawAxis( true ); |
014 | mCameraPivot->setFixedYawAxis( true ); |
017 | void MyCameraController::update( float deltaTime) |
019 | mCameraPivot->setPosition(mBodyNode->getPosition() + Vector3(0,2.2f,0)); |
020 | mCameraOriginal->_update( true , true ); |
021 | mCameraOriginal->lookAt(mCameraPivot->getPosition(),Node::TS_WORLD); |
023 | Vector3 vo = mCameraOriginal->_getDerivedPosition(); |
024 | Vector3 vb = mCameraPivot->_getDerivedPosition(); |
026 | NxVec3 vc(vb.x,vb.y,vb.z); |
027 | NxVec3 vecDir = NxVec3(vo.x - vb.x,vo.y - vb.y,vo.z - vb.z); |
028 | float length = vecDir.normalize(); |
029 | NxRay nxRay(vc,vecDir); |
031 | NxShape * shape = mNxScene->raycastClosestShape(nxRay,NX_ALL_SHAPES,hit); |
033 | if (shape && hit.distance < length) |
035 | Vector3 hitWorld(hit.worldImpact.x,hit.worldImpact.y,hit.worldImpact.z); |
038 | float transZ = -0.8f; |
040 | hitWorld += mCameraOriginal->_getDerivedOrientation() * Vector3(0,0,transZ); |
041 | Vector3 hitLocal = -mCameraPivot->convertWorldToLocalPosition(hitWorld); |
042 | mCameraNode->setPosition(hitWorld); |
043 | mCameraNode->lookAt(mCameraPivot->getPosition(),Node::TS_WORLD); |
048 | mCameraNode->setPosition(mCameraPivot->getPosition() + Vector3(0,1.1,0)); |
049 | Vector3 v = mCameraPivot->getOrientation() * Vector3(0,0,3); |
050 | mCameraNode->lookAt(mCameraPivot->getPosition() + v,Node::TS_WORLD); |
058 | Vector3 transPos = vo - mCameraNode->getPosition(); |
059 | Vector3 localPos = mCameraPivot->convertWorldToLocalPosition(mCameraNode->getPosition()); |
060 | Vector3 originalPos = mCameraOriginal->getPosition(); |
061 | if (localPos.z > originalPos.z) |
063 | localPos.z -= 25 * deltaTime; |
065 | if (localPos.z <= originalPos.z) |
067 | localPos.z = originalPos.z; |
068 | bViewRestore = false ; |
071 | originalPos.z = localPos.z; |
073 | mCameraNode->setPosition(mCameraPivot->convertLocalToWorldPosition(originalPos)); |
076 | mCameraNode->setPosition(vo); |
078 | mCameraNode->lookAt(mCameraPivot->getPosition(),Node::TS_WORLD); |
081 | Vector3 cameraNodePos = mCameraNode->getPosition(); |
082 | float height = mTerrainGroup->getHeightAtWorldPosition(cameraNodePos.x,0,cameraNodePos.z) + 0.5; |
083 | if (cameraNodePos.y < height) |
085 | cameraNodePos.y = height; |
086 | mCameraNode->setPosition(cameraNodePos); |
090 | void MyCameraController::updateGoal( float pitchAngle, float yawAngle, float deltaZoom) |
092 | mCameraPivot->yaw(Degree(yawAngle),Node::TS_LOCAL); |
097 | mCameraPivot->pitch(Degree(pitchAngle), Node::TS_LOCAL); |
098 | mPivotPitch += pitchAngle; |
101 | Real dist = mBodyNode->_getDerivedPosition().distance(mCameraOriginal->_getDerivedPosition()); |
102 | Real distChange = deltaZoom * dist; |
105 | if (!bViewRestore && dist + distChange >= 6 && dist + distChange <= 28) |
107 | mCameraOriginal->translate(0, 0, distChange, Node::TS_LOCAL); |