———-基于cocos2dx 3.9 + cocos studio 2.3.3.0 + VS2015测试

———转载自:https://blog.csdn.net/qq_32934429

一、关于世界摄像头,如果你不知道该如何设置,下面的几种方式可选其一:

1 第三人称视角(多数2D~3D游戏中,摄像头跟随主角形式),初始化时,斜向下45°取景,此时不要调用lookat:

  1. auto camera = Camera::createPerspective(60.0, (float)winSize.width / winSize.height, 1, 1000);
  2. _camera->setRotation3D(Vec3(-45, 0, 0));

在场景的更新中,不断更新位置(跟随人物),放置在人物的斜上方(一般来说高度由Y决定,前后距离由Z决定):

  1. //update camera's position
  2. _camera->setPosition3D(_orc->getPosition3D() + camera_offset);

2 如果是第一人称视角,则需要将camera挂在主角身上:初始化之后挂载到人物上

  1. auto _camera = Camera::createPerspective(60, (float)winSize.width / winSize.height, 1, 500);
  2. _camera->setPosition3D(Vec3(0, 1, 0));
  3. _camera->lookAt(Vec3(0, 0, 1));
  4. _orc->addChild(_camera);

之后再场景切换中,不断设置摄像机的位置以及观察点:

  1. _camera->setPosition3D(Vec3(0, 1, 0));
  2. _camera->lookAt(_orc->getPosition3D() + Vec3(0, 0, 1), Vec3(0.0f, 1.0f, 0.0f));//第二个参数可省略,此为默认值,观察物体正前方

最后,camera有个setCameraFlag属性值,在3D世界中,所有需要被观察的物体都需要通过setCameraMask的mask值与flag做与操作,如果不为0,则表示受此摄像机观察,如果你不想写的这么麻烦,或者怕漏掉,可以直接在所有3D元素的基层layer中最后调用一次就好,一般如下:

  1. _layer3D = Layer::create();
  2. addChild(_layer3D);
  3. //初始化摄像机 
  4. //初始化UI
  5. //初始化世界对象...
  6. //通过_layer3D->addChild将所有node添加到地图
  7. //最后
  8. _layer3D->setCameraMask(2);//具体的值根据实际情况而定

=============================分割线=============================

  1. // MARK: Camera 位于cocos/2d/CCNode.cpp中,看到就明白了,第二个参数默认为true
  2. void Node::setCameraMask(unsigned short mask, bool applyChildren)
  3. {
  4. _cameraMask = mask;
  5. if (applyChildren)
  6. {
  7. for (const auto& child : _children)
  8. {
  9. child->setCameraMask(mask, applyChildren);
  10. }
  11. }
  12. }

如果你想通过前后左右来移动镜头:

  1. void HelloWorld::onTouchesMoved(const std::vector<Touch*>& touchs, Event * event)
  2. {
  3. //log("call onTouchesMoved");
  4. //推进角度
  5. /*if (!touchs.empty()) {
  6. Point touch = touchs[0]->getPreviousLocation() - touchs[0]->getLocation();
  7. _distanceZ -= touch.y * 0.1f;
  8. updateCameraTransform();
  9. }*/
  10. //自由角度
  11. if (touchs.size() == 1) {
  12. Point newPos = touchs[0]->getPreviousLocation() - touchs[0]->getLocation();
  13. Vec3 cameraPos = _camera->getPosition3D();
  14. cameraPos.z -= newPos.y;
  15. cameraPos.x += newPos.x;
  16. _camera->setPosition3D(cameraPos);
  17. }
  18. }

二、关于3D精灵触摸的简单交互:目标在一个3D地图中,人物朝我们点击的位置移动

首先,我们需要一个地图界面,在3D中,如何方式我们想要的效果呢?

1 用2D贴图实现效果,测试后,发现图片模糊严重,可能和我们取得素材有关系,下面是一种可能的实现方式:

  1. if (0) {
  2. auto sbg = Sprite::create("model/plane.png");
  3. //sbg->setCameraMask(2);
  4. sbg->setRotation3D(Vec3(90, 0, 0));//水平旋转90,放在世界中心位置
  5. sbg->setPosition3D(_center);
  6. _layer3D->addChild(sbg, -1);//放于底层
  7. }

