精品教程|基于Cocos2d-x 3.6 打造体素游戏

前言:海波是位拥有十几年游戏开发经验的资深游戏追梦人。如今的他正在独立游戏人的道路上不断奋斗前行。今天,他为大家带来了体素游戏《彩虹尽头》的开发心得。

1.什么是体素游戏?

提到体素游戏,最大名鼎鼎的当然是《Minecraft》(《我的世界》)。虽然最常冠以沙盒游戏的分类,但整个世界、角色等都是采用立方体体素打造的,我们也将其分类在体素游戏之内。

Voxel(体素)是volumepixel的缩写,是指构成由立体渲染对象世界的最小单位。体素不光是单指立方体,也包括各种其他几何体,比如:球体、圆柱体、甚至是复杂几何体的迭代形式。如下图场景就是分别用球体与圆柱体打造:

4e6f790dbe7a7f44875a6d65630ab100.jpeg

95eeedf8044c5d1f6d754dbfc227334f.jpeg

当然随着《Minecraft》的风靡,体素游戏发展到了更高高度,各种体素工具和体素游戏引擎也逐渐出现,虽然不是作为本文重点,但简单介绍3个体素引擎(体素工具在本文的工具篇再详细介绍):

VoxelFarm Engine (简称VFE): http://voxelfarm.com

Atomontage Engine (无限细节技术): http://www.atomontage.com

Voxlap Engine(一个牛人在93年就开始开发的小引擎): http://advsys.net/ken/voxlap/voxlap05.htm

2.如何打造体素游戏?

言归正传,本文意在讲述如何用简单快捷的方式打造我们自己的体素游戏。

近年来, 一些有着极简设计和明亮色块的体素游戏,也在移动手机市场不断取得成功。其中比较有代表性的就是《天天过马路》了,当然也有用2D像素Isometric化模拟的体素游戏,如《cloud path》。

出于追随潮流我也开始打造了一个自己的体素游戏,先来做个广告:《Rainbow' End》(《彩虹尽头》)28天开发完成,已经在App Store和Google play上线,欢迎体验。

(小编注:苹果用户点击文章末尾“阅读原文”即可免费下载。)

059c7de75a0576a3df3d417a72f3dd05.jpeg

本文将根据这款游戏,从工具、引擎、表现方面来给大家讲述如何快速开发一款体素游戏。

工具篇

制作体素的工具有很多,当然也可以用3dmax、maya等传统建模工具制作,但不符合快速开发的原则,所以为了在一个月内开发完成,只能花时间去寻找更多的工具,以下为推荐工具列表:

  • Painter3D

  • (http://www.paint3d.net/)

  • Sproxel

  • (http://sproxel.blogspot.com.br/)

  • Qubicle Constructor

  • (http://www.minddesk.com/qubicle_constructor.php)

  • VoxelShop

  • (https://blackflux.com/index.php)

  • Qblock (在线制作,可以看到其他玩友上传的作品来寻找灵感)

  • (http://kyucon.com/qblock/)

  • MagicaVoxel Editor (本文使用,重点推荐)

  • (https://voxel.codeplex.com/)

本文采用 MagicalVolex Editor 0.93 进行开发(目前版本0.96.3) 。注意:0.93更早的版本没有对相同体素导出时进行顶点优化,会导致游戏中顶点数偏高,游戏性能下降。 MagicalVolex Editor工具使用教程本文不讲,可参考网站提供的教学视频学习。

2524af5b7cfc773be40500909744b912.jpeg

我们将通过MagicalVolex Editor建好的体素模型以export导出选项中的obj导出,再通过3dMax或Blender3D、 Cinema4D、 Maya等传统建模工具的任何一种将obj文件导出成fbx文件备用。

(当然一些引擎本身是支持obj文件格式的,包括Cocos2d-x,之所以转换下是为了使用Cocos2d-x下的模型的c3b或c3t格式,考虑以后引擎升级可能的模型优化)。

引擎篇

U3D + MagicaVoxel Editor是可以非常简单的进行开发的,本文不做赘述。由于正版U3D发行问题和作者本身也想测试下Cocos2d-x 3D的性能,所以选择了Cocos2d-x 3.6版本。

首先是模型转化,将fbx文件通过Cocos2d-x提供的工具fbx-conv进行转化,生成c3b模型文件和纹理文件。

(注意:这里可以对文件进行优化,可以让游戏中所有使用的模型在MagicalVolex Editor下使用相同的调色版,这样导出的索引色是相同的,这就意味着所有模型可以共用一张纹理文件,可以大大减少io读取时间和显存使用。)

然后初始化3D场景,包括创建天空盒、创建3D摄像机、创建天光、给场景添加灯光,是为了弥补体素对象色彩单一导致的显示比较平的问题,添加灯光来区别体素不同面的间隔,相关代码如下:

//创建摄像机
m_pMainCamera = Camera::createPerspective(45, size.width/size.height, 1, 5000);
if(!m_pMainCamera)
    return false;
Vec3 camPos = Vec3(0,150.0f*cosf(M_PI/2.8f),150.0f*sinf(M_PI/2.8f));
Vec3 lookAt = Vec3(0,0,0);
m_pMainCamera->setPosition3D(camPos);
m_pMainCamera->lookAt(lookAt);
this->addChild(m_pMainCamera);
m_pMainCamera->setCameraFlag(CameraFlag::USER1);

//创建天空盒子
Skybox* skyBox = Skybox::create("sky4.png", "sky4.png", "sky4.png", "sky4.png", "sky4.png", "sky4.png");
if(!skyBox)
return false;
skyBox->setScale(1000);    ///注意大于创建摄像机的最远面
skyBox->setCameraMask((unsigned short)CameraFlag::USER1);   ///设置只能被创建的摄像机看到
skyBox->setGlobalZOrder(-1);
this->addChild(skyBox);

//创建环境光
AmbientLight* ambientLight = AmbientLight::create(Color3B(150, 150, 150));
this->addChild(ambientLight);
//设置天光
DirectionLight* directionLight = DirectionLight::create(Vec3(-2, -4, -3), Color3B(158, 158, 158));
this->addChild(directionLight);

初始化场景后,开始加载体素模型,为了增加体素表现力,我们为体素增加描边效果。

///创建无描边体素对象
Sprite3D* spriteWithoutOutLine = Sprite3D::create("bear.c3b");
if(!spriteWithoutOutLine)
    return false;
spriteWithoutOutLine->setPosition3D(Vec3(-50,0,0));
spriteWithoutOutLine->setRotation3D(Vec3(0,-140,0));
spriteWithoutOutLine->setCameraMask((unsigned short)CameraFlag::USER1);

spriteWithoutOutLine->setForceDepthWrite(true); ///设置强制3D对象进行深度检测,如果场景中有半透明物体的话
this->addChild(spriteWithoutOutLine);

///创建有描边体素对象
EffectSprite3D* spriteWitOutLine = EffectSprite3D::create("girl1.c3b");
if(!spriteWitOutLine)
   return false;
spriteWitOutLine->setPosition3D(Vec3(0,0,0));
spriteWitOutLine->setRotation3D(Vec3(0,-150,0));
spriteWitOutLine->setCameraMask((unsigned short)CameraFlag::USER1);

spriteWithoutOutLine->setForceDepthWrite(true); ///设置强制3D对象进行深度检测,如果场景中有半透明物体的话
this->addChild(spriteWitOutLine);

OutlineEffect3D* outline = OutlineEffect3D::create();
outline->setOutlineColor(Vec3(0.3f, 0.3f, 0.3f));
outline->setOutlineWidth(0.03f);
spriteWitOutLine->addEffect(outline, 3); ///设置外描边

在Cocos2d-x 3.6中添加了针对particle3D的支持,既然做3D游戏,粒子效果也是必不可少的。遗憾的是3.6中粒子部分还有相关bug和资源路径读取相对写死的情况,所以本文例子针对paritcle3D部分对引擎做了相关改动,具体可见引擎源码部分打入的lwwhb标记。3D粒子对象创建相关代码如下:

void HelloWorld::spawnExplosion(const cocos2d::Vec3& pos)
{
    auto explosion = PUParticleSystem3D::create("explosionSystem.pu");
    if(!explosion)
        return;
    explosion->setCameraMask((unsigned short)CameraFlag::USER1);
    explosion->setPosition3D(pos);
    explosion->setScale(2.0f);
    this->addChild(explosion);
    explosion->startParticleSystem();
}

彩虹对象采用了ParitcleUniverse的条带系统实现,对象的定义和实现如下:

class RibbonTrail : public cocos2d::Node, public cocos2d::BlendProtocol
{
    RibbonTrail();
    virtual ~RibbonTrail();
    public:
        static RibbonTrail* create(const std::string &textureFile, float width, float length);
        bool initWithFile(const std::string &path, float width, float length);
        virtual void update(float delta);
        virtual void draw(cocos2d::Renderer* renderer, const cocos2d::Mat4& transform, uint32_t flags) override;
        // overrides
        virtual void setBlendFunc(const cocos2d::BlendFunc &blendFunc) override;
        virtual const cocos2d::BlendFunc &getBlendFunc() const override;
        cocos2d::PURibbonTrail* getTrail() const { return m_pTrail; }
    private:
        cocos2d::PURibbonTrail*             m_pTrail;
        cocos2d::BlendFunc                  m_BlendFunc;
};

RibbonTrail* RibbonTrail::create(const std::string &textureFile, float width, float length)
{
    if (textureFile.length() < 4)
        CCASSERT(false, "invalid filename for texture file");
    auto ribbonTrail = new (std::nothrow) RibbonTrail();
    if (ribbonTrail && ribbonTrail->initWithFile(textureFile, width, length))
    {
        ribbonTrail->autorelease();
        return ribbonTrail;
    }
    CC_SAFE_DELETE(ribbonTrail);
    return nullptr;

}
RibbonTrail::RibbonTrail()
:m_pTrail(nullptr)
{
    m_BlendFunc = {GL_SRC_ALPHA , GL_ONE};
}
RibbonTrail::~RibbonTrail()
{
    m_pTrail = nullptr;
}
bool RibbonTrail::initWithFile(const std::string &path, float width, float length )
{
    m_pTrail = new (std::nothrow) PURibbonTrail("RibbonTrail", path);
    if(m_pTrail)
    {
        m_pTrail->setNumberOfChains(1);
        m_pTrail->setMaxChainElements(100);
        m_pTrail->setTrailLength(length);
        m_pTrail->setUseVertexColours(true);
        m_pTrail->setInitialColour(0, Vec4(1, 1, 1, 1));
        //m_pTrail->setColourChange(0, Vec4(0.8, 0.8, 0.8, 0.8));
        m_pTrail->setInitialWidth(0, width);
        m_pTrail->setDepthTest(true);
        m_pTrail->setDepthWrite(true);
        return true;
    }
    return false;
}
void RibbonTrail::update(float delta)
{
    if(m_pTrail)
        m_pTrail->update(delta);
}
void RibbonTrail::draw(Renderer* renderer, const Mat4& transform, uint32_t flags)
{
    if(m_pTrail)
        m_pTrail->render(renderer, transform, m_BlendFunc);
}
const BlendFunc& RibbonTrail::getBlendFunc() const
{
    return m_BlendFunc;
}

void RibbonTrail::setBlendFunc(const BlendFunc &blendFunc)
{
    m_BlendFunc = blendFunc;
}

9ef665f6ef9bf749115c0a8cb6179c63.jpeg

相关粒子全部代码下载:

https://github.com/lwwhb/cocos2dx3.6_voxel_tutorial

3.后记

以目前Cocos2d-x 3.6版本提供的3D部分封装,是可以完全满足一个简单3D游戏的需求,而且包体相对U3D实现要小得多,但引擎部分仍然有很多地方需要加强,比如材质系统没有材质的渲染,只能针对每个对象手动设置shader实现。

另外Cocos2d-x中针对Sprite3D的合批操作也没有办法实现,需要修改引擎或采用模型的instancing来去进行速度优化。但总体来说完成一个简单的体素游戏Cocos2d-x是可以应付的,大家可以放心使用。

本文版权归tinyflare.com所有,欢迎转载,但必须保留此段声明,且在文章页面明显位置给出原文连接。

苹果用户点击文章末尾“阅读原文”即可免费下载《彩虹尽头》。

冒险路上会有危险,有迷宫,也伴随着乐趣,更有各种可爱体素小伙伴随你去成长!

5f1aadda639038cb1adaabf6236928e1.jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值