今天我们来学习cocos2dx ver3.3 Sprite3DTest ,那么从这个例子中我们可以学到什么呢?

虽然说cocos2dx对3d的支持较unity3d来讲只是一点点皮毛的功夫,但是从上手容易度来讲要好很多,更适合我这样的没玩过3d的菜鸟。如果只是实现2.5d的一些场景,cocos2dx只要能在性能和稳定性上做得更好一点的话,想必是可以留住需要实现3d但又习惯了cocos2dx的老玩家的。

1. Sprite3DBasicTestSprite3D 两种基本创建方式(1). 直接使用一个带有素材的obj (3dmax对象)

Sprite3D::create(“sprite3dTest/scene01.obj”);

(2). 使用不带素材的对象,自己设置素材

auto sprite = Sprite3D::create(“Sprite3DTest/boss1.obj”);

sprite->setScale(3.f);

sprite->setTexture(“Sprite3DTest/boss.png”);

(3)

auto sprite = EffectSprite3D::createFromObjFileAndTexture(“Sprite3DTest/boss1.obj”, “Sprite3DTest/boss.png”);

2. Sprite3DHitTest 拖拽(1) 点击 在onTouchBegan 中设置透明度target->setOpacity(100);

放手在 onTouchEnded中还原透明度 target->setOpacity(255);

(2)移动 onTouchMoved

target->setPosition(target->getPosition() + touch->getDelta());

(3) addEventListenerWithSceneGraphPriority更具sprite的z order决定丢用先后

3. Sprite3DEffectTest 一些shader 的运用例子

将具体实现封装到了Effect3DOutline中

4.加载3dmax导出的文件,使用fbx-conv.exe转化std::string fileName = “Sprite3DTest/orc.c3b”;

auto sprite = EffectSprite3D::create(fileName);

5. Sprite3DWithSkinOutlineTestshader特效在c3b的运用

6. Animate3DTest 动画的播放和切换 小乌龟的demo(1) 同一个c3b既可以创建sprite也可以创建action

std::string fileName =”Sprite3DTest/tortoise.c3b”;

auto sprite = Sprite3D::create(fileName);

sprite->setScale(0.1f);

auto s =Director::getInstance()->getWinSize();

sprite->setPosition(Vec2(s.width *4.f / 5.f, s.height /2.f));

addChild(sprite);

_sprite = sprite;

auto animation = Animation3D::create(fileName);

(2) animation 可以通过 时间来分拆,这个时间是在3Dmax中定义的

if (animation)

{ //2个动画的时间不同,这些在3Dmax中定义

auto animate = Animate3D::create(animation, 0.f,1.933f);

_swim = RepeatForever::create(animate);

sprite->runAction(_swim);

_swim->retain();

_hurt = Animate3D::create(animation,1.933f, 2.8f);

_hurt->retain();

_state = State::SWIMMING;

}

(3)乌龟游到尽头 调用 reachEndCallBack

通过reverse获得相反的action

auto inverse = (MoveTo*)_moveAction->reverse();

沿y轴180度转向

auto rot = RotateBy::create(1.f, Vec3(0.f, 180.f, 0.f));

(4)在onTouchesEnded中实现播放乌龟的 hurt动作 ,但是这里没法实现hurt动作的结束回调,所以采用_hurt->getDuration() 

来获得其结束时间,并且做一个renewCallBack来回到_swim 动作

auto delay = DelayTime::create(_hurt->getDuration() – Animate3D::getTransitionTime());

auto seq = Sequence::create(delay, CallFunc::create(CC_CALLBACK_0(Animate3DTest::renewCallBack, this)), nullptr);

疑问?Animate3D::getTransitionTime() 用来干嘛

为啥会用void Animate3DTest::update(float dt) 来维护一个中间状态 HURT_TO_SWIMMING 和SWIMMING_TO_HURT

7. AttachmentTest装武器获取某个骨骼,Bip001 R Hand是在3Dmax定义的

auto sp = Sprite3D::create(“Sprite3DTest/axe.c3b”);

sprite->getAttachNode(“Bip001 R Hand”)->addChild(sp);

点击后去掉附加节点

_sprite->removeAllAttachNode();

8. Sprite3DReskinTest皮肤换装, 动态更换材质(mesh)(1)字号字体

TTFConfig ttfConfig(“fonts/arial.ttf”, 20);

auto label1 = Label::createWithTTF(ttfConfig,”Hair”);

应该是在3dm中定义好了mesh的名称?

_girlPants[0]= “Girl_LowerBody01″;

_girlPants[1]= “Girl_LowerBody02″;

_girlUpperBody[0] = “Girl_UpperBody01″;

_girlUpperBody[1] = “Girl_UpperBody02″;

_girlShoes[0]= “Girl_Shoes01″;

_girlShoes[1]= “Girl_Shoes02″;

_girlHair[0]= “Girl_Hair01″;

_girlHair[1]= “Girl_Hair02″;

(2) 初始化的时候隐藏备选的部分,点击切换的时候再 交换显示