暂时没有深入,后续会详细测试,说实话我也是个入门摸索阶段,基本上你很难在网上找到一些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图形,一般来说,你可以通过下面的方式初始化:

  1. //terrain test
  2. 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)

  1. _terrain = Terrain::create(data, Terrain::CrackFixedType::SKIRT);//裂缝修补方式
  2. _terrain->setMaxDetailMapAmount(4);//最大细节层数
  3. //_terrain->setDrawWire(false);
  4. //_terrain->setSkirtHeightRatio(3);
  5. _terrain->setLODDistance(64, 128, 192);//裂缝距离
  6. _layer3D->addChild(_terrain);

上面有点啰嗦,如果你看晕了可以跳着看~扯得有点远了,回归主题,现在我们有了一个3D的草皮了~下面加对象~很简单:

  1. _orc = Sprite3D::create("model/mod_niutou.c3b");
  2. _orc->setScale(.1f);//大小矫正
  3. _orc->setRotation3D(Vec3(0, 180, 0));//方向矫正
  4. _orc->setPosition3D(Vec3(0, 0, 10));
  5. _targetPos = _orc->getPosition3D();//纪录物体位置
  6. _layer3D->addChild(_orc);

现在我们有了地图和物体,就剩下移动拉~,触发点击我们只监听touchesEnd

  1. void HelloWorld::onTouchesEnded(const std::vector<Touch*>& touches, cocos2d::Event *event)
  2. {
  3. for (auto &item : touches)
  4. {
  5. auto touch = item;
  6. auto location = touch->getLocationInView();//获取点击的屏幕坐标
  7. if (_camera)
  8. {
  9. if (_orc)
  10. {
  11. //log("onTouchesEnded...");
  12. Vec3 nearP(location.x, location.y, -1.0f), farP(location.x, location.y, 1.0f);
  13. auto size = Director::getInstance()->getWinSize();
  14. //_camera->unprojectGL(size, &nearP, &nearP);
  15. //_camera->unprojectGL(size, &farP, &farP);
  16. //将屏幕坐标转为世界坐标,这里要不要指定大小,需要根据实际情况而定
  17. nearP = _camera->unproject(nearP);
  18. farP = _camera->unproject(farP);
  19. Vec3 dir(farP - nearP);
  20. if (1) {//注意这里,如果你采用了地形贴图的方式,则走if语句
  21. dir.normalize();
  22. bool isInTerrain = _terrain->getIntersectionPoint(Ray(nearP, dir),_targetPos);
  23. return;
  24. }
  25. float dist = 0.0f;
  26. float ndd = Vec3::dot(Vec3(0, 1, 0), dir);
  27. if (ndd == 0)
  28. dist = 0.0f;
  29. float ndo = Vec3::dot(Vec3(0, 1, 0), nearP);
  30. dist = (0 - ndo) / ndd;
  31. Vec3 p = nearP + dist * dir;
  32. //防越界
  33. if (p.x > 190)
  34. p.x = 190;
  35. if (p.x < -190)
  36. p.x = -190;
  37. if (p.z > 190)
  38. p.z = 190;
  39. if (p.z < -190)
  40. p.z = -190;
  41. //p.z = -p.z;
  42. _targetPos = p;
  43. //log("move3D %f-%f-%f", _targetPos.x, _targetPos.y, _targetPos.z);
  44. //_terrain->getIntersectionPoint();
  45. }
  46. }
  47. }
  48. }

上面方法的最终目的就是将我们点击的屏幕坐标,转化为世界中的坐标,然后,我们通过schedule方式更新:

  1. void HelloWorld::update(float fDelta)
  2. {
  3. updateState(fDelta);//不断更新物体状态
  4. if (isState(_curState, State_Move))
  5. {
  6. move3D(fDelta);//移动物体
  7. if (isState(_curState, State_Rotate))//如果需要,则旋转物体
  8. {
  9. Vec3 curPos = _orc->getPosition3D();
  10. Vec3 newFaceDir = _targetPos - curPos;
  11. newFaceDir.y = 0;
  12. newFaceDir.normalize();
  13. turn(newFaceDir);//旋转
  14. }
  15. }
  16. //update camera
  17. _camera->setPosition3D(_orc->getPosition3D() + camera_offset);//上面有介绍
  18. //set Y
  19. _orc->setPositionY(_terrain->getHeight(_orc->getPositionX(), _orc->getPositionZ()));//额,下面有介绍
  20. }

