您的位置:首页 > 移动开发 > Cocos引擎

cocos2dx 3.1从零学习(一)——入门篇(一天学会打飞机)

2016-04-03 19:18 513 查看
没办法,浏览这么高,为啥没人投票呢?朋友们,我这篇文章參加了csdn博文大赛。喜欢的来点个赞吧!

点击:http://vote.blog.csdn.net/Article/Details?articleid=32726283

我们有C++基础。学习引擎总是急于求成,想立刻做出一款简单的游戏给朋友玩。

可是我们往往看了非常多资料却一直不知道怎样下手去写。有时候仅仅要能走出第一步我们就会游刃有余。可是眼高手低的我们不是大神,须要有人指引一下。这里我就写一下我是怎样入门学习cocos2dx3.1的,给大家參考一下。

假设你想第一天就写出微信打飞机,请耐心去阅读。我也是一个菜鸟,博客难免粗糙和出错,请大家谅解。加油吧!

我们创建project后总会自带一个HelloWorld类。短短的几行代码就出来了一个游戏的雏形,请问我们真的理解它了吗?假设我们能早一点弄明确这几行代码,我们也许会比方今走得更远。

理解HelloWorld类

HelloWorld去掉退出button仅仅有以下三个函数。

static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(HelloWorld);//一定要自己看源代码

学习之前我要强调一遍。这三个方法一定要做到透彻理解和重写。

由于全部的游戏场景都须要这三个函数。

创建游戏HelloWorld场景的时候,仅仅须要在AppDelegate写一句: Helloworld::createScene();

请看它的实现

// 'scene' is an autorelease object
auto scene = Scene::create();

// 'layer' is an autorelease object
auto layer = HelloWorld::create();

// add layer as a child to scene
scene->addChild(layer);

// return the scene
return scene;


首先新建一个新场景,然后create一个HelloWorld层并加入到新创建的场景,最后返回这个场景给AppDelegate。

一个场景的创建和展示就是这么简单,可是你有没有发现HelloWorld中没有create呢?而且没有调用init()方法呢?我们在HelloWorld中没有在不论什么地方看到调用init初始化,那么这是在哪里调用的呢?

请看CREATE_FUNC的宏定义:

#defineCREATE_FUNC(__TYPE__) \
static __TYPE__*create() \
{ \
__TYPE__ *pRet = new __TYPE__(); \
if (pRet && pRet->init()) \
{ \
pRet->autorelease(); \
return pRet; \
} \
else \
{ \
delete pRet; \
pRet = NULL; \
return NULL; \
} \
}

\表示换行。由于宏定义代码假设放同一行阅读不方便。

能够看到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宏定义

MainScene *MainScene::create1(){
auto mainS = new MainScene;
if ( mainS && mainS->init())
{
mainS->autorelease();
return mainS;
}
else
{
delete mainS;
mainS = NULL;
return nullptr;
}
}


改动AppDegate.cpp里面的HelloWorld::createScene();改为
auto scene = MainScene::createScene();

再使用一下自己写的create1: auto layer=MainScene::create1();

这样我们就全然掌握了游戏场景的创建和它的原理。事实上更重要的是我们认识到了应该去怎么学,cocos2dx引擎使用了大量的宏定义,我们一定不能仅仅追求表面的使用方法。而应该深入下去学习宏实现了那些东西。

特别是到后面的内存管理更是如此。

总结一下。也再反复一遍。全部游戏场景的基础都是这三句代码:

static cocos2d::Scene* createScene();
virtual bool init();
CREATE_FUNC(HelloWorld);


我在后面每一天新加场景都要手写一遍,一定要搞把这三句代码以及实现牢记于心。后面的学习中还会遇到大量的宏,我们要学会跟踪宏定义。细致阅读每一段代码。这样我们才干学懂而不是简单的学会。

好,后面接下来完好我们的第一个游戏场景。如今新建的场景是空白的,什么都没有。我们要尝试往场景中加入各种展示元素。

