OGRE+PhysX仿魔兽世界摄像机
OGRE+PhysX仿魔兽世界摄像机(包含碰撞,跟随,防墙体穿透以及摄像头不被遮挡)的实现(10/7更新,封装了下)
刚终于解决了摄像机的问题,写个文交流交流先~
做法其实蛮简单,场景是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
01
|
#include "Ogre.h"
|
02
|
#include "NxScene.h"
|
03
|
#include "NxRay.h"
|
04
|
#include "OgreTerrainGroup.h"
|
05
|
|
06
|
//std::pair<Ogre::SceneNode *,NxActor *> NodeActorPair;
|
07
|
|
08
|
using namespace Ogre;
|
09
|
class MyCameraController
|
10
|
{
|
11
|
private :
|
12
|
/*
|
13
|
NxRevoluteJoint * mJoint;
|
14
|
NxSpringDesc * mSpringDesc;
|
15
|
Ogre::SceneManager * mSceneMgr;
|
16
|
NxRay mRay;
|
17
|
NodeActorPair * mCaracter;
|
18
|
NodeActorPair * mCamera;*/
|
19
|
|
20
|
SceneNode * mCameraPivot;
|
21
|
SceneNode * mCameraNode;
|
22
|
SceneNode * mCameraOriginal;
|
23
|
SceneNode * mBodyNode;
|
24
|
Camera * mCamera;
|
25
|
NxRaycastHit mHitPoint;
|
26
|
NxScene * mNxScene;
|
27
|
bool bViewRestore;
|
28
|
float mPivotPitch;
|
29
|
Ogre::TerrainGroup * mTerrainGroup;
|
30
|
|
31
|
public :
|
32
|
//MyCameraController(Ogre::SceneManager * sceneMgr,NxScene * scene,NodeActorPair * caracter,NodeActorPair * camera) : mSceneMgr(sceneMgr), mNxScene(scene), mCamera(caracter)
|
33
|
MyCameraController(Camera * camera,SceneNode * body,NxScene * scene,TerrainGroup * tg) : mCamera(camera), mBodyNode(body), mNxScene(scene), mTerrainGroup(tg), bViewRestore( false ), mPivotPitch(0.0f)
|
34
|
{
|
35
|
setup();
|
36
|
}
|
37
|
|
38
|
void setup();
|
39
|
void update( float deltaTime);
|
40
|
void updateGoal( float pitchAngle, float yawAngle, float deltaZoom);
|
41
|
|
42
|
SceneNode * getCameraPivot()
|
43
|
{
|
44
|
return mCameraPivot;
|
45
|
}
|
46
|
};
|
.cpp
001
|
#include "MyCameraController.h"
|
002
|
|
003
|
void MyCameraController::setup()
|
004
|
{
|
005
|
SceneManager * sMgr = mCamera->getSceneManager();
|
006
|
mCameraPivot = sMgr->getRootSceneNode()->createChildSceneNode();
|
007
|
mCameraOriginal = mCameraPivot->createChildSceneNode(Vector3(0,3,-20));
|
008
|
mCameraNode = sMgr->getRootSceneNode()->createChildSceneNode();
|
009
|
|
010
|
mCameraNode->attachObject(mCamera);
|
011
|
|
012
|
mCameraNode->setFixedYawAxis( true );
|
013
|
mCameraOriginal->setFixedYawAxis( true );
|
014
|
mCameraPivot->setFixedYawAxis( true );
|
015
|
}
|
016
|
|
017
|
void MyCameraController::update( float deltaTime)
|
018
|
{
|
019
|
mCameraPivot->setPosition(mBodyNode->getPosition() + Vector3(0,2.2f,0));
|
020
|
mCameraOriginal->_update( true , true );
|
021
|
mCameraOriginal->lookAt(mCameraPivot->getPosition(),Node::TS_WORLD);
|
022
|
|
023
|
Vector3 vo = mCameraOriginal->_getDerivedPosition();
|
024
|
Vector3 vb = mCameraPivot->_getDerivedPosition();
|
025
|
Ogre::Ray ray;
|
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);
|
030
|
NxRaycastHit hit;
|
031
|
NxShape * shape = mNxScene->raycastClosestShape(nxRay,NX_ALL_SHAPES,hit);
|
032
|
|
033
|
if (shape && hit.distance < length)
|
034
|
{
|
035
|
Vector3 hitWorld(hit.worldImpact.x,hit.worldImpact.y,hit.worldImpact.z);
|
036
|
|
037
|
//为了防止穿透
|
038
|
float transZ = -0.8f;
|
039
|
//float transY = (-0.8f) * vo.y / (NxMath::sqrt(vo.x * vo.x + vo.z * vo.z)); //这个还是算了,不加上去。。。
|
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);
|
044
|
|
045
|
//为了防止在角落里时摄像机反向
|
046
|
if (hitLocal.z < 0)
|
047
|
{
|
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);
|
051
|
}
|
052
|
bViewRestore = true ;
|
053
|
}
|
054
|
else
|
055
|
{
|
056
|
if (bViewRestore)
|
057
|
{
|
058
|
Vector3 transPos = vo - mCameraNode->getPosition();
|
059
|
Vector3 localPos = mCameraPivot->convertWorldToLocalPosition(mCameraNode->getPosition());
|
060
|
Vector3 originalPos = mCameraOriginal->getPosition();
|
061
|
if (localPos.z > originalPos.z)
|
062
|
{
|
063
|
localPos.z -= 25 * deltaTime;
|
064
|
}
|
065
|
if (localPos.z <= originalPos.z)
|
066
|
{
|
067
|
localPos.z = originalPos.z;
|
068
|
bViewRestore = false ;
|
069
|
}
|
070
|
else
|
071
|
originalPos.z = localPos.z;
|
072
|
|
073
|
mCameraNode->setPosition(mCameraPivot->convertLocalToWorldPosition(originalPos));
|
074
|
}
|
075
|
else
|
076
|
mCameraNode->setPosition(vo);
|
077
|
|
078
|
mCameraNode->lookAt(mCameraPivot->getPosition(),Node::TS_WORLD);
|
079
|
}
|
080
|
|
081
|
Vector3 cameraNodePos = mCameraNode->getPosition();
|
082
|
float height = mTerrainGroup->getHeightAtWorldPosition(cameraNodePos.x,0,cameraNodePos.z) + 0.5;
|
083
|
if (cameraNodePos.y < height)
|
084
|
{
|
085
|
cameraNodePos.y = height;
|
086
|
mCameraNode->setPosition(cameraNodePos);
|
087
|
}
|
088
|
}
|
089
|
|
090
|
void MyCameraController::updateGoal( float pitchAngle, float yawAngle, float deltaZoom)
|
091
|
{
|
092
|
mCameraPivot->yaw(Degree(yawAngle),Node::TS_LOCAL);
|
093
|
//mBodyNode->yaw(Degree(deltaYaw),Node::TS_LOCAL);
|
094
|
|
095
|
//if(mPivotPitch + pitchAngle <= 60 && mPivotPitch + pitchAngle >= -40)
|
096
|
{
|
097
|
mCameraPivot->pitch(Degree(pitchAngle), Node::TS_LOCAL);
|
098
|
mPivotPitch += pitchAngle;
|
099
|
}
|
100
|
|
101
|
Real dist = mBodyNode->_getDerivedPosition().distance(mCameraOriginal->_getDerivedPosition());
|
102
|
Real distChange = deltaZoom * dist;
|
103
|
|
104
|
// bound the zoom
|
105
|
if (!bViewRestore && dist + distChange >= 6 && dist + distChange <= 28)
|
106
|
{
|
107
|
mCameraOriginal->translate(0, 0, distChange, Node::TS_LOCAL);
|
108
|
}
|
109
|
}
|
OGRE+PhysX仿魔兽世界摄像机相关推荐
- .NET Core 仿魔兽世界密保卡实现
<魔兽世界>的老玩家都知道,密保卡曾经被用于登录验证,以保证账号安全.今天我用.NET Core模拟了一把密保卡(也叫矩阵卡)的实现,分享给大家. 密保卡的原理 这是一张典型的魔兽世界密保 ...
- mysql修改元宝,端游[君·天下]高仿魔兽世界一键启动服务端+配套客户端+元宝金币修改教程等...
资源说明: 1.本资源为君·天下OL单机版一键启动服务端,此游戏高仿魔兽世界. 2.四大职业:武修.羽箭.仙道.真巫. 3.慈善系统.变身系统.召唤系统.动态副本-单人进入.配对系统.狩猎时装.商城. ...
- [原创]魔兽RPG仿魔兽世界:巫妖王击杀剧情 中文语音
[原创]魔兽RPG仿魔兽世界:巫妖王击杀剧情 中文语音 简述:利用魔兽争霸客户端中自带WE地图编辑器制作完成,地图导入了魔兽世界中提取的lich king语音文件和模型,并配合we触发器完成系列动作 ...
- OGRE+Physx赛车游戏开发
摘自:http://www.cnblogs.com/lancidie/archive/2011/08/22/2148886.html 最近在用OGRE+NXOGRE(一个结合AGEIA PHYSX 和 ...
- Ogre个人初步学习总结
Ogre个人初步学习总结 目录 1.开发环境 2.环境配置 2.1Ogre环境配置 2.2ParticleUniverse 环境配置 3.OGRE的学习札记 3.1Ogre的学前准备 3.1.1Ogr ...
- 用火炬之光的资源作游戏
自从知道了火炬之光的资源可以提取出来, 就一直想用它们自己做个游戏. 现在这个Demo已经做好了.欢迎大家看看演示视频和Demo. Demo视频: http://v.youku.com/v_show/ ...
- 分享105个ASP整站程序源码,总有一款适合您
分享105个ASP整站程序源码,总有一款适合您 105个ASP整站程序源码下载链接:https://pan.baidu.com/s/148X7V8o435Scu0lUgWMwFQ?pwd=8mwj ...
- [OGRE]基础教程来三发:来谈一谈摄像机吧
今天我们来谈谈OGRE中的摄像机吧,像机吧,机吧,吧. 首先先来给大家介绍两个函数:createViewport和createCamera,这两个方法已经在基类ExampleApplication中定 ...
- 摄像机,光源和阴影 -----OGRE 3D 1.7 Beginner‘s Guide中文版 第四章
文章来源:http://www.cnblogs.com/oneDouble/articles/2545706.html 迄今为止,我们总是创建静止的场景并且在场景中没有移动的物体.在这一章,我们将会改 ...
最新文章
- C#自定义工业控件开发
- Linux platform总线(1):总体框架
- 经常吹空调皮肤是不是会变黑
- 正则化与L0、L1、L2范数祥解
- Java面试题 String类能不能被继承?为什么?
- Community Server系列之三:页面间关系2[介绍]
- [网络]------长连接和短连接
- 分析容灾备份建设需求
- 在esx server VI里导入其它虚拟机
- 如何查看mysql默认字符集_如何找出MySQL中的默认服务器字符集?
- struts2启动报错com/opensymphony/xwork2/spring/SpringObjectFactory.java:220:-1
- 3年,从工程师到创始人
- linux多进程spawn,【Linux Shell脚本编程】expect解决脚本交互 + Shell的多进程处理
- Ubuntu 64-bit下搭建 Apache 2、PHP5、MySQL、GO Web服务器
- 模拟win10系统开机加载的动画
- 书香小说APP界面设计
- edk2+vs2019环境搭建
- 良好的编程习惯有哪些?
- java hex to ascii_在java中读取hex文件并将其转换为ascii
- 在线文档可以直接打印吗?哪里可以打印在线文档