首先我们需要不断的更新物体状态,比如在物体移动时,可能我们又点了一下,那么状态就会有更新(其实主要是旋转的状态,位移只需要重新设置终点即可)

这里要说明的是上面最后一段代码,设置了物体的Y,注意这里我们使用了地形,那么地形肯定就有高和低了,物体的Y随着地形需要做动态的变化,SO~terrain通过当前对象位于世界的x与z坐标值计算出了相应的地形高度

下面是状态更新的代码,代码来源cocos2dx示例:

  1. void HelloWorld::updateState(float elapsedTime)
  2. {
  3. Vec3 curPos = _orc->getPosition3D();//当前位置
  4. Vec3 curFaceDir;
  5. _orc->getNodeToWorldTransform().getForwardVector(&curFaceDir);//当前朝向的向量
  6. curFaceDir = -curFaceDir;
  7. curFaceDir.normalize();
  8. Vec3 newFaceDir = _targetPos - curPos;//根据新的位置获取其向量
  9. newFaceDir.y = 0.0f;
  10. newFaceDir.normalize();
  11. float cosAngle = std::fabs(Vec3::dot(curFaceDir, newFaceDir) - 1.0f);//计算二个向量的点积,获取偏转角度?我没看明白。。。。
  12. float dist = curPos.distanceSquared(_targetPos);//二点间距离
  13. if (dist <= 4.0f)//如果距离过小,则只考虑是否需要旋转
  14. {
  15. if (cosAngle <= 0.01f)
  16. _curState = State_Idle;
  17. else
  18. _curState = State_Rotate;
  19. }
  20. else//移动的同时考虑是否旋转
  21. {
  22. if (cosAngle>0.01f)
  23. _curState = State_Rotate | State_Move;
  24. else
  25. _curState = State_Move;
  26. }
  27. }

下面是移动,很简单,就不多说了,注意他的移动不是通过action而是一步一步的:

  1. void HelloWorld::move3D(float elapsedTime)
  2. {
  3. Vec3 curPos = _orc->getPosition3D();
  4. Vec3 newFaceDir = _targetPos - curPos;
  5. newFaceDir.y = 0.0f;
  6. newFaceDir.normalize();
  7. //位移向量*速度*时间
  8. Vec3 offset = newFaceDir * 50.0f * elapsedTime;
  9. curPos += offset;
  10. _orc->setPosition3D(curPos);
  11. offset.x = offset.x;
  12. offset.z = offset.z;
  13. //pass the newest orc position
  14. if (_state) {
  15. _state->setUniformVec3("u_target_pos", _orc->getPosition3D());
  16. }
  17. //log("move3D %f-%f-%f",newFaceDir.x,newFaceDir.y,newFaceDir.z);
  18. }

最后是旋转,代码来源cocos2dx示例:

  1. //旋转3D精灵
  2. void HelloWorld::turn(Vec3 dir) {
  3. Vec3 axis;
  4. float angle = -1 * acos(dir.dot(Vec3(0, 0, -1)));
  5. dir.cross(dir, Vec3(0, 0, -1), &axis);
  6. Quaternion q2;
  7. q2.createFromAxisAngle(Vec3(0, 1, 0), (float)-M_PI, &q2);
  8. Quaternion headingQ;
  9. headingQ.createFromAxisAngle(axis, angle, &headingQ);
  10. _orc->setRotationQuat(headingQ*q2);
  11. }

## 标题 ##