auto girlPants = sprite->getMeshByName(_girlPants[1]);

if(girlPants)

{

girlPants->setVisible(false);

}

9.Sprite3DWithOBBPerfromanceTest 包围盒与3D模型碰撞的实现

1.decltype 用来获取auto的type 并且作为声明非常适用

auto obbSize = _obb.size();

for(decltype(obbSize) i = 0; i < obbSize; i++)

2.ray 来将触摸点转换到3d场景中,判断是否点击到。

Ray ray;

calculateRayByLocationInView(&ray,location);

那么ray的射出方向会沿着camera的正中心发射吗?

3.画3d的边框

#include “DrawNode3D.h”

DrawNode3D* _drawDebug;

首先要画一个sprite的外框,比如得到他得obb

_drawDebug->clear();

Mat4 mat = _sprite->getNodeToWorldTransform();

mat.getRightVector(&_obbt._xAxis);

_obbt._xAxis.normalize();

mat.getUpVector(&_obbt._yAxis);

_obbt._yAxis.normalize();

mat.getForwardVector(&_obbt._zAxis);

_obbt._zAxis.normalize();

_obbt._center = _sprite->getPosition3D();

Vec3 corners[8] = {};

_obbt.getCorners(corners);

然后获得corners,调用drawCube()函数

_drawDebug->drawCube(corners, Color4F(0,1,1,1));

4. obb可以不依赖sprite独立得存在

void Sprite3DWithOBBPerfromanceTest::addNewOBBWithCoords(Vec2 p)

{

Vec3 extents = Vec3(10, 10, 10);

AABB aabb(-extents, extents);

auto obb = OBB(aabb);

obb._center = Vec3(p.x,p.y,0);

_obb.push_back(obb);

}

5. 对碰撞的判断其实只很简单的一句话

_obbt.intersects(_obb)

10.Sprite3DMirrorTest 3D模型的镜像

sprite->setScaleX(-5);在素材上转换

sprite->setCullFace(GL_FRONT); 在mesh上转换

11. Camera3DTestDemo

Camera3DTest.cpp

camera 可以被添加到任何地方,如果一个sprite or layer想要被camera观察到,

必须要包含其flag(效果也会作用到其child)

auto sprite = Sprite::create(“myFile.png”);

sprite->setCameraMask(CameraFlag::USER1);

auto camera = Camera::createPerspective(60, winSize.width/winSize.height, 1, 1000);

camera->setCameraFlag(CameraFlag::USER1);

scene->addChild(camera);

(1) 缩放,在点击label的时候设置标志位_bZoomOut _bZoomIn 

Vec3 lookDir = _camera->getPosition3D() – _sprite3D->getPosition3D();

Vec3 cameraPos = _camera->getPosition3D();

if(lookDir.length() <= 300)

{

cameraPos += lookDir.getNormalized();

_camera->setPosition3D(cameraPos);

}

重新设置camera的位置即可

(2) 左右旋转 实际上就是沿着y轴左右旋转

Vec3rotation3D= _camera->getRotation3D();

rotation3D.y+= 1;

_camera->setRotation3D(rotation3D);

(3) 视角切换

_cameraType来记录当前camera类型

enum class CameraType

{

FreeCamera=0,

FirstCamera=1,

ThirdCamera=2,

};

[第一人称] 实际上就是把相机放在了 角色头上

Vec3 newFaceDir;

_sprite3D->getWorldToNodeTransform().getForwardVector(&newFaceDir);

newFaceDir.normalize();

_camera->setPosition3D(Vec3(0,35,0) + _sprite3D->getPosition3D());

_camera->lookAt(_sprite3D->getPosition3D() + newFaceDir*50, Vec3(0, 1, 0));

[第三人称]

_camera->setPosition3D(Vec3(0, 130, 130) + _sprite3D->getPosition3D());

_camera->lookAt(_sprite3D->getPosition3D(), Vec3(0,1,0));

(4)让camera 随着角色移动

每一帧都需要更新camera的状态

void Camera3DTestDemo::updateCamera(float fDelta)

-> move3D(fDelta);

先计算出角色需要移动多少:

①得到朝向目标的单位向量

Vec3 newFaceDir = _targetPos – curPos;

newFaceDir.y = 0.0f;

newFaceDir.normalize();

② 获得实际移动向量

Vec3 offset = newFaceDir * 25.0f * elapsedTime;

③ camera随之移动即可

Vec3 cameraPos= _camera->getPosition3D();

cameraPos.x+=offset.x;

cameraPos.z+=offset.z;

_camera->setPosition3D(cameraPos);

常用总结:

1. 3d 纬度的朝向设置

sprite->setRotation3D(Vec3(0,180,0));

2. sprite->setEffect() 添加特效

3.sprite->getAttachNode(“Bip001 R Hand”) 获取某个骨骼

4. sprite->getMeshByName(_girlPants[1]); 获取材质

5. Vec2 getNormalized() const;

Vec3 Vec3::getNormalized() const;获取向量的标准化形式. 若为零向量,返回(0,0)

也可以理解为获取一个向量的单位向