这一章是一个简单的实例,当做练习,熟练一下代码。 首先介绍个地图软件,然后就是贴代码。
1.Tiled Map Editor软件
这款软件用于生成tmx格式的地图文件,我们利用他来制作地图。
(1)新建一个文件,会弹出下面的提示框,填写自己想要的参数即可:
(2)把图片拖到图块窗口,图片包含我们需要的元素,这样,我们就可以利用图片的元素,对新建的文件进行简单的绘图。
(3)最后,点击图块的图片元素,再到新建的文件点击,就可以覆盖上面的格子,也就完成画图。
(4)注意要把生成的地图文件tmx和所用的图片放在resource文件夹下,以免找不到资源,报错,需要打开tmx文件,修改png的路径,直接名字就好了。
把tmx文件用文本编辑器打开,这个是最终版本:
<?xml version="1.0" encoding="UTF-8"?> <map version="1.0" orientation="orthogonal" renderorder="right-up" width="60" height="10" tilewidth="70" tileheight="70"> <tileset firstgid="1" name="map" tilewidth="70" tileheight="70"> <image source="map.png" width="980" height="490"/> </tileset> <tileset firstgid="99" name="Meta_tiles" tilewidth="70" tileheight="70"> <image source="Meta_tiles.png" width="280" height="70"/> <tile id="0"> <properties> <property name="Collidable" value="true"/> </properties> </tile> <tile id="1"> <properties> <property name="food" value="true"/> </properties> </tile> <tile id="2"> <properties> <property name="win" value="true"/> </properties> </tile> </tileset> <layer name="块层 1" width="60" height="10"> <data encoding="base64" compression="zlib"> eJxjYBhZwGOEYW8g9qJKyA1+YM0w6t/hDEb9O7zBqH+HNwD5d6DrQ3pikH8B3KEnZA== </data> </layer> <objectgroup name="objects"> <object name="PlayerPoint" x="72" y="353.333" width="66.6667" height="70.6667"/> <object x="94.6667" y="394.667"/> </objectgroup> <layer name="barrier" width="60" height="10"> <data encoding="base64" compression="zlib"> eJxjYBgFowACeAfaATQEvEgYm9hw8zuyv/iQxGCAD0PH0Abo/uVjIC5+h2r84/IvjD3cAK78O5LAcC6vhisAAJOfAis= </data> </layer> <layer name="Meta" width="60" height="10"> <data encoding="base64" compression="zlib"> eJxjYBgFowACUgbaATQEKUgYm9hw8zuyv5KRxGAgGUPH0Abo/k1mIC1+U2nqOuoDXP6FsWFguMQzrvw7ksBwLq+GKwAAN1oP/Q== </data> </layer> </map>(5)让生成的图片显示出来,只需加以下一句代码
TMXTiledMap* map = TMXTiledMap::create("level101.tmx"); this->addChild(map);
(6)添加对象图层,命名为Objects,如下图:
(6)故名思义,在对象绘制一个矩形,它代表一个对象,顺带设置他的属性,名称。
TMXObjectGroup* objGroup = map->getObjectGroup("objects"); ValueMap = playerPointMap = objGroup->getObject(PlayerPoint); float playerx = playerPointMap.at("x").asFloat(); float playery = playerPointMap.at("y").asFloat(); mPlayer->setPosition(Point(playerx,playery));
(7)在图层菜单,我们再添加两个图层,首先添加的层命名为barrier,把水果放上去,其次,再添加Meta层,把红色格子放上去作为障碍物,并设置红色格子的属性,自己添加属性。
(8)我们把水果添加为食物,只要碰到,就表示吃了水果,水果就自动消失,需要添加属性food。
(9)最后,我们也添加一个win属性的水果,迟到了就表示游戏结束,步骤和上面的一样,然后选中Meta层将该格子覆盖到地图某个格子
2.贴代码由此开始
2.1.创建主角
玩家和怪物有一些共同的操作,我们将他定义为一个实体,作为父类,玩家和怪物都继承他。
#include"cocos2d.h" #include"Controller.h" using namespace cocos2d; class Entity:public Node,public ControllerListener { public: void bindSprite(Sprite* sprite); void setController(Controller* controller); virtual void setTagPosition(int x,int y); virtual Point getTagPosition(); protected: Sprite* m_sprite;//bind this sprite Controller* m_controller; }; #endif cpp #include"Entity.h" void Entity::bindSprite(Sprite* sprite) { m_sprite = sprite; this->addChild(m_sprite); } void Entity::setController(Controller* controller)//important { this->m_controller = controller; m_controller->setControllerListener(this); } void Entity::setTagPosition(int x,int y)//设置精灵的坐标 { setPosition(x,y); } Point Entity::getTagPosition()//获取精灵的坐标 { return getPosition(); }上面的实体类很简单,用bindsprite来绑定精灵。其余的方法有ControllerListener继承而来,下面会讲到。
#ifndef _CONTROLLERLISTENER_H_ #define _CONTROLLERlISTENER_H_ #include"cocos2d.h" using namespace cocos2d; class ControllerListener { public: virtual void setTagPosition(int x,int y) = 0; virtual Point getTagPosition() = 0; }; #endif
下面,我们来定义一个玩家类,Player,继承自Entity。
#ifndef _PLAYER_H_ #define _PLAYER_H_ #include"Entity.h" class Player:public Entity { public: CREATE_FUNC(Player); virtual bool init(); void run(); void setTiledMap(TMXTiledMap* map); void setViewPointByPlayer(); virtual void setTagPosition(int x,int y); private: TMXTiledMap* m_map;//用来加载地图文件 bool isJumping; TMXLayer* Meta;//检测碰撞的地图层 Point tileCoordForPosition(Point pos);//将像素坐标转换为地图格子坐标 }; #endif cpp文件 #include"Player.h" #include"WinScene.h" bool Player::init() { isJumping = false; return true; } void Player::run()//animation { SpriteFrameCache* framecache = SpriteFrameCache::getInstance(); framecache->addSpriteFramesWithFile("boys.plist","boys.png"); SpriteFrame* frame = NULL; Vector<SpriteFrame*> framelist; for(int i = 1; i <= 15; i++) { frame = framecache->getSpriteFrameByName(StringUtils::format("run%d.png",i)); framelist.pushBack(frame); } // Animation* animation = Animation::createWithSpriteFrames(framelist); animation->setLoops(-1); animation->setDelayPerUnit(0.08F); // Animate* animate = Animate::create(animation); m_sprite->runAction(animate); } void Player::setTiledMap(TMXTiledMap* map)//加载tmx地图文件 { this->m_map = map; this->Meta = m_map->getLayer("Meta");//直接获取我们加的图层 this->Meta->setVisible(false); } void Player::setViewPointByPlayer()//让主角始终在屏幕中央,地图向后滑动 { if(m_sprite == NULL) return; Layer* parent = (Layer*)getParent(); Size tiledSize = m_map->getTileSize();//地图方块数量 Size mapTiledNum = m_map->getMapSize();//地图单个格子大小 Size MapSize = Size(mapTiledNum.width * tiledSize.width,mapTiledNum.height * tiledSize.height);//整个地图的大小 Size visibleSize = Director::getInstance()->getVisibleSize(); Point spritepos = getPosition(); float x = std::max(spritepos.x,visibleSize.width/2); float y = std::max(spritepos.y,visibleSize.height/2); x = std::min(x,MapSize.width - visibleSize.width/2); y = std::min(y,MapSize.height - visibleSize.height/2); Point destPos = Point(x,y); Point centerpos = Point(visibleSize.width/2,visibleSize.height/2); Point viewPos = centerpos - destPos; parent->setPosition(viewPos); } void Player::setTagPosition(int x,int y)//设置精灵的位置,里面包含对碰撞的判断,食物的判断检测 { Size spritesize = m_sprite->getContentSize(); Point dstPos = Point(x + spritesize.width/2,y);//主角前方的坐标 Point tiledPos = tileCoordForPosition(Point(dstPos.x,dstPos.y));//前方坐标在地图里面格子的位置 int tiledGid = Meta->getTileGIDAt(tiledPos);//获取地图格子唯一的标志 if(tiledGid != 0)//不为0表示存在这个格子 { Value properties = m_map->getPropertiesForGID(tiledGid);//获取格子的属性 //Value prop = properties.asValueMap().at("Collidable");//我们加的属性 ValueMap propMap = properties.asValueMap(); if(propMap.find("Collidable") != propMap.end()) { Value prop = propMap.at("Collidable"); if(prop.asString().compare("true") == 0 && isJumping == false) { isJumping = true; auto jumpBy = JumpBy::create(0.5f,Point(-100,0),80,1); CallFunc* callfunc = CallFunc::create([&]() { isJumping = false; }); auto action = Sequence::create(jumpBy,callfunc,NULL); this->runAction(action); } } //food if(propMap.find("food") != propMap.end()) { Value prop = propMap.at("food"); if(prop.asString().compare("true") == 0) { TMXLayer* barrier = m_map->getLayer("barrier"); barrier->removeTileAt(tiledPos); } } if(propMap.find("win") != propMap.end()) { Value prop = propMap.at("win"); if(prop.asString().compare("true") == 0) { TMXLayer* barrier = m_map->getLayer("barrier"); barrier->removeTileAt(tiledPos); Director::getInstance()->replaceScene(WinScene::createScene()); } } } Entity::setTagPosition(x,y); setViewPointByPlayer(); } Point Player::tileCoordForPosition(Point pos)//将像素坐标转换为地图格子坐标 { Size mapTiledNum = m_map->getMapSize();//地图单个格子大小 Size tiledSize = m_map->getTileSize();//地图方块数量 int x = pos.x/tiledSize.width; //cocos2d-x的默认y坐标是由下至上的,所以要做一个相减操作 int y = (700 - pos.y)/tiledSize.height; //格子坐标从0开始计算 if(x > 0) { x-=1; } if(y > 0) { y-=0; } return Point(x,y); }run函数,其实就是一个animation,让精灵看起来是在跑的样子,几张图片轮流播放。
2.2添加控制器
我们把让精灵向前跑作为一个类,为了以后方便扩展,我们首先实现控制器的父类。
#ifndef _CONTROLLER_H_ #define _CONTROLLER_H_ #include"cocos2d.h" #include"ControllerListener.h" using namespace cocos2d; class Controller:public Node { public: void setControllerListener(ControllerListener* controllerListener); protected: ControllerListener* m_controllerListener; }; #endif #include"Controller.h" void Controller::setControllerListener(ControllerListener* controllerListener) { this->m_controllerListener = controllerListener; }主要是通过继承这个控制基类,来实现不同的控制需求。
2.3子类控制器
最后,根据需求,制定我们的控制子类。
#ifndef _SIMPLECONTROLLER_H_ #define _SIMPLECONTROLLER_H_ #include"cocos2d.h" #include"Controller.h" using namespace cocos2d; class SimpleMoveController:public Controller { public: CREATE_FUNC(SimpleMoveController); virtual bool init(); virtual void update(float dt); void setxSpeed(int xspeed); void setySpeed(int yspeed); private: int x_speed; int y_speed; void registerTouchEvent(); }; #endif
#include"SimpleMoveController.h" bool SimpleMoveController::init() { this->x_speed = 0; this->y_speed = 0; registerTouchEvent(); this->scheduleUpdate(); return true; } void SimpleMoveController::update(float dt)//update函数,需要在init()那里调用this->scheduleUpdate()才会调用 { if(m_controllerListener == NULL) return; Point pos = m_controllerListener->getTagPosition(); pos.x += x_speed; pos.y += y_speed; m_controllerListener->setTagPosition(pos.x,pos.y); } void SimpleMoveController::setxSpeed(int speed) { this->x_speed = speed; } void SimpleMoveController::setySpeed(int speed) { this->y_speed = speed; } void SimpleMoveController::registerTouchEvent()//触摸事件处理 { auto listener = EventListenerTouchOneByOne::create(); listener->onTouchBegan = [](Touch* touch,Event* event) { return true; }; listener->onTouchMoved = [&](Touch* touch,Event* event) { Point touchPos = Director::getInstance()->convertToGL(touch->getLocationInView()); Point pos = m_controllerListener->getTagPosition();//记得引用时,luba里面 【&】 int speed = 0; if(touchPos.y > pos.y) { speed = 1; } else { speed = -1; } setySpeed(speed); }; listener->onTouchEnded = [&](Touch* touch,Event* event) { setySpeed(0); }; _eventDispatcher->addEventListenerWithSceneGraPHPriority(listener,this);//分发事件 }
2.4场景代码
#include "HelloWorldScene.h" #include "Player.h" #include "SimpleMoveController.h" USING_NS_CC; 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() { ////////////////////////////// // 1. super init first if ( !Layer::init() ) { return false; } TMXTiledMap* map = TMXTiledMap::create("level101.tmx");//加载tmx地图文件 this->addChild(map);//将他加到场景 addPlayer(map);//把玩家添加到地图 return true; } void HelloWorld::addPlayer(TMXTiledMap* map) { Size visibleSize = Director::getInstance()->getVisibleSize(); Sprite* playerSprite = Sprite::create("Player.png"); //将精灵绑定到玩家对象上面 Player* mPlayer = Player::create(); mPlayer->bindSprite(playerSprite); mPlayer->run(); mPlayer->setTiledMap(map); mPlayer->setPosition(Point(100,visibleSize.height/2)); map->addChild(mPlayer);//将Player加到地图 //给玩家设置控制器 SimpleMoveController* smc = SimpleMoveController::create(); smc->setxSpeed(1); smc->setySpeed(0); this->addChild(smc);//加到场景,因为继承自Node mPlayer->setController(smc); } void HelloWorld::menuCloseCallback(Ref* pSender) { #if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert"); return; #endif Director::getInstance()->end(); #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS) exit(0); #endif }
2.5效果图