cocos2dx 学习笔记之摄像头与3D精灵的移动相关推荐

  1. 【论文学习笔记-2】高分辨率3D深度重建

    [论文学习笔记-2] 高分辨率3D深度重建 背景介绍 模型 目标 Related Works 背景介绍 应用场景广泛:桥,电缆etc 高分辨率图像的特点:像素多,potential disparity ...

  2. cocos2d-x学习笔记15:cocos2d-x教程资源总结

    注:cocos2d可作为cocos2dx的参考,两者接口很相似. 名称:知易的<知易Cocos2D-iPhone开发教程> 官方地址:http://blog.sina.com.cn/s/a ...

  3. cocos2d-x学习笔记16:记录存储1:CCUserDefault

    cocos2d-x学习笔记16:记录存储1:CCUserDefault 一.简述 CCUserDefalt作为NSUserDefalt类的cocos2d-x实现版本,承担了cocos2d-x引擎的记录 ...

  4. Halcon学习笔记:3D_coordinates(3D标定)

    Halcon学习笔记:3D_coordinates(3D标定) 欢迎有兴趣的朋友一起学习,代码理解注释有问题的可以告诉我,一起讨论,共同进步. *初始化程序,dev_close_window() *关 ...

  5. cocos2d-x 学习笔记(2)cocos2d-x重要概念,项目结构及 CCDirector 导演控件

    cocos2d-x 学习笔记(2)cocos2d-x重要概念及项目结构 在cocos2d引擎中,有几个概念,分别是导演,场景,布景和人物角色. 导演(CCDirector)在cocos2d-x引擎中, ...

  6. cocos2d-x 学习笔记(1)关于cocos2d-x(环境配置,项目结构,文件说明)

    cocos2d-x 学习笔记(1)关于cocos2d-x(环境配置,项目结构,文件说明) 1.问:什么是cocos2d-x? 是一套开放源代码,主要基于c,c++语言,针对windows,linux, ...

  7. unity学习笔记-番外(3d模型的动作设计以及导入-2018版)材质的替换以及动作穿模(自己的手穿模到自己的其他部位)

    unity学习笔记-番外(3d模型的动作设计以及导入) 动作设计白嫖方法 方法一:小k网 需要注意的地方 方法二:mixamo 需要注意的地方 材质的替换 一 动作的穿模 2021.5.13更新 -2 ...

  8. PhysX学习笔记2 -cloth系统3d模型的导入

    PhysX学习笔记2 -cloth系统3d模型的导入 PhysX的cloth的demo中,3d模型是用.obj格式. 环境:win2000, PhysX安装包: PhysX_6.11.01_Syste ...

  9. Halcon学习笔记:select_points_object_model_3d(3D对象模型阀值分割)

    Halcon学习笔记:select_points_object_model_3d 3D对象模型阀值分割 This example program shows how to use the operat ...

  10. 【新手入门】树莓派Raspberry学习笔记4 摄像头安装+配置+简单使用

    树莓派Raspberry Pi3B+学习笔记4 今日达成: ①摄像头安装+配置+简单使用 ②树莓派上python编程初尝试 这份笔记里主要讲的是摄像头的安装+配置+简单使用. 一.摄像头和树莓派连接+ ...

最新文章

  1. python重复执行_python flask schedule重复运行 任务被重复执行问题 解决方案
  2. webdriver For selenium
  3. 安装 Windows8 后值得做的十多项简单优化,让Win8速度快上加快!
  4. Zookeeper服务端线程分析(单机)
  5. 民生银行IT运维架构管理可视化实践
  6. Java算法-符号~
  7. Quartz.NET总结(四)Quartz 远程调度
  8. 百度地图标点点击变色_《和平精英》版本爆料第三弹:雪地洞穴开启!组队标点功能升级~...
  9. UVA 10895——Matrix Transpose
  10. phpnow升级php版本 php-5.2.14-Win32升级至5.3.5
  11. 并发容器——ConcurrentHashMap
  12. 如何在Excel选取想要的数据(特定行)
  13. linux 下部署tomcat问题
  14. Smartbi报表工具二次开发和集成能力怎么样?
  15. C/C++ 字符数字-‘0‘ 字符数字+‘0‘ 是什么意思
  16. 计算机显卡初始化失败,电脑显卡驱动安装失败原因,其解决办法
  17. 双十一买什么充电宝好?实惠好用的充电宝推荐
  18. 工作随记3:一次交换机环路故障
  19. RS232接口EMC设计方案
  20. Linux应用程序动态更改用户ID

热门文章

  1. 如何科学进行用户分析?六大方法论了解一下!
  2. Excel两行交换及两列交换,快速互换相邻表格数据的方法
  3. 公链生态周报:BI指数周涨幅超4%,以太坊POS代码即将完成 | 链塔智库
  4. os.path -- 常用路径操作
  5. THREE实战2_正交投影相机与透视相机
  6. 目前常用的4种备份系统架构
  7. wps目录怎么加一条_WPS中如何正确插入目录_WPS怎么做目录
  8. 【业界新闻】浪潮高端存储系统技术发展及展望(下篇)
  9. 美国大学 计算机专业 排名,2019美国大学计算机专业排名
  10. Keil编译*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS