我们有C++基础,学习引擎总是急于求成,想立马做出一款简单的游戏给朋友玩。但是我们往往看了很多资料却一直不知道如何下手去写,有时候只要能走出第一步我们就会游刃有余,但是眼高手低的我们不是大神,需要有人指引一下。这里我就写一下我是如何入门学习cocos2dx3.1的,给大家参考一下。
如果你想第一天就写出微信打飞机,请耐心去阅读。我也是一个菜鸟,博客难免粗糙和出错,请大家谅解。加油吧!
我们创建工程后总会自带一个HelloWorld类,短短的几行代码就出来了一个游戏的雏形,请问我们真的理解它了吗?如果我们能早一点弄明白这几行代码,我们或许会比现在走得更远。
HelloWorld去掉退出按钮只有下面三个函数。
学习之前我要强调一遍,这三个方法一定要做到透彻理解和重写。因为所有的游戏场景都需要这三个函数。
创建游戏HelloWorld场景的时候,只需要在AppDelegate写一句: Helloworld::createScene();
请看它的实现
首先新建一个新场景,然后create一个HelloWorld层并添加到新创建的场景,最后返回这个场景给AppDelegate。
一个场景的创建和展示就是这么简单,但是你有没有发现HelloWorld中没有create呢?并且没有调用init()方法呢?我们在HelloWorld中没有在任何地方看到调用init初始化,那么这是在哪里调用的呢?
请看CREATE_FUNC的宏定义:
\表示换行,因为宏定义代码如果放同一行阅读不方便。
可以看到CREATE_FUNC宏其实是定义了一个静态成员函数,这个函数可以new一个新的对象,并对其进行init()操作。
这样整个Helloworld的逻辑就清晰了:
外部类调用static cocos2d::Scene* createScene();来创建一个新的场景。 CREATE_FUNC(HelloWorld);是定义一个静态的create类成员函数,并在这个函数中调用virtual bool init();初始化这个场景。
以后每当我们新建一个场景的时候,按照这个格式即可。好了,下面该动手了。
现在请删除Helloworld新建一个叫MainScene的场景,完善该类,修改AppDelegate.cpp中的场景创建为auto scene = MainScene::createScene();运行展示新的场景。(这个时候整个屏幕还是黑暗的,,因为没有任何元素显示)。
注意:如果是新手,我建议你再看一下Helloworld里面这三句代码的定义,你肯定会出错。
比如:static方法里面 auto layer =***(这里是当前类名,不是Layer)::create();
init()方法最好加virtual修饰,也可以这样写, virtual bool init() override;(override表示继承来的,对它重载)。在init里面一定先初始化父类Layer::init()。
到这里应该会自己重写一个空白的场景了,如果你以前不明白这里,并且刚才没有动手写的话,那么再请你删除CREATE_FUNC这句,自己重写一个create函数吧。如果你记不起怎么写,我不建议你再继续往下阅读,学习就到此为止吧。
动手重写CREATE_FUNC宏定义
修改AppDegate.cpp里面的HelloWorld::createScene();改为 auto scene = MainScene::createScene();
再使用一下自己写的create1: auto layer=MainScene::create1();
这样我们就完全掌握了游戏场景的创建和它的原理,其实更重要的是我们认识到了应该去怎么学,cocos2dx引擎使用了大量的宏定义,我们一定不能只追求表面的用法,而应该深入下去学习宏实现了那些东西。特别是到后面的内存管理更是如此。
总结一下,也再重复一遍,所有游戏场景的基础都是这三句代码:
我在后面每一天新加场景都要手写一遍,一定要搞把这三句代码以及实现牢记于心。后面的学习中还会遇到大量的宏,我们要学会跟踪宏定义,仔细阅读每一段代码,这样我们才能学懂而不是简单的学会。
好,后面接下来完善我们的第一个游戏场景。现在新建的场景是空白的,什么都没有。我们要尝试往场景中添加各种展示元素。
这里有三个定义要搞清楚,场景(scene)、层(layer)、精灵(sprite)。(原谅我表达能力有限,大家最好先阅读一本cocos2dx的书籍,把一些理论知识弄明白)。
场景可以包含多个层,层可以包含多个精灵。 精灵可以是我们在游戏看到的所有的元素,比如按钮、血量条、人物等。比如酷跑中,可以看到近处的背景精灵移动快,远处的背景精灵移动慢,我们可以添加两个层到场景中,一个层中循环快速的精灵背景,一个循环慢速的精灵背景。这样就容易理解它们的概念了吧。
进入正题
如果你是新手,一定要把下面的代码自己敲一下。磨刀不误砍柴工,现在担心敲代码耽误时间,以后只会耽误更多的时间。
首先在resource文件夹下放一张图片00191880.jpg(华为入职时的照片,工号191880,从来没改过^_^),
然后创建精灵,展示这张图片。
三句,很简单,添加到init()函数中吧!
我没学过OC,我以前做windows客户端,感觉这种写法很不习惯,入乡随俗吧,如果你连this也不知道是什么意思的话,建议你看一下C++Primer,这本书很重要。
下面是我练习的代码,自己尝试一下吧!
如果你阅读过cocos2dx的书籍或者百度一下的话,相信你上面的代码一定看得懂。Director::getInstance()是一个单例,获取整个游戏的导演类,我不会告诉你后面的getVisibleSize()、getWinSize()还有setScale()是什么意思的,希望你能养成自己动手去查的习惯。
下面写一点小菜吧,添加的精灵不会动是不是很没意思?下面就让图片动起来:
在init里面添加
这是一个定时器,每隔一段时间会执行myupdate函数。
myupdate的定义如下(我不会告诉你schedule后面还可以再加一个参数表示隔多久执行一次的,自己查去吧):
上面这段代码就是移动刚才你添加的所有的精灵。
for (auto a: sp)看不懂?好吧,这个类似于迭代器的遍历,你可以改成for(auto a= sp.begin();a!=sp.end();a++){}(原谅我手打代码,没有在编译器写,因为我是重新整理的)。
再运行一下你的程序让它们动起来吧,如果你够厉害的吧,肯定会有办法让它们在屏幕中怎么都停不下来!
补充:Sprite->setTexture()这个可以修改精灵的材质。
*****************************************************************************************
到这里,你是不是已经猜到微信打飞机飞机是怎么移动的了?只要再加一个碰撞检测就我们就可以实现了。碰撞检测?咱们不整这么高端了,其实就是for循环获取这子弹和飞机的位置,查看子弹精灵是否在飞机精灵的位置啦。
咱们不急,下面学一下标签(Label),它让我们可以展示打飞机的分数等。
Label的创建有很多种方法,下面简单介绍三种
Label::create
Label::createWithTTF
Label::createWithBMFont
学过了Sprite肯定能看懂下面代码。再说一遍,一定要自己去尝试一下。
//下面的代码是我学习了前面的时候自己去写的,多加尝试
还有一个Label::createWithBMFont("fonts/futura-48.fnt","只能是英文或者数字"),futura-48.fnt字体很漂亮,有需要的我再上传吧。
上面的代码还有很多我没提过的知识点,因为这些都是我学习的笔记,最近我时间不是很多,所以不会写得太细,有忽略掉的就自己动手去查吧!相信你的自学能力不会比我差。
setColor是设置颜色。enableShadow加阴影。enableOutline加描边。还有很多特性,大家有兴趣都自己尝试一下。
*********************************************************************************************
到这里一个场景典型的元素就介绍完了,Sprite和lable,下面来个硬菜吧!打飞机!
利用微信打飞机的素材,实现打飞机的基本功能。
有几个小细节说一下:
因为还没学习事件响应,这里提前学习一下单点触控。
在init添加下面这段代码,屏幕就可以响应单击事件了。
CC_CALLBACK_2表示回调函数接收两个参数。
onTouchBegan、onTouchMoved等在头文件声明,这几个函数是继承自基类Layer的。如果你不确定跟基类的函数名是否一致,请在声明的时候加override,表示重载。注意onTouchBegan是返回值类型是bool,其他的是void。
bool onTouchBegan (Touch*, Event*)override;
void onTouchMoved(Touch*, Event*)override;
getBoundingBox()是获取精灵所占的矩形大小,containsPoint()查看点是否在矩形内。知道了如果响应单点触控,这样就可以完全实现飞机的拖拽了。
我前面提到过定时器,每帧执行回调函数。可以把敌机存到一个数组里,每次遍历敌机数组,判断子弹的点是否在敌机中。如果是的话,就表示命中,然后在数组中删除敌机元素,在层中删除敌机精灵。
对精灵执行 sp->removeFromParentAndCleanup(true);可以在层中消除自身。
到这里应该可以写出简单的打飞机了。