这一章是一个简单的实例,当做练习,熟练一下代码。 首先介绍个地图软件,然后就是贴代码。
这款软件用于生成tmx格式的地图文件,我们利用他来制作地图。
(1)新建一个文件,会弹出下面的提示框,填写自己想要的参数即可:
(2)把图片拖到图块窗口,图片包含我们需要的元素,这样,我们就可以利用图片的元素,对新建的文件进行简单的绘图。
(3)最后,点击图块的图片元素,再到新建的文件点击,就可以覆盖上面的格子,也就完成画图。
(4)注意要把生成的地图文件tmx和所用的图片放在resource文件夹下,以免找不到资源,报错,需要打开tmx文件,修改png的路径,直接名字就好了。
把tmx文件用文本编辑器打开,这个是最终版本:
(5)让生成的图片显示出来,只需加以下一句代码
TMXTiledMap* map = TMXTiledMap::create("level101.tmx");
this->addChild(map);
(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层将该格子覆盖到地图某个格子
玩家和怪物有一些共同的操作,我们将他定义为一个实体,作为父类,玩家和怪物都继承他。
#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 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,让精灵看起来是在跑的样子,几张图片轮流播放。
我们把让精灵向前跑作为一个类,为了以后方便扩展,我们首先实现控制器的父类。
#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;
}
主要是通过继承这个控制基类,来实现不同的控制需求。
最后,根据需求,制定我们的控制子类。
#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);//分发事件
}
#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
}