cocos2dx 学习笔记之摄像头与3D精灵的移动
———-基于cocos2dx 3.9 + cocos studio 2.3.3.0 + VS2015测试
———转载自:https://blog.csdn.net/qq_32934429
一、关于世界摄像头,如果你不知道该如何设置,下面的几种方式可选其一:
1 第三人称视角(多数2D~3D游戏中,摄像头跟随主角形式),初始化时,斜向下45°取景,此时不要调用lookat:
- auto camera = Camera::createPerspective(60.0, (float)winSize.width / winSize.height, 1, 1000);
- _camera->setRotation3D(Vec3(-45, 0, 0));
在场景的更新中,不断更新位置(跟随人物),放置在人物的斜上方(一般来说高度由Y决定,前后距离由Z决定):
- //update camera's position
- _camera->setPosition3D(_orc->getPosition3D() + camera_offset);
2 如果是第一人称视角,则需要将camera挂在主角身上:初始化之后挂载到人物上
- auto _camera = Camera::createPerspective(60, (float)winSize.width / winSize.height, 1, 500);
- _camera->setPosition3D(Vec3(0, 1, 0));
- _camera->lookAt(Vec3(0, 0, 1));
- _orc->addChild(_camera);
之后再场景切换中,不断设置摄像机的位置以及观察点:
- _camera->setPosition3D(Vec3(0, 1, 0));
- _camera->lookAt(_orc->getPosition3D() + Vec3(0, 0, 1), Vec3(0.0f, 1.0f, 0.0f));//第二个参数可省略,此为默认值,观察物体正前方
最后,camera有个setCameraFlag属性值,在3D世界中,所有需要被观察的物体都需要通过setCameraMask的mask值与flag做与操作,如果不为0,则表示受此摄像机观察,如果你不想写的这么麻烦,或者怕漏掉,可以直接在所有3D元素的基层layer中最后调用一次就好,一般如下:
- _layer3D = Layer::create();
- addChild(_layer3D);
- //初始化摄像机
- //初始化UI
- //初始化世界对象...
- //通过_layer3D->addChild将所有node添加到地图
- //最后
- _layer3D->setCameraMask(2);//具体的值根据实际情况而定
=============================分割线=============================
- // MARK: Camera 位于cocos/2d/CCNode.cpp中,看到就明白了,第二个参数默认为true
- void Node::setCameraMask(unsigned short mask, bool applyChildren)
- {
- _cameraMask = mask;
- if (applyChildren)
- {
- for (const auto& child : _children)
- {
- child->setCameraMask(mask, applyChildren);
- }
- }
- }
如果你想通过前后左右来移动镜头:
- void HelloWorld::onTouchesMoved(const std::vector<Touch*>& touchs, Event * event)
- {
- //log("call onTouchesMoved");
- //推进角度
- /*if (!touchs.empty()) {
- Point touch = touchs[0]->getPreviousLocation() - touchs[0]->getLocation();
- _distanceZ -= touch.y * 0.1f;
- updateCameraTransform();
- }*/
- //自由角度
- if (touchs.size() == 1) {
- Point newPos = touchs[0]->getPreviousLocation() - touchs[0]->getLocation();
- Vec3 cameraPos = _camera->getPosition3D();
- cameraPos.z -= newPos.y;
- cameraPos.x += newPos.x;
- _camera->setPosition3D(cameraPos);
- }
- }
二、关于3D精灵触摸的简单交互:目标在一个3D地图中,人物朝我们点击的位置移动
首先,我们需要一个地图界面,在3D中,如何方式我们想要的效果呢?
1 用2D贴图实现效果,测试后,发现图片模糊严重,可能和我们取得素材有关系,下面是一种可能的实现方式:
- if (0) {
- auto sbg = Sprite::create("model/plane.png");
- //sbg->setCameraMask(2);
- sbg->setRotation3D(Vec3(90, 0, 0));//水平旋转90,放在世界中心位置
- sbg->setPosition3D(_center);
- _layer3D->addChild(sbg, -1);//放于底层
- }
暂时没有深入,后续会详细测试,说实话我也是个入门摸索阶段,基本上你很难在网上找到一些cocos2d介绍新特性的文章,这也是写这篇文章的目的,下面是采用3D里的地形方式实现,即Terrain:
2 如果你想大概知道这是个啥玩意,可以先看下面这篇文章:
http://edu.the9.com/a/xingyexinxi/Cocos2d_x/2015/0506/271.html
我们直接打开官方的文档,能看到三个构造函数(最复杂的如下):
TerrainData (const char *heightMapsrc, const char *alphamap, const DetailMap &detail1, const DetailMap &detail2, const DetailMap &detail3, const DetailMap&detail4, const Size &chunksize=Size(32, 32), float mapHeight=2, float mapScale=0.1) 构造函数,该构造函数构造一个地形,有4个detailmaps,1个Alpha地图 |
第一个参数:高度图(如果你和我一样没用过PS 3DX一些图形工具的画,还是先百度吧)一种特殊的灰度图(黑白),表现世界中的地形(越亮的位置,表示地形相对越高)(这里,如果你用第一个构造形式,会用你指定的texture平铺满高度图,实际效果你可以想象~~~一片那啥,我看实际效果好像是拉伸的形式,反正效果很差,如果你只是想平铺,下面会有一种更好的形式)
第二个参数:alpha贴图,你如果想知道,可以看看上面的链接;你打开test中cpptest的资源文件夹下的alphamap.png,可以看到它是一个127*127 由r g b a通道组成的png图片,在结合4个detailmaps的参数,你是不是明白了什么呢。实际上,alpha贴图上每一个块(块的大小默认是32px)的元素表示了 r g b a 所占的权重比,如果你的r g b a设置为同一个图片,同时将倒数第二个参数mapHeight设为 0.000001啥的,就是上面说的平铺了
第三到六个参数:具体的 r g b a图形,一般来说,你可以通过下面的方式初始化:
- //terrain test
- Terrain::DetailMap r("model/Grass1.jpg"), g("model/Grass1.jpg"), b("model/Grass1.jpg"), a("model/Grass1.jpg");
第七个参数是每个块的大小,默认为32
第八个参数是地形上最大的高度
第九个是地形的缩放值,举个例子,现在我们假定我们的世界大小是 640x 640,但是高度图大小为 257x257,如果想铺满我们的世界:
(mapScale = 640/ 256),普及来说,如果是W * H呢,mapScale = Math.max(W , H)/ (高度图的大小 - 1)
- _terrain = Terrain::create(data, Terrain::CrackFixedType::SKIRT);//裂缝修补方式
- _terrain->setMaxDetailMapAmount(4);//最大细节层数
- //_terrain->setDrawWire(false);
- //_terrain->setSkirtHeightRatio(3);
- _terrain->setLODDistance(64, 128, 192);//裂缝距离
- _layer3D->addChild(_terrain);
上面有点啰嗦,如果你看晕了可以跳着看~扯得有点远了,回归主题,现在我们有了一个3D的草皮了~下面加对象~很简单:
- _orc = Sprite3D::create("model/mod_niutou.c3b");
- _orc->setScale(.1f);//大小矫正
- _orc->setRotation3D(Vec3(0, 180, 0));//方向矫正
- _orc->setPosition3D(Vec3(0, 0, 10));
- _targetPos = _orc->getPosition3D();//纪录物体位置
- _layer3D->addChild(_orc);
现在我们有了地图和物体,就剩下移动拉~,触发点击我们只监听touchesEnd
- void HelloWorld::onTouchesEnded(const std::vector<Touch*>& touches, cocos2d::Event *event)
- {
- for (auto &item : touches)
- {
- auto touch = item;
- auto location = touch->getLocationInView();//获取点击的屏幕坐标
- if (_camera)
- {
- if (_orc)
- {
- //log("onTouchesEnded...");
- Vec3 nearP(location.x, location.y, -1.0f), farP(location.x, location.y, 1.0f);
- auto size = Director::getInstance()->getWinSize();
- //_camera->unprojectGL(size, &nearP, &nearP);
- //_camera->unprojectGL(size, &farP, &farP);
- //将屏幕坐标转为世界坐标,这里要不要指定大小,需要根据实际情况而定
- nearP = _camera->unproject(nearP);
- farP = _camera->unproject(farP);
- Vec3 dir(farP - nearP);
- if (1) {//注意这里,如果你采用了地形贴图的方式,则走if语句
- dir.normalize();
- bool isInTerrain = _terrain->getIntersectionPoint(Ray(nearP, dir),_targetPos);
- return;
- }
- float dist = 0.0f;
- float ndd = Vec3::dot(Vec3(0, 1, 0), dir);
- if (ndd == 0)
- dist = 0.0f;
- float ndo = Vec3::dot(Vec3(0, 1, 0), nearP);
- dist = (0 - ndo) / ndd;
- Vec3 p = nearP + dist * dir;
- //防越界
- if (p.x > 190)
- p.x = 190;
- if (p.x < -190)
- p.x = -190;
- if (p.z > 190)
- p.z = 190;
- if (p.z < -190)
- p.z = -190;
- //p.z = -p.z;
- _targetPos = p;
- //log("move3D %f-%f-%f", _targetPos.x, _targetPos.y, _targetPos.z);
- //_terrain->getIntersectionPoint();
- }
- }
- }
- }
上面方法的最终目的就是将我们点击的屏幕坐标,转化为世界中的坐标,然后,我们通过schedule方式更新:
- void HelloWorld::update(float fDelta)
- {
- updateState(fDelta);//不断更新物体状态
- if (isState(_curState, State_Move))
- {
- move3D(fDelta);//移动物体
- if (isState(_curState, State_Rotate))//如果需要,则旋转物体
- {
- Vec3 curPos = _orc->getPosition3D();
- Vec3 newFaceDir = _targetPos - curPos;
- newFaceDir.y = 0;
- newFaceDir.normalize();
- turn(newFaceDir);//旋转
- }
- }
- //update camera
- _camera->setPosition3D(_orc->getPosition3D() + camera_offset);//上面有介绍
- //set Y
- _orc->setPositionY(_terrain->getHeight(_orc->getPositionX(), _orc->getPositionZ()));//额,下面有介绍
- }
首先我们需要不断的更新物体状态,比如在物体移动时,可能我们又点了一下,那么状态就会有更新(其实主要是旋转的状态,位移只需要重新设置终点即可)
这里要说明的是上面最后一段代码,设置了物体的Y,注意这里我们使用了地形,那么地形肯定就有高和低了,物体的Y随着地形需要做动态的变化,SO~terrain通过当前对象位于世界的x与z坐标值计算出了相应的地形高度
下面是状态更新的代码,代码来源cocos2dx示例:
- void HelloWorld::updateState(float elapsedTime)
- {
- Vec3 curPos = _orc->getPosition3D();//当前位置
- Vec3 curFaceDir;
- _orc->getNodeToWorldTransform().getForwardVector(&curFaceDir);//当前朝向的向量
- curFaceDir = -curFaceDir;
- curFaceDir.normalize();
- Vec3 newFaceDir = _targetPos - curPos;//根据新的位置获取其向量
- newFaceDir.y = 0.0f;
- newFaceDir.normalize();
- float cosAngle = std::fabs(Vec3::dot(curFaceDir, newFaceDir) - 1.0f);//计算二个向量的点积,获取偏转角度?我没看明白。。。。
- float dist = curPos.distanceSquared(_targetPos);//二点间距离
- if (dist <= 4.0f)//如果距离过小,则只考虑是否需要旋转
- {
- if (cosAngle <= 0.01f)
- _curState = State_Idle;
- else
- _curState = State_Rotate;
- }
- else//移动的同时考虑是否旋转
- {
- if (cosAngle>0.01f)
- _curState = State_Rotate | State_Move;
- else
- _curState = State_Move;
- }
- }
下面是移动,很简单,就不多说了,注意他的移动不是通过action而是一步一步的:
- void HelloWorld::move3D(float elapsedTime)
- {
- Vec3 curPos = _orc->getPosition3D();
- Vec3 newFaceDir = _targetPos - curPos;
- newFaceDir.y = 0.0f;
- newFaceDir.normalize();
- //位移向量*速度*时间
- Vec3 offset = newFaceDir * 50.0f * elapsedTime;
- curPos += offset;
- _orc->setPosition3D(curPos);
- offset.x = offset.x;
- offset.z = offset.z;
- //pass the newest orc position
- if (_state) {
- _state->setUniformVec3("u_target_pos", _orc->getPosition3D());
- }
- //log("move3D %f-%f-%f",newFaceDir.x,newFaceDir.y,newFaceDir.z);
- }
最后是旋转,代码来源cocos2dx示例:
- //旋转3D精灵
- void HelloWorld::turn(Vec3 dir) {
- Vec3 axis;
- float angle = -1 * acos(dir.dot(Vec3(0, 0, -1)));
- dir.cross(dir, Vec3(0, 0, -1), &axis);
- Quaternion q2;
- q2.createFromAxisAngle(Vec3(0, 1, 0), (float)-M_PI, &q2);
- Quaternion headingQ;
- headingQ.createFromAxisAngle(axis, angle, &headingQ);
- _orc->setRotationQuat(headingQ*q2);
- }
## 标题 ##
cocos2dx 学习笔记之摄像头与3D精灵的移动相关推荐
- 【论文学习笔记-2】高分辨率3D深度重建
[论文学习笔记-2] 高分辨率3D深度重建 背景介绍 模型 目标 Related Works 背景介绍 应用场景广泛:桥,电缆etc 高分辨率图像的特点:像素多,potential disparity ...
- cocos2d-x学习笔记15:cocos2d-x教程资源总结
注:cocos2d可作为cocos2dx的参考,两者接口很相似. 名称:知易的<知易Cocos2D-iPhone开发教程> 官方地址:http://blog.sina.com.cn/s/a ...
- cocos2d-x学习笔记16:记录存储1:CCUserDefault
cocos2d-x学习笔记16:记录存储1:CCUserDefault 一.简述 CCUserDefalt作为NSUserDefalt类的cocos2d-x实现版本,承担了cocos2d-x引擎的记录 ...
- Halcon学习笔记:3D_coordinates(3D标定)
Halcon学习笔记:3D_coordinates(3D标定) 欢迎有兴趣的朋友一起学习,代码理解注释有问题的可以告诉我,一起讨论,共同进步. *初始化程序,dev_close_window() *关 ...
- cocos2d-x 学习笔记(2)cocos2d-x重要概念,项目结构及 CCDirector 导演控件
cocos2d-x 学习笔记(2)cocos2d-x重要概念及项目结构 在cocos2d引擎中,有几个概念,分别是导演,场景,布景和人物角色. 导演(CCDirector)在cocos2d-x引擎中, ...
- cocos2d-x 学习笔记(1)关于cocos2d-x(环境配置,项目结构,文件说明)
cocos2d-x 学习笔记(1)关于cocos2d-x(环境配置,项目结构,文件说明) 1.问:什么是cocos2d-x? 是一套开放源代码,主要基于c,c++语言,针对windows,linux, ...
- unity学习笔记-番外(3d模型的动作设计以及导入-2018版)材质的替换以及动作穿模(自己的手穿模到自己的其他部位)
unity学习笔记-番外(3d模型的动作设计以及导入) 动作设计白嫖方法 方法一:小k网 需要注意的地方 方法二:mixamo 需要注意的地方 材质的替换 一 动作的穿模 2021.5.13更新 -2 ...
- PhysX学习笔记2 -cloth系统3d模型的导入
PhysX学习笔记2 -cloth系统3d模型的导入 PhysX的cloth的demo中,3d模型是用.obj格式. 环境:win2000, PhysX安装包: PhysX_6.11.01_Syste ...
- Halcon学习笔记:select_points_object_model_3d(3D对象模型阀值分割)
Halcon学习笔记:select_points_object_model_3d 3D对象模型阀值分割 This example program shows how to use the operat ...
- 【新手入门】树莓派Raspberry学习笔记4 摄像头安装+配置+简单使用
树莓派Raspberry Pi3B+学习笔记4 今日达成: ①摄像头安装+配置+简单使用 ②树莓派上python编程初尝试 这份笔记里主要讲的是摄像头的安装+配置+简单使用. 一.摄像头和树莓派连接+ ...
最新文章
- python重复执行_python flask schedule重复运行 任务被重复执行问题 解决方案
- webdriver For selenium
- 安装 Windows8 后值得做的十多项简单优化,让Win8速度快上加快!
- Zookeeper服务端线程分析(单机)
- 民生银行IT运维架构管理可视化实践
- Java算法-符号~
- Quartz.NET总结(四)Quartz 远程调度
- 百度地图标点点击变色_《和平精英》版本爆料第三弹:雪地洞穴开启!组队标点功能升级~...
- UVA 10895——Matrix Transpose
- phpnow升级php版本 php-5.2.14-Win32升级至5.3.5
- 并发容器——ConcurrentHashMap
- 如何在Excel选取想要的数据(特定行)
- linux 下部署tomcat问题
- Smartbi报表工具二次开发和集成能力怎么样?
- C/C++ 字符数字-‘0‘ 字符数字+‘0‘ 是什么意思
- 计算机显卡初始化失败,电脑显卡驱动安装失败原因,其解决办法
- 双十一买什么充电宝好?实惠好用的充电宝推荐
- 工作随记3:一次交换机环路故障
- RS232接口EMC设计方案
- Linux应用程序动态更改用户ID
热门文章
- 如何科学进行用户分析?六大方法论了解一下!
- Excel两行交换及两列交换,快速互换相邻表格数据的方法
- 公链生态周报:BI指数周涨幅超4%,以太坊POS代码即将完成 | 链塔智库
- os.path -- 常用路径操作
- THREE实战2_正交投影相机与透视相机
- 目前常用的4种备份系统架构
- wps目录怎么加一条_WPS中如何正确插入目录_WPS怎么做目录
- 【业界新闻】浪潮高端存储系统技术发展及展望(下篇)
- 美国大学 计算机专业 排名,2019美国大学计算机专业排名
- Keil编译*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS