游戏各元素
声音
-
特别注意,声音素材不要有海报和歌词,以免造成没有声音的意外。
-
要添加声音相关的头文件SimpleAudioEngine.h。
-
音乐文件放在Resources文件夹下。
-
代码如下。
可拉伸图片功能-九妹图
-
可任意缩小放大拉伸图片,并且不会失真,会对图片进行自动填充。
-
添加头文件cocos-ext.h和using namespace cocos2d::extension命名空间。
-
Cocos2d-x将此功能封装在Scanle9Sprite中,如下。
-
完成。
九妹与按钮
-
利用九妹图的拉伸功能配合标签和按钮实现功能。
-
正常状态按钮运行如下。
-
单击状态按钮运行如下。
-
ControlButton相当于一个空壳,具有很多按钮具备的功能,九妹图和标签就是空壳里的东西。
-
newButton->setPreferredSize(Size(50,60)); //强制设置按钮大小,如果标题大小超过则还是标题设置的大小不会有影响…没啥子用
-
完成。
探索UI-CocoStudio
-
CocoStudio是一套专业的永久免费的游戏开发工具集 。
-
CocoStudio包含了游戏开发的UI编辑器、动画编辑器、场景编辑器、数据编辑器。
-
高效率,高质量,低风险,低成本。
-
HelloWorld实例如下。
-
与vs关联使用方法详解。
-
- 第一种,直接使用法,直接进入proj.win32文件夹使用.sln文件。
- 第二种,进入Resources文件夹下的res复制里面内容到已创建的Coco2dx工程的Resources文件夹下。
- 第一种,直接使用法,直接进入proj.win32文件夹使用.sln文件。
按钮监听事件
-
将用到的头文件和命名空间放在HelloWorld.h中,并声明一个监听回调函数和一个变量,如下。
#ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" #include "cocostudio/CocoStudio.h" #include "ui/CocosGUI.h" USING_NS_CC; using namespace cocos2d::ui; using namespace cocostudio::timeline; class HelloWorld : public cocos2d::Layer { public: // there's no 'id' in cpp, so we recommend returning the class instance pointer static cocos2d::Scene* createScene(); // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone virtual bool init(); void onClick(Ref*,TouchEventType type); //单击监听事件 ImageView* imageviews; CREATE_FUNC(HelloWorld); }; #endif // __HELLOWORLD_SCENE_H__
2. 使用getChildByName()函数获取UI按钮,并添加监听和定义监听回调函数,如下。
#include "HelloWorldScene.h"
Scene* 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;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
//启动UI
auto rootNode = CSLoader::createNode("MainScene.csb");
addChild(rootNode);
//获取控件对象
Button* button1 = (Button*)rootNode->getChildByName("Button_1"); //通过getChildByName()函数获取UI按钮
Button* button2 = (Button*)rootNode->getChildByName("Button_2"); //通过getChildByName()函数获取UI按钮
button2->setTitleText("Begin"); //改变按钮标题
imageviews = (ImageView*)rootNode->getChildByName("Image_1");
//添加按钮单击监听,需要宏函数toucheventselector传递参数
button1->addTouchEventListener(this, toucheventselector(HelloWorld::onClick));
return true;
}
void HelloWorld::onClick(Ref*, TouchEventType type)
{
switch (type)
{
case TouchEventType::TOUCH_EVENT_BEGAN: //单击事件开始时触发(按下的时候)
break;
case TouchEventType::TOUCH_EVENT_MOVED: //按下之后进行移动操作时触发
break;
case TouchEventType::TOUCH_EVENT_ENDED: //按下之后然后松开之后触发
if (imageviews->isVisible()) //判断图片是否显示
{
imageviews->setVisible(false); //隐藏图片
}
else
{
imageviews->setVisible(true); //显示图片
}
break;
case TouchEventType::TOUCH_EVENT_CANCELED: //因为特殊情况中断时触发
break;
}
}
血量条
-
例如英雄联盟的英雄血量条。
-
代码如下,参照按钮监听事件代码。
//在.cpp文件中添加如下代码 bool HelloWorld::init() { 。。。 //获取控件对象 。。。 xueliangtiao = (LoadingBar*)rootNode->getChildByName("xueliang"); //获取血量的进度条 //添加按钮单击监听,需要宏函数toucheventselector传递参数 。。。 button2->addTouchEventListener(this, toucheventselector(HelloWorld::onClick2)); //按钮2事件 return true; } void HelloWorld::onClick2(Ref*, TouchEventType type) { switch (type) { case TouchEventType::TOUCH_EVENT_BEGAN: //单击事件开始时触发(按下的时候) break; case TouchEventType::TOUCH_EVENT_MOVED: //按下之后进行移动操作时触发 break; case TouchEventType::TOUCH_EVENT_ENDED: //按下之后然后松开之后触发 xueliangtiao->setPercent(xueliangtiao->getPercent() + 1); //单击按钮2血量增加1 break; case TouchEventType::TOUCH_EVENT_CANCELED: //因为特殊情况中断时触发 break; } } //在头文件下添加 LoadingBar* xueliangtiao; //进度条对象 void onClick2(Ref*, TouchEventType type); //单击监听事件
-
运行效果如下。
-
完成。
活动起来
运动(精灵的移动)
-
MoveTo
bool HelloWorld::init() { if ( !Layer::init() ) { return false; } //启动UI auto rootNode = CSLoader::createNode("MainScene.csb"); addChild(rootNode); Size newSize = Director::getInstance()->getVisibleSize(); //获取窗口大小 //创建一个精灵 Sprite* newSprite=Sprite::create("name.png"); newSprite->setPosition(Point(newSize.width/2, newSize.height / 4)); //设置坐标 this->addChild(newSprite); //创建MoveTo动作对象,第一个参数是动作要执行的时间,第二个参数是执行到的目的地 MoveTo* moveTo = MoveTo::create(0.9f, Point(newSize.width / 2, newSize.height / 2)); //精灵执行的动作 newSprite->runAction(moveTo); return true; }
-
MoveBy
bool HelloWorld::init() { if ( !Layer::init() ) { return false; } //启动UI auto rootNode = CSLoader::createNode("MainScene.csb"); addChild(rootNode); Size newSize = Director::getInstance()->getVisibleSize(); //获取窗口大小 //创建一个精灵 Sprite* newSprite=Sprite::create("name.png"); newSprite->setPosition(Point(newSize.width/2, newSize.height / 4)); //设置坐标 this->addChild(newSprite); //创建MoveBy动作对象,第一个参数是动作要执行的时间,第二个参数是要移动的距离,这里是向X移动50,Y移动50 MoveBy* moveBy = MoveBy::create(0.9f, Point(200,200)); //精灵执行的动作 newSprite->runAction(moveBy); return true; }
Blink-精灵的闪烁
直接看代码,如下。
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
//启动UI
auto rootNode = CSLoader::createNode("MainScene.csb");
addChild(rootNode);
Size newSize = Director::getInstance()->getVisibleSize(); //获取窗口大小
//创建一个精灵
Sprite* newSprite=Sprite::create("name.png");
newSprite->setPosition(Point(newSize.width/2, newSize.height / 4)); //设置坐标
this->addChild(newSprite);
//创建Blink对象,第一个参数是动作持续时间,单位是秒,第二个参数是闪烁次数
Blink* blink = Blink::create(3.0f,9);
//精灵执行动作
newSprite->runAction(blink);
return true;
}
BezierTo和BezierBy-按照指定路线运动
-
BezierTo
bool HelloWorld::init() { if ( !Layer::init() ) { return false; } //启动UI auto rootNode = CSLoader::createNode("MainScene.csb"); addChild(rootNode); Size newSize = Director::getInstance()->getVisibleSize(); //获取窗口大小 //创建一个精灵 Sprite* newSprite=Sprite::create("name.png"); newSprite->setPosition(Point(newSize.width/2, newSize.height / 4)); //设置坐标 this->addChild(newSprite); //创建贝塞尔曲线配置 _ccBezierConfig bezier; bezier.controlPoint_1 = Point(100, 0); //波谷偏向值 相当于sinx的凹进去的部分 bezier.controlPoint_2 = Point(200, 300); //波峰偏向值 相当于sinx的凸出来的地方 bezier.endPosition = Point(500, 600); //动作终点 //创建CCBezierTo动作对象 BezierTo* bezierTo = BezierTo::create(4.0f, bezier); //精灵执行动作 newSprite->runAction(bezierTo); return true; }
-
BezierBy
//创建贝塞尔曲线配置 _ccBezierConfig bezier; bezier.controlPoint_1 = Point(100, 0); //波谷偏向值 抛物线底部(刚开始弯曲的地方) bezier.controlPoint_2 = Point(200, 300); //波峰偏向值 相当于抛物线高出的凸起 bezier.endPosition = Point(500, 600); //动作终点 //创建CCBezierTo动作对象 BezierBy* bezierTo = BezierTo::create(4.0f, bezier); //精灵执行动作 newSprite->runAction(bezierTo);
RepeatForever-重复动作
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
//启动UI
auto rootNode = CSLoader::createNode("MainScene.csb");
addChild(rootNode);
Size newSize = Director::getInstance()->getVisibleSize(); //获取窗口大小
//创建一个精灵
Sprite* newSprite=Sprite::create("name.png");
newSprite->setPosition(Point(newSize.width/2, newSize.height / 4)); //设置坐标
this->addChild(newSprite);
//创建一个JumoBy动作对象,会忘右方向跳动,第一个参数是时间,第二个是每次移动距离,如向
//X的方向移动50,y的方向移动30,第三个参数是跳的高度,第四个参数是每次跳跳多少下
JumpBy* jumpBy = JumpBy::create(3.0f, Point(50, 30), 100,1);
//以jumpby为参数创建一个永久重复性的动作
RepeatForever* forever = RepeatForever::create(jumpBy);
//以jumpby为参数创建一个指定次数重复性的动作
//Repeat* forever = Repeat::create(jumpBy,9);
//执行动作
newSprite->runAction(forever);
return true;
}
多种动作连续进行
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
//启动UI
auto rootNode = CSLoader::createNode("MainScene.csb");
addChild(rootNode);
Size newSize = Director::getInstance()->getVisibleSize(); //获取窗口大小
//创建一个精灵
Sprite* newSprite=Sprite::create("name.png");
newSprite->setPosition(Point(newSize.width/2, newSize.height / 4)); //设置坐标
this->addChild(newSprite);
//创建一个移动动作对象
MoveBy* moveBy = MoveBy::create(2.2f, Point(100,200));
//创建一个弹跳对象,弹跳高度是100,单次弹跳次数是
JumpBy* jumpBy = JumpBy::create(3.0f, Point(50, 30), 100, 2);
//创建一个旋转动作对象,参数分别是时间,X,Y
RotateBy* rotateBy = RotateBy::create(2.5f, 200, 200);
//创建组合动作对象,将上述动作连串起来,Spawn可以让所有动作同时执行
//Action* action = Spawn::create(moveBy, jumpBy, rotateBy, NULL);
//Action是所有动作父类,Spawn也是一个动作,不过此动作可包含若干个动作,需要用NULL结束。
////创建组合动作对象,将上述动作连串起来,Sequence可以让动作一个一个执行
Action* action = Sequence::create(moveBy, jumpBy, rotateBy, NULL);
//执行动作
newSprite->runAction(action);
return true;
}
动作的监听
动作结束监听
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
//启动UI
auto rootNode = CSLoader::createNode("MainScene.csb");
addChild(rootNode);
Size newSize = Director::getInstance()->getVisibleSize(); //获取窗口大小
//创建一个精灵
Sprite* newSprite=Sprite::create("name.png");
newSprite->setPosition(Point(newSize.width/2, newSize.height / 4)); //设置坐标
this->addChild(newSprite);
//移动动作
MoveTo* moveTo = MoveTo::create(10.0f, Point(newSize.width, newSize.height / 2));
//回调函数,CallFunc也是一个动作,只不过此动作是回调一个函数
auto back = [&]() { //[]符号表示要开始一个lambda函数,()里填写函数,{}是函数体内容
backoo(); //在.h文件中声明void backoo();
};
CallFunc* callFunc = CallFunc::create(back);
//组合两个动作
Action* action = Sequence::create(moveTo, callFunc, NULL);
//执行动作
newSprite->runAction(action);
return true;
}
void HelloWorld::backoo()
{
Size vsize = Director::getInstance()->getVisibleSize();
Label* label = Label::create("I am coming!","Arial",35);
label->setPosition(Point(vsize.width / 2, vsize.height / 2));
this->addChild(label);
}
C++的lambada函数
- []符号表示要开始一个lambda函数,如动作结束监听代码里的back;
- ()符号里表示要填写的函数。
- {}符号表示函数体。
- []表示不截取任何变量,[&]表示截取外部作用域中所有变量(只要变量没被释放就可在lambda中使用,但局部变量不可使用因为会被释放),[=]截取外部作用域所有变量且即使外部的值变了lambda函数里之前截取复制的值也不变。
- [=,&a]和[=]功能相同但只对外部变量a使用引用。
- [a]和[=]功能相同,a是外部变量,但是只针对变量a其余变量忽略。
屏幕触摸事件
事件详解如下
-
代码。
bool HelloWorld::init() { if (!Layer::init()) { return false; } //启动UI auto rootNode = CSLoader::createNode("MainScene.csb"); addChild(rootNode); //创建一个触摸监听 auto listener = EventListenerTouchOneByOne::create(); //触摸事件开始,相当于玩手机时我们手指按下的时候 listener->onTouchBegan = [](Touch* touch, Event* event) { Point pos1 = touch->getLocation(); //获取单击坐标,基于3D Point pos2 = touch->getLocationInView(); //获取单击坐标,基于2D,以左上角为原点 Point pos3 = Director::getInstance()->convertToGL(pos2); //获取单击坐标,基于cocos2dx,以左下角为原点 //打印坐标 log("Scene onTouchBeganPoint:pos1 x=%f,y=%f", pos1.x, pos1.y); log("Point:pos2 x=%f,y=%f", pos2.x, pos2.y); log("Point:pos3 x=%f,y=%f", pos3.x, pos3.y); return true; }; //触摸移动事件,相当于手指在屏幕滑动过程 listener->onTouchMoved = [=](Touch* touch, Event* event) { log("Scene onTouchMoved!"); }; //触摸事件结束,相当于手指松开离开屏幕 listener->onTouchEnded = [=](Touch* touch, Event* event) { log("Scene onTouchEnded!"); }; //还有onTouchCancelled触摸事件,其是打断触摸事件发生的事件,一般为系统层的消息,如来电话 //_eventDispatcher是事件管理器,相当于Director::getInstance()->getEventDispatcher()是一个单例类。 //addEventListenerWithSceneGraphPriority函数第一个参数是事件监听对象,当触摸事件发生时回调,第二个是绑定的对象。 //addEventListenerWithSceneGraphPriority(EventListener* a,Node* b);当node对象被释放监听事件注册也会被取消 _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); //注意:当多个事件同时触发会根据node的层次优先回调(越是上面的对象越先回调) return true; }
-
addEventListenerWithFixedPriority也是用于注册监听事件,但这个函数需要手动指定触摸事件回调的优先级,且需要手动取消监听事件。
多点触摸-多个手指同时操作
-
原始代码原理如下。
bool HelloWorld::init() { if (!Layer::init()) { return false; } //启动UI auto rootNode = CSLoader::createNode("MainScene.csb"); addChild(rootNode); //创建一个触摸监听,EventListenerTouchOneByOne->EventListenerTouchAllAtOnce auto listener = EventListenerTouchAllAtOnce::create(); //触摸事件开始,相当于玩手机时我们手指按下的时候,onTouchBegan->onTouchesBegan listener->onTouchesBegan = [](const std::vector<Touch*>& touches, Event* event) {}; //触摸移动事件,相当于手指在屏幕滑动过程 listener->onTouchesMoved = [=](const std::vector<Touch*>& touches, Event* event) {}; //触摸事件结束,相当于手指松开离开屏幕 listener->onTouchesEnded = [=](const std::vector<Touch*>& touches, Event* event) {}; _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); return true; }
-
实例如下,来展示多点触摸效果。
bool HelloWorld::init() { if (!Layer::init()) { return false; } //启动UI auto rootNode = CSLoader::createNode("MainScene.csb"); addChild(rootNode); Label* label1 = Label::create("", "Arial", 20); label1->setPosition(Point(400, 100)); this->addChild(label1, 1, 1); //添加标签进入场景,后面两个参数是对象绘制层次和对象Tag值 //绘制层次数值越小越优先被绘制,对象的Tag值就是一个int类型的数据,不过可用其做简单操作 Label* label2 = Label::create("", "Arial", 20); label2->setPosition(Point(400, 200)); this->addChild(label2, 1, 2); Label* label3 = Label::create("", "Arial", 20); label3->setPosition(Point(400, 300)); this->addChild(label3, 1, 3); //创建一个触摸监听,EventListenerTouchOneByOne->EventListenerTouchAllAtOnce auto listener = EventListenerTouchAllAtOnce::create(); //触摸事件开始,相当于玩手机时我们手指按下的时候,onTouchBegan->onTouchesBegan listener->onTouchesBegan = [&](const std::vector<Touch*>& touches, Event* event) { auto logLabel = (Label*)this->getChildByTag(1); //用getChildByTag找Tag值 int num = touches.size(); logLabel->setString(Value(num).asString() + "Touches:"); }; //触摸移动事件,相当于手指在屏幕滑动过程 listener->onTouchesMoved = [&](const std::vector<Touch*>& touches, Event* event) { auto logLabel = (Label*)this->getChildByTag(2); int num = touches.size(); std::string text = Value(num).asString() + "Touches"; //创建std::string以打印日志体现多点触摸事件 for (auto& touch : touches) { auto lacation = touch->getLocation(); text += "[touchID" + Value(touch->getID()).asString() + "],"; //使用touch对象的getID函数确定手指 } //第一个触碰屏幕的手指产生的touch对象的ID就是0 logLabel->setString(text); }; //触摸事件结束,相当于手指松开离开屏幕 listener->onTouchesEnded = [&](const std::vector<Touch*>& touches, Event* event) { auto logLabel = (Label*)this->getChildByTag(3); int num = touches.size(); logLabel->setString(Value(num).asString() + "Touches:"); }; _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this); return true; }
-
第一个标签表示onTouchesBegan事件,1Touches表示该事件touches参数里只有一个Touch对象。
-
第二个标签表示onTouchesMoved事件,5Touches表示该事件touches参数里有5个Touch对象,5个手指同时移动。
-
第三行标签表示onToucherEnded事件,1Touches表示同第一个。
-
因为触摸事件结束时对onToucherBegan,onTouchesEnded和onTouchesCancelled事件都会判断Touch的ID是否是新的ID,然后进行再传递。所以这些事件是指有新的手指单击或离开才能触发,但onTouchesMoved在屏幕上的手指移动时才触发事件,所以会有多个对象。
End
实例该做还是得做~
~