这里有三个定义要搞清楚,场景(scene)、层(layer)、精灵(sprite)。(原谅我表达能力有限。大家最好先阅读一本cocos2dx的书籍,把一些理论知识弄明确)。

场景能够包括多个层,层能够包括多个精灵。

精灵能够是我们在游戏看到的全部的元素,比方button、血量条、人物等。

比方酷跑中,能够看到近处的背景精灵移动快,远处的背景精灵移动慢,我们能够加入两个层到场景中,一个层中循环高速的精灵背景,一个循环慢速的精灵背景。这样就easy理解它们的概念了吧。

进入正题

Sprite精灵创建

假设你是新手,一定要把以下的代码自己敲一下。

磨刀不误砍柴工,如今操心写程序耽误时间。以后仅仅会耽误很多其它的时间。

首先在resource目录下放一张图片00191880.jpg(华为入职时的照片。工号191880,从来没改过^_^),

然后创建精灵。展示这张图片。

三句,非常easy,加入到init()函数中吧!

auto sprite =Sprite::create("00191880.jpg");//auto是C++11的自己主动判断变量类型
sprite->setPosition(200, 200);//设置这个精灵在屏幕的位置
this->addChild(sprite);//把这个精灵加入到当前层中。


我没学过OC,我曾经做windowsclient,感觉这样的写法非常不习惯,入乡随俗吧,假设你连this也不知道是什么意思的话,建议你看一下C++Primer,这本书非常重要。

以下是我练习的代码。自己尝试一下吧!

auto spriteA =Sprite::create("1.png");
auto spriteB =Sprite::create("2.png");
auto spriteC =Sprite::create("3.png");
this->addChild(spriteA);
this->addChild(spriteB);
this->addChild(spriteC);
Size visibleSize =Director::getInstance()->getVisibleSize();
spriteA->setPosition(visibleSize.width /4-30, visibleSize.height / 2);
spriteB->setPosition(visibleSize.width /4 * 3+32, visibleSize.height / 2);
spriteC->setPosition(visibleSize.width /2, visibleSize.height / 8 * 7+50);
spriteA->setScale(1.5);
spriteB->setScale(1.5);
spriteC->setScale(1.5);

假设你阅读过cocos2dx的书籍或者百度一下的话。相信你上面的代码一定看得懂。Director::getInstance()是一个单例,获取整个游戏的导演类。我不会告诉你后面的getVisibleSize()、getWinSize()还有setScale()是什么意思的,希望你能养成自己动手去查的习惯。

以下写一点小菜吧。加入的精灵不会动是不是非常没意思?以下就让图片动起来:

在init里面加入

this->schedule(schedule_selector(Second::myupdate));


这是一个定时器,每隔一段时间会运行myupdate函数。

myupdate的定义例如以下(我不会告诉你schedule后面还能够再加一个參数表示隔多久运行一次的,自己查去吧):

voidSecond::myupdate(float f){//注意有一个float f 形參
auto sp = this->getChildren();//获取这个层中全部的孩子,也就是全部的精灵,看不懂?别逗我了,点进去看源代码吧。注意它的返回值类型。

for (auto a: sp)
{
a->setPosition(a->getPosition().x, a->getPosition().y - 2);
}
}

上面这段代码就是移动刚才你加入的全部的精灵。

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肯定能看懂以下代码。再说一遍。一定要自己去尝试一下。

std::string words = "三翻四复";//windows下是不支持中文的,xcode支持。
auto label = Label::create(words,"STHeiti", 30);//黑体,三十号
this->addChild(label);

TTFConfigconfig("fonts/barnacle.ttf", 25);//TTF字体
auto labelTTF =Label::createWithTTF(config, "Hello ");
labelTTF->setPosition(333,333);
labelTTF->setColor(Color3B(255, 0, 0));
this->addChild(labelTTF);

