Cocos2dx学习第八章(游戏实例之跑跑跑)

前端之家收集整理的这篇文章主要介绍了Cocos2dx学习第八章(游戏实例之跑跑跑)前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

这一章是一个简单的实例,当做练习,熟练一下代码。 首先介绍个地图软件,然后就是贴代码

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效果

猜你在找的Cocos2d-x相关文章