Cocos2d-x开发者指南09:3D功能
也许你已经开始接触Cocos2d-x,或者仅仅知道它是一个2D游戏引擎。但很多人可能不清楚,从3.x版本开始,Cocos2d-x就增加并改进了3D功能。众所周知,3D游戏有着巨大的市场前景,Cocos2d-x中已经具备了3D开发所需的所有功能。
如果你对3D开发还比较陌生,很多专业术语也不熟悉,不用担心。这篇文章将带你了解一些额外的软件工具,一同探索Cocos2d-x中的3D功能。
初学3D
Sprite3D
和2D游戏一样,3D游戏中也有Sprite对象,它是任何游戏的核心基本对象。3D精灵不仅有x、y轴,还多了z轴。与常见的Sprite类似,Sprite3D有多种使用方式。以下是加载并显示一个Sprite3D对象的简单示例:
auto sprite = Sprite3D::create("boss.c3b");
sprite->setScale(5.f);
sprite->setPosition(Vec2(200,200));
scene->addChild(sprite);
上述代码基于提供的文件创建了一个Sprite3D对象。
术语
虽然这里没有专门的语言类,但在使用3D时,你需要了解一些常用术语:
- Mesh(网格):构建一个形状的顶点以及对应的渲染纹理。
- Model(模型):可被渲染的对象,是网格的集合。在Cocos2d-x引擎中,我们通常称之为“Sprite3D”。
- Camera(摄像机):由于3D场景不是平面的,所以需要设置一个摄像机。不同场景的摄像机参数各不相同。
- Light(光线):光线用于使场景更接近真实世界。游戏中的对象要想看起来更真实,其颜色必须随光线变化。面对光线时是亮的,背对光线时则是暗的。用光线照射一个对象意味着该对象必须根据光线来计算其颜色。
使用Sprite3D
给Sprite3D对象添加3D模型
前文提到3D模型是网格的集合,因此可以将一个3D模型添加到其他3D模型上,以创建丰富的效果。下面以给对象添加武器为例,首先需要找到武器要添加到的附着点,可使用getAttachNode(attachment_point_name)函数实现,然后使用addChild()函数将新模型作为子节点添加到附着点上。这一过程可以理解为将多个简单的3D模型组合起来创建更复杂的模型。示例代码如下:
auto sp = Sprite3D::create("axe.c3b");
sprite->getAttachNode("Bip001 R Hand")->addChild(sp);
更换3D模型
在进行3D建模时,可能需要给模型添加动态变化,比如道具、外观的变化或视觉信号,以使用户注意到模型状态的改变。
如果3D模型由网格组成,可以使用getMeshByIndex()和getMeshByName()来访问网格数据,从而实现更换武器或改变对象外观的效果。例如,给女孩穿上一件外套,可通过改变正在使用的网格对象的可见度来实现:
auto sprite = Sprite3D::create("ReskinGirl.c3b");
// display the first coat
auto girlTop0 = sprite->getMeshByName("Girl_UpperBody01");
girlTop0->setVisible(true);
auto girlTop1 = sprite->getMeshByName("Girl_UpperBody02");
girlTop1->setVisible(false);
// swap to the second coat
girlTop0->setVisible(false);
girlTop1->setVisible(true);
动画
Sprite3D对象是游戏的核心,我们已经学习了如何操作精灵,接下来还需要掌握更复杂的操作,比如动画。可以使用Animation3D和Animate3D对象来运行一个3D动画。首先使用Animation3D对象创建一个Animate3D动作,示例代码如下:
// the animation is contained in the .c3b file
auto animation = Animation3D::create("orc.c3b");
// creates the Action with Animation object
auto animate = Animate3D::create(animation);
// runs the animation
sprite->runAction(RepeatForever::create(animate));
运行《开发者指南示例》中的代码可以看到这个动作效果。需要注意的是,3D动画与2D中的概念完全相同,可参考开发者指南中的第四章。
多个动画
当需要同时运行多个动画时,可以使用animation start time和animation length这两个参数来创建多个动画,这两个参数的单位都是秒。示例如下:
auto animation = Animation3D::create(fileName);
auto runAnimate = Animate3D::create(animation, 0, 2);
sprite->runAction(runAnimate);
auto attackAnimate = Animate3D::create(animation, 3, 5);
sprite->runAction(attackAnimate);
在上述示例中,有两个动画在运行。第一个动画立即运行并持续2秒,第二个动画在3秒后开始并持续5秒。
动画速度
动画的速度为正整数时动画前进,为负数时动画后退。例如,将动画速度设置为10,意味着该动画持续10秒。
混合动画
当使用多个动画时,混合效果会自动应用于各个动画之间,其目的是在特效之间创建一个平滑的过渡效果。例如有两个动画A和B,动画A的最后一帧和动画B的第一帧重叠,使动画之间的改变看上去更自然。
默认过渡时间为0.1秒,也可以使用Animate3D::setTransitionTime方法来设置过渡时间。Cocos2d-x只支持关键帧之间的线性插值,这可以填补曲线中的空缺,确保有一个平滑的路径。如果在制作模型过程中使用了其他插值方法,建模型工具fbx-conv将产生额外的关键帧来弥补,这种弥补将根据目标帧实现。更多关于fbx-conv的信息将在本章最后讨论。
摄像机
摄像机类型
Camera对象在3D建模中非常重要,因为3D世界不是平面的,需要使用Camera作为3D世界的导航,就像看电影时屏幕面板的左右切换一样。Camera对象继承自Node,因此支持大部分的Action对象。Camera对象有两种类型:透视(perspective)摄像机和正交(orthographic)摄像机。
- 透视摄像机:用于查看具有远近特效的对象。使用透视摄像机时,物体在近处看上去较大,在远处看上去较小。
- 正交摄像机:用于查看在较远距离处的对象,可以将其想象成将3D世界转换到2D视角。使用正交摄像机时,无论离Camera对象多远,物体大小不变。游戏中的迷你地图通常使用正交摄像机渲染,地牢游戏中的由上到下的视角也使用正交摄像机。
摄像机的使用
在Cocos2d-x中使用Camera对象并不复杂。当使用3D时,基于Director对象的投影属性,每个Scene都会自动创建一个默认摄像机。如果还需要其他摄像机,可以通过以下方法创建:
auto s = Director::getInstance()->getWinSize();
auto camera = Camera::createPerspective(60, (GLfloat)s.width/s.height, 1, 1000);
// set parameters for camera
camera->setPosition3D(Vec3(0, 100, 100));
camera->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0));
addChild(camera); //add camera to the scene
默认摄像机为透视摄像机,如果需要创建正交摄像机,可以使用Camera::createOrthographic()方法,示例如下:
auto s = Director::getInstance()->getWinSize();
auto camera = Camera::createOrthographic(s.width, s.height, 1, 1000);
从摄像机中隐藏对象
有时不需要所有的对象都显示在摄像机的视图中,从摄像机中隐藏对象非常简单,只需要对节点设置setCameraMask(CameraFlag),对摄像机设置setCameraFlag(CameraFlag)。示例如下:
//Camera
camera->setCameraFlag(CameraFlag::USER1);
//Node
node->setCameraMask(CameraFlag::USER1);
光
光对于创建游戏格调和氛围非常重要。目前Cocos2d-x支持4种光线,开发者可根据需要使用不同的光线,每种光线实现了不同的效果。
Ambient Light(环境光)
AmbientLight对象将光应用于整个场景中,就像办公环境中,所有的灯都在天花板上,办公室中的每一个对象都处于同样的光线中。示例代码如下:
auto light = AmbientLight::create (Color3B::RED);
addChild (light);
Directional Light(平行光)
DirectionalLight对象通常用于模拟一种无限远的光源,如太阳光。使用DirectionalLight时,无论在什么情况下,其密度都是相同的。示例代码如下:
auto light = DirectionLight::create(Vec3(-1.0f, -1.0f, 0.0f), Color3B::RED);
addChild (light);
Point Light(点光源)
PointLight对象通常用于模拟灯泡、灯或者手电筒的效果,其方向是从光点的位置照射出来的。根据距离PointLight的远近,光线的密度不同,离起始位置越近,光线越强;离终点位置越近,光线越暗,投影的距离越大,光线越弱。示例代码如下:
auto light = PointLight::create(Vec3(0.0f, 0.0f, 0.0f), Color3B::RED, 10000.0f);
addChild (light);
Spot Light(聚光灯)
SpotLight对象通常用于模拟类似手电筒的聚光效果,它只在一个方向发射锥形状光源。例如在黑暗环境中,如地牢游戏中可以看到火把的位置,但只能看到手电筒发射出来的有限的圆锥形范围。示例代码如下:
auto spotLight = SpotLight::create(Vec3(-1.0f, -1.0f, 0.0f), Vec3(0.0f, 0.0f, 0.0f), Color3B::RED, 0.0, 0.5, 10000.0f);
addChild (spotLight);
Light Masking(光照遮罩)
在厨房或卧室中,可能会使用多个灯,但有时只需要用一个灯来照亮某个部分,这其实就是在应用Light Masking。Light Masking作用于Node上,只应用于特定的照明光源。例如,在一个场景中有三个光源,一个节点只能被一个光源照亮,而不是三个。可以用setLightFlag(LightFlag)函数控制哪个节点对象被光源照到。需要注意的是,所有的光源通过单一路径渲染。由于移动平台性能问题,不推荐使用多个光源,默认最大值为1。如果需要打开多个光源,必须在info.plist文件中定义如下关键词:
cocos2d.x.3d.max_dir_light_in_shadercocos2d.x.3d.max_point_light_in_shadercocos2d.x.3d.max_spot_light_in_shader
3D编辑器
3D编辑器是用于构建3D图形的一系列工具,既有商业工具也有免费工具。比较主流的编辑器有:
- Blender(免费)
- 3DS Max
- Cinema4D
- Maya
大部分3D编辑器保存的文件都是通用格式,以便在其他编辑器中能方便使用,同时也便于游戏引擎以通用方式导入模型。
Cocos2d-x提供的工具
fbx - conv命令行工具
fbx - conv命令行工具允许将一个FBX文件转换为Cocos2d-x专有的格式。FBX是目前最主流的3D文件格式,所有的主流编辑器都支持这种格式。fbx - conv默认导出.c3b格式的文件,使用起来很简单,只需要几个参数:
fbx - conv [-a|-b|-t] FBXFile
参数含义如下:
-?:帮助-a:导出文本格式和二进制格式-b:导出二进制格式-t:导出文本格式
例如:
fbx - conv -a boss.FBX
关于fbx - conv需注意以下几点:
- 模型必须有材质,且材质中至少包含一个纹理。
- 目前只支持骨骼动画。
- 目前只支持一个骨骼对象,不支持多个。
- 导出多个静态模型可以创建一个3D场景。
- 网格的顶点数或者指数的最大值是32767。
3D文件格式
目前Cocos2d-x支持两种3D文件格式:
- Wavefront Object文件:
.obj文件。支持这种格式是因为3D编辑器广泛接受它,并且非常容易解析,但这种格式受限制,不支持高级特征,如动画。 - Cocos2d - x 3D ad - hoc格式:
c3t和c3b文件。这是Cocos2d - x的专有文件格式,接受动画、材质和其他高级3D特征。后缀“t”代表文本,后缀“b”代表二进制。开发者通常需要使用c3b,因为它非常高效;但如果需要调试文件,在git或者其他VCS中追踪更改日志,则需要使用c3t。
进阶概念
BillBoard
BillBoard(并不是指高速公路边上的广告牌)是一个特殊的精灵,它一直正对着摄像机。旋转Camera的同时,BillBoard对象也会跟着旋转。使用BillBoard是一种常见的渲染技术,例如在速降滑雪比赛中,滑雪者路过的树木、石头或者其他物体都是BillBoard对象。
BillBoard的用法
创建BillBoard对象很容易,BillBoaed继承自Sprite,所以它支持Sprite对象的大部分属性。可以使用以下方法创建BillBoard对象:
auto billboard = BillBoard::create("Blue_Front1.png", BillBoard::Mode::VIEW_POINT_ORIENTED);
还可以为摄像机的XOY平面(想象成飞机的地板)创建一个BillBoard对象,只需改变BillBoard对象的模式:
auto billboard = BillBoard::create("Blue_Front1.png", BillBoard::Mode::VIEW_PLANE_ORIENTED);
create方法看上去有些不同,因为BillBoard::Mode中多传递了一个参数。有两种BillBoard::Mode类型:VIEW_POINT_ORIENTED和VIEW_PLANE_ORIENTED。
VIEW_POINT_ORIENTED代表Billboard对象面向摄像机的位置。VIEW_PLANE_ORIENTED代表Billboard对象面向摄像机XOY平面的位置。
还可以给BillBoard设置属性,就像其他Node一样,属性包括但不限于缩放、位置、旋转。例如:
billboard->setScale(0.5f);
billboard->setPosition3D(Vec3(0.0f, 0.0f, 0.0f));
billboard->setBlendFunc(BlendFunc::ALPHA_NON_PREMULTIPLIED);
addChild(billboard);