auto labelTTF1 =Label::createWithTTF("ABCDEFG", "fonts/16.ttf", 44);
labelTTF1->setTextColor(Color4B(255, 0,0, 255));
labelTTF1->setPosition(444, 444);
labelTTF1->enableShadow(Color4B::BLUE,Size(10,-10));
labelTTF1->enableOutline(Color4B::GREEN,3);
labelTTF1->enableGlow(Color4B::BLACK);
this->addChild(labelTTF1);


//以下的代码是我学习了前面的时候自己去写的。多加尝试

std::vector<std::string> names ={"AAAAAAAA","BBBBBBBBBB","CCCCCCCCCCC","DDDDDDDDDDDD"};
for (auto str : names){
auto tmpLabel = Label::create(str,"STHeiti", 40);
tmpLabel->setPosition(visibleSize.width / 2,visibleSize.height-i*45);
tmpLabel->setColor(Color3B(rand() /255, rand() / 255, rand() / 255));
tmpLabel->setRotation(rand()/180);
this->addChild(tmpLabel);
}


另一个Label::createWithBMFont("fonts/futura-48.fnt","仅仅能是英文或者数字")。futura-48.fnt字体非常美丽,有须要的我再上传吧。

上面的代码还有非常多我没提过的知识点,由于这些都是我学习的笔记,近期我时间不是非常多,所以不会写得太细,有忽略掉的就自己动手去查吧!

相信你的自学能力不会比我差。

setColor是设置颜色。enableShadow加阴影。enableOutline加描边。还有非常多特性。大家有兴趣都自己尝试一下。

*********************************************************************************************

開始打飞机

到这里一个场景典型的元素就介绍完了。Sprite和lable,以下来个硬菜吧!打飞机!

利用微信打飞机的素材。实现打飞机的基本功能。

有几个小细节说一下:

1:使用鼠标拖动飞机。

由于还没学习事件响应,这里提前学习一下单点触控。

在init加入以下这段代码,屏幕就能够响应单击事件了。

auto listen =EventListenerTouchOneByOne::create();
listen->onTouchBegan =CC_CALLBACK_2(GameScene::onTouchBegan, this);
listen->onTouchMoved =CC_CALLBACK_2(GameScene::onTouchMoved, this);
listen->setSwallowTouches(true);
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(listen,this);

CC_CALLBACK_2表示回调函数接收两个參数。

onTouchBegan、onTouchMoved等在头文件声明,这几个函数是继承自基类Layer的。假设你不确定跟基类的函数名是否一致,请在声明的时候加override,表示重载。

注意onTouchBegan是返回值类型是bool,其它的是void。

bool onTouchBegan (Touch*, Event*)override;

void onTouchMoved(Touch*, Event*)override;

voidGameScene::onTouchMoved(Touch* pTouch, Event* pEvent){
Point touch =pTouch->getLocation();//返回点击的位置
Rect rectPlayer =spPlayer->getBoundingBox();//看返回值类型。应该知道这个是飞机所占矩形区域的大小

if(rectPlayer.containsPoint(touch)){//假设点击的点在这个矩形区域内就能够对飞机进行拖动
Point temp = pTouch->getDelta();
spPlayer->setPosition(spPlayer->getPosition() + temp);
}

}

getBoundingBox()是获取精灵所占的矩形大小。containsPoint()查看点是否在矩形内。知道了假设响应单点触控,这样就能够全然实现飞机的拖拽了。

2.怎样推断子弹是否命中飞机?

我前面提到过定时器。每帧运行回调函数。

能够把敌机存到一个数组里,每次遍历敌机数组,推断子弹的点是否在敌机中。

假设是的话。就表示命中,然后在数组中删除敌机元素。在层中删除敌机精灵。

对精灵运行 sp->removeFromParentAndCleanup(true);能够在层中消除自身。

到这里应该能够写出简单的打飞机了。

PS:假设你使用vector,然后erase在迭代器中删除。

那请注意正确使用STL遍历时的erase。

见我的博客:STL各种容器怎样正确的erase
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: