vardmm game playerrPos = this.game.player.getPosition();

1467人阅读
cocos creator记录(3)
本人也是第一次接触cocos creator,现在也正处于学习阶段,按照官网的教程一点一点来练习。下面是练习官网的第一个实例:
《Star.js》
cc.Class({
& & extends: cc.Component,
& & properties: {
& & & & pickRadius: 0
& & // use this for initialization
& & onLoad: function () {
& & getPlayerDistance: function(){
& & & & //getPosition()和position是一个意思,一个是函数,一个是属性
& & & & var playerPos = this.mainlogic.player.getPosition();//mainlogic表示的就是脚本Game中的实例
& & & & var dist = cc.pDistance(this.node.position, playerPos);//cc.pDistance(v1, v2)返回指定两个向量之间的距离
& & onPicked: function(){
& & & & this.mainlogic.spawnNewStar();//创建一个星星
& & & & this.mainlogic.gainScore();
& & & & this.node.destroy();//释放该对象,并释放所有它对其他对象的引用
& & // called every frame, uncomment this function to activate update callback
& & update: function (dt) {
& & & & if(this.getPlayerDistance() & this.pickRadius){
& & & & & & this.onPicked();
& & & & & &
& & & & var opacityRatio = 1 - this.mainlogic.timer/this.mainlogic.starD
& & & & var minOpacity = 50;
& & & & //Math.floor()用作向下取整
& & & & //Math.ceil()用作向上取整
& & & & //Math.round()四舍五入取整
& & & & this.node.opacity = minOpacity + Math.floor(opacityRatio * (255 - minOpacity));
《Game.js》
cc.Class({
& & extends: cc.Component,
& & properties: {
& & & & starPrefab: {
& & & & & & default: null,
& & & & & & type: cc.Prefab
& & & & },
& & & & maxStarDuration: 0,
& & & & minStarDuration: 0,
& & & & ground: {
& & & & & & default: null,
& & & & & & type: cc.Node
& & & & },
& & & & player: {
& & & & & & default: null,
& & & & & & type: cc.Node
& & & & },
& & & & scoreDisplay: {
& & & & & & default: null,
& & & & & & type: cc.Label
& & & & },&
& & & & scoreAudio: {
& & & & & & default: null,
& & & & & & url: cc.AudioClip
& & // use this for initialization
& & onLoad: function () {
& & & & //获取地平面的y轴坐标,node属性里有x(x轴坐标),y(y轴坐标),width(宽度),height(高度)
& & & & this.groundY = this.ground.y + this.ground.height/2; &&
& & & & //初始化定时器
& & & & this.timer = 0;
& & & & this.starDuration = 0;
& & & & //生成一个新的星星
& & & & this.spawnNewStar();
& & & & this.score = 0;
& & spawnNewStar: function(){
& & & & //使用给定的模板在场景中生成一个新节点
& & & & var newStar = cc.instantiate(this.starPrefab); //iinstantiate是复制给定的对象
& & & & //将新增的节点添加到Canvas节点下面
& & & & this.node.addChild(newStar);
& & & & //为星星设置一个随机位置
& & & & newStar.setPosition(this.getNewStarPosition());
& & & & //将Game组件的实例传入星星组件
& & & & newStar.getComponent(&Star&).mainlogic = & & &//mainlogic是脚本Star中的一个变量
& & & & //重置定时器
& & & & this.starDuration = this.minStarDuration + cc.random0To1() * (this.maxStarDuration - this.minStarDuration);
& & & & this.timer = 0;
& & getNewStarPosition: function(){ & & & & & & & & & & & & & &
& & & & var randX = 0;
& & & & //根据地平面位置和主角跳跃高度,随机得到一个星星的y坐标
& & & & //cc.random0To1()是返回一个0到1之间的随机数
& & & & //getComponent获取节点上指定类型的组件,如果节点有附加指定类型的组件,则返回,如果没有则为空,传入的参数也可以是脚本的额名字
& & & & var randY = this.groundY + cc.random0To1() * this.player.getComponent(&Player&).jumpHeight + 50;
& & & & //根据屏幕宽度,随机得到一个星星x坐标, randomMinus1To1()是返回一个-1到1之间的随机数
& & & & var maxX = this.node.width/2;
& & & & randX = cc.randomMinus1To1() * maxX;
& & & & //返回星星的坐标
& & & & return cc.p(randX, randY);
& & // called every frame, uncomment this function to activate update callback
& & update: function (dt) {
& & & & //每帧更新定时器,超过限度还没有生成新的星星,则判定游戏失败
& & & & if(this.timer & this.starDuration){
& & & & & & this.gameOver();
& & & & & &
& & & & this.timer +=
& & gameOver: function(){
& & & & this.player.stopAllActions();//停止player节点的跳跃动作
& & & & cc.director.loadScene(&game&);//通过场景名进行加载,&game&就是场景的名字,也在资源管理器中进行查看
& & gainScore: function(){
& & & & this.score += 1;
& & & & //string是Label的一个属性(显示文本内容),toString()是将数字转为字符串
& & & & this.scoreDisplay.string = &Score: & + this.score.toString();
& & & & //播放得分音效
& & & & cc.audioEngine.playEffect(this.scoreAudio, false);
《Player.js》
cc.Class({
& & extends: cc.Component,
& & properties: {
& & & & //主角跳跃高度
& & & & jumpHeight: 0,
& & & & //主角跳跃持续时间
& & & & jumpDuration: 0,
& & & & //最大移动距离
& & & & maxMoveSpeed: 0,&
& & & & //加速度
& & & & accel: 0,
& & & & //跳跃音效
& & & & jumpAudio: {
& & & & & & default: null,
& & & & & & url: cc.AudioClip
& & setJumpAction: function(){
& & & & var jumpUp = cc.moveBy(this.jumpDuration, cc.p(0, this.jumpHeight)).easing(cc.easeCubicActionOut());
& & & & var jumpDown = cc.moveBy(this.jumpDuration, cc.p(0, -this.jumpHeight)).easing(cc.easeCubicActionIn());
& & & & var callback = cc.callFunc(this.playJumpSound, this);
& & & & return cc.repeatForever(cc.sequence(jumpUp, jumpDown, callback));
& & playJumpSound: function(){
& & & & cc.audioEngine.playEffect(this.jumpAudio, false);
& & setInputControl: function(){
& & & & var self =
& & & & cc.eventManager.addListener({
& & & & & & event: cc.EventListener.KEYBOARD,
& & & & & & onKeyPressed: function(keyCode, event){
& & & & & & & & switch(keyCode){
& & & & & & & & & & case cc.KEY.a:
& & & & & & & & & & & & self.accLeft =
& & & & & & & & & & & & self.accRight =
& & & & & & & & & & & &
& & & & & & & & & & case cc.KEY.d:
& & & & & & & & & & & & self.accLeft =
& & & & & & & & & & & & self.accRight =
& & & & & & & & & & & &
& & & & & & & & }
& & & & & & },
& & & & & & onKeyReleased: function(keyCode, event){
& & & & & & & & switch(keyCode){
& & & & & & & & & & case cc.KEY.a:
& & & & & & & & & & & & self.accLeft =
& & & & & & & & & & & &
& & & & & & & & & & case cc.KEY.d:
& & & & & & & & & & & & self.accRight =
& & & & & & & & & & & &
& & & & & & & & }
& & & & & & }
& & & & }, self.node);
& & // use this for initialization
& & onLoad: function () {
& & & & this.jumpAction = this.setJumpAction();
& & & & this.node.runAction(this.jumpAction);
& & & & this.accLeft =
& & & & this.accRight =
& & & & this.xSpeed = 0;
& & & & this.setInputControl();
& & // called every frame, uncomment this function to activate update callback
& & update: function (dt) {
& & & & if(this.accLeft){
& & & & & & this.xSpeed -= this.accel *
& & & & } else if(this.accRight){
& & & & & & this.xSpeed += this.accel *
& & & & if(Math.abs(this.xSpeed) & this.maxMoveSpeed){
& & & & & & this.xSpeed = this.maxMoveSpeed * this.xSpeed / Math.abs(this.xSpeed);
& & & & this.node.x += this.xSpeed *
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:28566次
排名:千里之外
原创:40篇
(1)(1)(10)(8)(2)(1)(1)(2)(2)(1)(1)(6)(5)(1)Cocos2dx v3.6制作射箭遊戲(二)
Cocos2d-x v3.6制作射箭游戏(二)
六 24, 2015by&RENSHANin&COCOS2D-X
上章我们创建并加载了游戏地图,接下来的两章我们将实现如下的效果。
在开始之前,先给大家道个歉,因为前几周忙,没有时间写教程,所以迟迟都没更新,让有些童鞋久等了,见谅哦!!
本章我们的主要任务是创建射箭的弓箭手(也就是游戏猪脚),并且让这个猪脚随着触摸点的改变不断的旋转手中的弓箭。
对于这个射箭的角色而言,它能不停的射出弓箭。当我们按住屏幕上某点时,会从该角色拿弓箭的手的位置&画&一条标注箭支运动轨迹的红线(看似抛物线);当在屏幕上滑动手指或鼠标时,这条红线会随着触摸点的位置不停的变换轨迹;当松开屏幕上的手指或鼠标时,会射出一支弓箭,这支弓箭会按最终的红线路径移动。另外,玩家手中的弓箭会随着屏幕上的手指或鼠标旋转。
下面我们一起来创建这个 Player 猪脚类,其初步定义如下:
class Player: public Sprite
bool init(Vec2 playerPos);
static Player* create(Vec2 playerPos);
void createPlayer();
void createPlayerHpBar();
void rotateArrow(Point touchPoint);
void createAndShootArrow( Point touchPoint);
void shootArrow();
void finishRunAction();
void update(float dt);
CC_SYNTHESIZE(int, playerHp, PlayerHp);
// 玩家血量值
CC_SYNTHESIZE(bool, startDraw, StartDraw);
// 是否开始画红色的路径线
CC_SYNTHESIZE(bool, isRunAction, IsRunAction); // 玩家是否正在执行射箭动画
Vec2 playerPos;
// 角色在 tmx 地图上的位置
Size playerSize;
// 角色尺寸
Size winSize;
// 屏幕窗口尺寸
Sprite* playerbody;
// 角色身体
Sprite* playerarrow;
// 角色的弓箭,也就是会随触摸点旋转的弓和箭部分
Sprite* hPBgSprite;
// 角色血条背景精灵
ProgressTimer* hpBar;
// 角色血条
ccQuadBezierConfig bezier; // 路径贝赛尔
DrawNode* drawNode;
// 这里表示我们的线条对象
以上的各方法都是我们这两章需要实现的,其他更多的方法我们将在后面需要的时候再扩充。其中CC_SYNTHESIZE宏的作用是定义一个保护型的变量,并声明一个getfunName函数和setfunName函数,你可以用getfunName函数得到变量的值,用setfunName函数设置变量得值。如:CC_SYNTHESIZE(int, playerHp, PlayerHp);定义了一个整型的 playerHp 变量,同时还声明了 getPlayerHp() 和 setPlayerHp() 两个方法。ccQuadBezierConfig是我们新定义的一个结构体,后面我们会详细的讲解。
下面我们就从上到下依次来看看以上的各方法。
首先是 Player 的初始化(init)和创建(create),这里我们通过给定 Player 的位置来创建该角色,而这个传入的坐标位置应该是我们从 TiledMap 的对象层中读取到的位置(上章有讲)。具体代码如下:
Player * Player::create(Vec2 playerPos)
Player *pRet
= new Player();
if (pRet && pRet-&init(playerPos))
pRet-&autorelease();
return pRet;
delete pRet;
pRet = NULL;
return NULL;
bool Player::init(Vec2 playerPos)
if (!Sprite::init())
return false;
this-&playerPos = playerPos;
createPlayer();
// 创建角色
createPlayerHpBar();
// 创建角色血量条
scheduleUpdate();
return true;
下面我们接着来看看 createPlayer 方法,该方法将初始化我们的 Player 角色,代码如下所示:
void Player::createPlayer()
playerbody = Sprite::createWithSpriteFrameName("playerbody.png");
playerSize = Size(playerbody-&getContentSize().width/2, playerbody-&getContentSize().height / 3*2);
// 设置Player的尺寸,大小略小于playerbody的尺寸,这样利于我们后面更准确的进行碰撞设置。
playerbody-&setAnchorPoint(Vec2(0.7f, 0.4f));
this-&addChild(playerbody);
this-&setPosition(Vec2(playerPos.x+ GameManager::getInstance()-&getObjectPosOffX(), playerPos.y + playerSize.height * 0.4f));
playerarrow = Sprite::createWithSpriteFrameName("playerarrow.png");
playerarrow-&setPosition(Vec2(0, 0));
playerarrow-&setAnchorPoint(Vec2(0.3f, 0.5f));
this-&addChild(playerarrow);
createPlayer 方法中我们将创建如下所示的一个游戏角色。
因为没有找到合适的游戏资源(原游戏中得到的资源都是零件,要使用需要把它们一帧一帧重组),所以我们的游戏一切从简,不整那些复杂的。这里我们只把角色简单分成了两个部分,第一部分当然是玩家的身体playerbody,第二部分是随着触摸点/鼠标旋转的手和弓箭playerarrow。(PS:当然因为资源限制这个原因,可能会稍稍降低咱游戏的档次,应该不能怪我啰!O(&_&)O~)
设置playerbody位置时,你可能已经发现,我们并没有把角色身体设置在传入的playerPos处,而是对它稍微做了一定的调整。这是因为我们传入的位置它是紧贴本格瓦片底部的(我们制作tmx文件时,需要这样做。上章没说清楚,这章补起,要记住哦!)。如下图所示:
Y值坐标也不可太接近本格瓦片底部,也就是不要设为9.990,9.998这类太接近10的,因为 tmx 文件中存放的坐标值是整数,如果设为9.990,9.998,那么存放的值会是9.990 X 32 = 319.68 = 320,同理 9.998 X 32 也是 320。320 对于瓦片大小是32 X 32的地图来说是个特殊的数字,因为 320 /32 = 10。这样在程序中就会误以为9.990,9.998之类的点是坐标上的第10个点。
而且上章我们也说过,由于分辨率适配的原因,对象组中对象的位置与实际的位置是有一定的偏差的,所以我们在设置角色身体位置时,需要修正这些偏差。以上代码中设置位置的原理图如下:
其中,对象组在 X 轴上的偏移值我们把它保存在了 GameManager 中,而 GameManager 是个单例类,后面章节我们会详细的讲解。当然如果你现在就想运行代码,那就先把GameManager::getInstance()-&getObjectPosOffX()部分去掉吧。
创建好角色后,接下来我们需要创建角色的血量条,血量条可通过 Cocos2d-x 中封装好的进度条类 ProgressTimer 来创建。其代码段如下:
void Player::createPlayerHpBar()
// 创建血条底,即进度条的底背景
hPBgSprite = Sprite::createWithSpriteFrameName("hpbg.png");
hPBgSprite-&setPosition(Vec2(playerbody-&getContentSize().width / 2, playerbody-&getContentSize().height));
playerbody-&addChild(hPBgSprite);
// 创建血条
hpBar = ProgressTimer::create(Sprite::createWithSpriteFrameName("hp1.png"));
hpBar-&setType(ProgressTimer::Type::BAR); // 设置进度条样式(条形或环形)
hpBar-&setMidpoint(Vec2(0, 0.5f));
// 设置进度条的起始点,(0,y)表示最左边,(1,y)表示最右边,(x,1)表示最上面,(x,0)表示最下面。
hpBar-&setBarChangeRate(Vec2(1, 0));
// 设置进度条变化方向,(1,0)表示横方向,(0,1)表示纵方向。
hpBar-&setPercentage(100);
// 设置当前进度条的进度
hpBar-&setPosition(Vec2(hPBgSprite-&getContentSize().width / 2, hPBgSprite-&getContentSize().height / 2 ));
hPBgSprite-&addChild(hpBar);
hPBgSprite-&setVisible(false);
// 设置整个血条不可见,我们将在Player 遭受攻击的时候再显示血条。
旋转角色弓箭
接下来我们来让 Player 的弓箭部分跟随着触摸点/鼠标旋转。所以我们定义了如下的函数:
void Player::rotateArrow(Point touchPoint)
auto playerPos = this-&getPosition();
auto pos = playerPos + playerarrow-&getPosition();
Point vector = touchPoint - pos;
auto rotateRadians = vector.getAngle();
auto rotateDegrees = CC_RADIANS_TO_DEGREES( -1 * rotateRadians);
if (rotateDegrees &= -180 && rotateDegrees &= -90){
rotateDegrees = -90;
else if (rotateDegrees &= 90 && rotateDegrees &= 180){
rotateDegrees = 90;
auto speed = 0.5 / M_PI;
auto rotateDuration = fabs(rotateRadians * speed);
playerarrow-&runAction( RotateTo::create(rotateDuration, rotateDegrees));
rotateArrow方法的参数为触摸点的位置。
获取角色弓箭在游戏场景中位置;
计算弓箭的旋转角度。这里利用三角正切函数来计算,原理如下图所示:vector(offX,offY) 是触摸点到弓箭之间的向量,通过 getAngle 方法,我们可以得到 vector 向量与X轴之间的弧度。再者,我们需要把弧度 rotateRadians 转化为角度,CC_RADIANS_TO_DEGREES就是能把弧度转化为角度的宏。转化时乘 -1 是因为Cocos2d-x中规定顺时针方向为正,这与我们计算出的角度方向相反,所以转化的时候需要把角度a变为-a。
控制旋转角度的范围,即只让它在角色右半边内旋转。
计算弓箭旋转时间。speed表示炮塔旋转的速度,0.5 / M_PI其实就是 1 / 2PI,它表示1秒钟旋转1个圆。rotateDuration表示旋转特定的角度需要的时间,计算它用弧度乘以速度。
让弓箭执行旋转动作。
好了,现在 Player 就初步定义好了。接下来,我们回到游戏场景把Player加入进去,并来测试下弓箭是否跟随触摸点旋转。
在 Cocos2d-x 3.x 引擎中,实现触摸响应的流程基本是一致的。所以在 3.6 中,其过程依旧是:
重载触摸回调函数;
创建并綁定触摸事件;
实现触摸回调函数。
所以我们要测试弓箭是否跟随触摸点旋转,第一步请先在 GameScene 中重写如下的触摸回调函数,并声明变量:
virtual bool onTouchBegan(Touch *touch, Event *unused_event);
// 开始触摸屏幕时响应
virtual void onTouchMoved(Touch *touch, Event *unused_event);
// 触摸屏幕并在屏幕上滑动时响应
virtual void onTouchEnded(Touch *touch, Event *unused_event);
// 触摸结束时响应
Point preTouchPoint;
// 上一个触摸点
Point currTouchPoint;
// 当前触摸点
接着,我们需要在 GameScene 的 init 初始化函数中创建并綁定触摸事件,并先随便创建一个 Player 对象,用于测试。如下:
SpriteFrameCache::getInstance()-&addSpriteFramesWithFile("texture.plist", "texture.pvr.ccz");
player = Player::create(Vec2(winSize.width / 4, winSize.height/5));
this-&addChild(player);
// 获取事件分发器
auto dispatcher = Director::getInstance()-&getEventDispatcher();
// 创建单点触摸监听器
auto listener = EventListenerTouchOneByOne::create();
// 让监听器綁定事件处理函数
listener-&onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan,this);
listener-&onTouchMoved = CC_CALLBACK_2(GameScene::onTouchMoved,this);
listener-&onTouchEnded = CC_CALLBACK_2(GameScene::onTouchEnded,this);
// 将事件监听器添加到事件调度器
dispatcher-&addEventListenerWithSceneGraphPriority(listener,this);
Player 的位置是固定的,我们当然不能随便设,这里只是为了测试。后面的章节中我们会创建一个类来专门管理从 TiledMap 中得到的对象,包括Player、敌人、道具,砖块等。
以上 plist 和 pvr.ccz文件是我们的打包资源,它们是用 Texturepacker 编辑器打包而来。更多详细内容请点此查看。
綁定好触摸事件后,最后我们需要实现它们,代码如下:
bool GameScene::onTouchBegan(Touch *touch, Event *unused_event)
currTouchPoint = touch-&getLocation();
if( !currTouchPoint.equals(preTouchPoint)){
player-&rotateArrow(currTouchPoint);
preTouchPoint = currTouchPoint;
return true;
void GameScene::onTouchMoved(Touch *touch, Event *unused_event)
currTouchPoint = touch-&getLocation();
if( !currTouchPoint.equals(preTouchPoint)){
player-&rotateArrow(currTouchPoint);
preTouchPoint = currTouchPoint;
void GameScene::onTouchEnded(Touch *touch, Event *unused_event)
// 射箭,下章内容
在 onTouchBegan 和 onTouchMoved 函数中,处理方法是一样的。即当当前触摸点与之前的触摸点不一致时,就旋转 Player 的弓箭。getLocation 方法将 touch 对象中保存的屏幕坐标转换成我们需要的 Cocos2d 坐标。 分不清屏幕坐标和Cocos2d 坐标的童鞋请参考Cocos2d-x 3.0坐标系详解一文。
当触摸结束时,Player 对象需要射出弓箭,这个我们暂时不写。
运行游戏,此时你就可以看到想要的效果了。关于本章资源,请点此下载。
今天的教程就到这里,下章我们将讲些有意思的,敬请期待!
另外,最近发现很多越俎代庖的开发者,所以这里打个版权,装下B。===》原创教程,转载请注明出至:/archives/515
更多相关文章
好久都没写文章了,就来一篇吧.这种方法是在制作&胖鸟大冒险&时用到的.&胖鸟大冒险&中使用Box2D来进行物理模拟和碰撞检測,因此对每一个机关须要创建一个b2body.然后&胖鸟&是依据&超级马里奧兄弟&设计的,所以机关能够是各种运动轨迹的平台,绕 ...
cocos2d-x自带了不少示例,以及几个比较简单的游戏,不过这些游戏都是用javascr
很多人问我:沈老师,要不要更新引擎版本到3.0,更新这么快,以后会不会每个月都有一次,好怕呀! 我说:不管你以前是哪个版本,3.0final是一个架构级别的升级,可以在新项目中果断升级,还包括以下: c++11 的特
选关界面 GameSelectLayer.ccbx [html]view plaincop
Alice和Bob这一次准备玩一个关于硬币的游戏:N枚硬币排成一列,有的正面朝上,有的背面朝上,从左到右依次编号为1..N.现在两人轮流翻硬币,每次只能将一枚正面朝上的硬币翻过来,并且可以随自己的意愿,在一枚硬币翻转后决定要不要将该硬币左边的任意一枚硬币也翻一次(正面翻到背面或背面翻到正面).翻
这是一个使用Box2d和cocos2d-x从头开始制作一个弹弓类游戏系列教材的第二部分. (第一部分请看本博客博客列表) 在这个系列教材的第一部分,我们在场景中加入可以投射危险的橡子炸弹投射器. 在这个系列的第二也是最后部分.我们将会把游戏完善成为完整版,并且增加我们射击的目标和游戏逻辑. 言归正传
本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区
我在博客标题为“ios如何实现本地推送,兼容ios8“/laoguigame/p/4522474.html的博文中介绍了通过oc代码实现本地推送的方法.现在介绍在lua中调用咱们之前写的oc方法接口来实现在lua层实现推送的功能,主要基于quick coc
平台开放平台招商资质标准细则 本标准适用于除虚拟业务(包括但不限于旅游.酒店.票务.充值.彩票)外的平台开放平台所有卖家.
第一章 入驻 1.1
平台开放平台暂未授权任何机构进行代理招商服务,入驻申请流程及
这个问题是因为项目中的JRE库与当前eclipse中的JRE库不一致造成的,我项目中是1. ...
XML文件内容如下: &apps slick=&3&& &app& &id&1&/id& &name&Google Maps&/n ...
WIN下的tomcat5的日记 是按日期来记实 的.移到linux下创造tomcat/logs下只有一个catalina.out件. 工夫一长.创造日记 文件暴增.对付 管理 真是不利便 . 要是能像win下的tom
ylbtech-dbs:ylbtech-storebook-room
A, 返回顶部
[原]Runtime ErrorDescription: An application error occurred on the server.... -7阅读2010 评论3 Description: ...
目录(?)[+] ====================================
我们用sublime几乎都会首先安装这个插件,这个插件是管理插件的功能,先安装它,再安装其
1 package com.example. 2 3 import java.util.ArrayL 4 import java.util.L 5 6 import a
1. static library How to check target architecture of a static library /2012/您所在的位置: &
基于Cocos2D-X的砖块地图教程
基于Cocos2D-X的砖块地图教程
Jorge Jordán
通过文章你将学习如何添加砖块地图到游戏中,跟着玩家滚动地图,并使用对象图层。也会学到如何使用地图编辑器去创造砖块地图本身。同时还有如何在地图上创造碰撞领域,如何使用砖块属性,如何创造可收集的道具并动态地修改地图,以及如何确保你的忍者不会吃太多东西。
这是Cocos2D-X砖块地图教程系列,你将在此创造一款有关沙漠中的忍者寻找美味的西瓜的简单游戏。
需要注意的是该教程是关于Cocos2D-X,即Cocos2D-iPhone的跨平台C++移植。所以你在此编写的代码将适用于iPhone,Android和更多平台上!
在本系列文章的第一部分中,你将学习如何添加砖块地图到游戏中,跟着玩家滚动地图,并使用对象图层。你将学到如何使用地图编辑器去创造砖块地图本身。
而第二部分是关于如何在地图上创造碰撞领域,如何使用砖块属性,如何创造可收集的道具并动态地修改地图,以及如何确保你的忍者不会吃太多东西。
注:本篇教程类似于Cocos2D-iPhone教程。
让我们开始创造砖块地图吧!
对于这一教程,你需要安装最新的Cocos2D-X版本(游戏邦注:在写本篇教程的时候更新到2.1.4)。如果你还未拥有最新版本的Cocos2D-X,先下载它并在终端运行如下命令去安装模版:
cd&~/Downloads/cocos2d-x-2.1.4&./install-templates-xcode.sh&-f&-u&
然后使用iOS\cocos2d-x\cocos2dx模版在Xcode创造一个新项目。点击Next,将项目命名为TileGame,将项目设置为Universal,点击Next然后点击Create。
你将在这一项目中使用ARC,所以如果这是你第一次听到ARC,我会鼓励你先了解下它。模版并不是默认使用ARC,但幸运的是,我们能够轻松地进行 修改。前往Edit\Refactor\Convert to Objective-C ARC。往下拉并只选择文件main.m,
AppDelegate.cpp, HelloWorldScene.cpp,然后点击Check并完成向导的步骤。
select targets(from raywenderlich)
创建并运行,然后确保一切都还正常运行&-你应该能够看到标准的&你好世界&屏幕。
接下来下载游戏资源的压缩文件。压缩文件包含如下内容:
你将面向玩家对象使用的精灵。
一些伴随着cfxr效用所创造的音效(你将会在教程中用到)。
一些伴随着Garage Band所创造的背景音乐。
你将用到的一些砖块设置&-这将伴随着你将使用的地图编辑器,但我认为我们能够更轻松地将其与其它内容包含在一起。
一些额外的&特别&砖块,将在之后进行详细解释。
当你下载了资源后,打开它并将TileGameResources文件夹拖到项目的Resources群组中。在项目菜单里,右击 Resources群组,并选择Add Files to
&TileGame&&选择Resources/TileGameResources文件夹,核实选中了Copy items into
destination group&s folder (if needed)以及Create groups for any added
folders,然后点击完成。
如果一切顺利的画,所有的文件都将出现在你的项目中。
你的项目应该如下:
tile game(from raywenderlich)
现在我们将开始创造地图!
Cocos2D-X支持基于开放源Tiled Map Editor去创造地图并将其以TMX格式进行保存。
下载Tiled Map Editor。在编写本篇教程的时候,其最新版本是0.9.0。
然后运行Tiled,前往File\New,并如下填写对话内容:
new map(from raywenderlich)
在定向区域中,你可以在Orthogonal或Isometric间做出选择。在此你将选择Orthogonal。
接下来你将设置地图的大小。记住这是在砖块中,而不是像素中。你将创造一个较小的地图,所以在此你应该选择50&50。Tiled将基于像素呈现给 你总体地图的大小,即在New Map对话的最底部。这是在长度和宽度的基础上将地图大小(50个砖块)乘以砖块的大小(32像素)所计算出来的。
最后,你将明确宽度和高度。你在此所选择的是取决于美术人员所设置的砖块。对于本篇教程,你将使用一些伴随着Tiled编辑器的样本砖块,即32&32规格,选择它便点击OK。
接下来你必须添加砖块设置去绘制你的地图。在菜单栏上点击Map,然后关掉New Tileset&,并如下填写对话框内容:
new tileset(from raywenderlich)
为了获得图像,点击Browse并导航至你自己的TileGame/Resources/TileGameResources文件夹,然后选择你之前从资源压缩中下载的tmw_desert_spacing.png文件,并将其添加到项目中。它将自动根据文件名填写名字。
你可以将宽度和高度设置为32&32,因为这也是砖块的大小。对于边缘和间隔:
边缘是关于在Tiled开始寻找真正的砖块像素前应该为当前的砖块略过多少多少像素(包括宽度和高度)。
间隔是关于Tiled在明确了实际砖块像素并转向下一个砖块数据之后应该前进多少像素(包括宽度和高度)。
如果你着眼于tmw_desert_spacing.png,你将发现每个砖块都围绕着一个1像素的黑色边缘,这也解释了边缘和间隔为1的设置。
tile(from raywenderlich)
当你点击OK时,你将看到砖块呈现在Tilesets窗口中。现在你可以开始绘制了。点击工具栏的Stamp Brush图标,然后点击地图上的任何一个位置去放置一个砖块。
tileset(from raywenderlich)
所以继续绘制地图&-尽可能发挥创造性!确保添加至少一些建筑到地图上,因为你在之后将需要一些碰撞内容。
tileset(from raywenderlich)
为了更轻松地绘制内容,你可以着眼于一些快捷方法。以下是最常用到的一些方法:
你可以在Tileset选择器中围绕着一系列砖块拖曳一个盒子,并同时放下多个相邻的砖块。
你可以通过View\Zoom In和View\Zoom Out进行放大和缩小。
z键将在基于Stamp Brush工具编辑地图时进行旋转。
在一些新功能中你可能会注意到Mini-map。这是一个很棒的功能,它让你能够看到一个迷你地图!着眼于我在Mini-map最下方的迷宫中的糟糕尝试。红色盒子代表你在主要编辑窗口中看到的区域。
tileset(from raywenderlich)
当你在阅读下一个区域中的滚动时牢牢记住这一Mini-map视图。
需要注意的是这一教程的资源是出现在地图前的&&所以如果你很懒的话便可以直接利用它。如果你这么做,你应该在Tiled打开地图并明确它是如何设置的。
当你完成地图的绘制时,在Layers视图中双击Tile
Layer,将名字改为Background。然后点击File\Save,并将文件保存到TileGame项目中的TileGame \Resources\TileGameResources,将文件命名为TileMap.tmx,并覆盖现有的文件。
你将在之后使用Tiled做其它事,但是现在让我们将这一地图带进游戏中!
添加Tiled地图到Cocos2D-X场景中
打开HelloWorldScene.h,在#include &cocos2d.h&之后添加如下内容:
using&namespace&cocos2d;&
这能指导编辑器去使用cocos2d命名空间,所以你不需要为所有内容加上cocos2d的前缀。
然后添加以下内容到类定义中,即在花括号之后:
private:&CCTMXTiledMap&*_tileM&CCTMXLayer&*_&
这创造了一个实例变量去追踪砖块地图本身,并创造了另一个实例变量去追踪地图的背景层。你将在之后学到更多有关砖块地图层面的内容。
接下来,用如下内容换掉HelloWorldScene.cpp:
CCTMXObjectGroup&*objectGroup&=&_tileMap-&objectGroupNamed(&Objects&);&&if(objectGroup&==&NULL){&CCLog(&tile&map&has&no&objects&object&layer&);&return&false;&}&&CCDictionary&*spawnPoint&=&objectGroup-&objectNamed(&SpawnPoint&);&&int&x&=&((CCString)*spawnPoint-&valueForKey(&x&)).intValue();&int&y&=&((CCString)*spawnPoint-&valueForKey(&y&)).intValue();&&_player&=&new&CCSprite();&_player-&initWithFile(&Player.png&);&_player-&setPosition(ccp(x,y));&&this-&addChild(_player);&this-&setViewPointCenter(_player-&getPosition());&
最后一行有个预兆&&但不要担心,你很快就能到达那里。
让我们暂停一会并解释对象层面和对象群组。首先注意你是通过在CCTMXTiledMap对象中(而不是layerNamed)通过objectGroupNamed方法检索对象层面。它返回了一个特殊的CCTMXObjectGroup对象。
然后objectGroup调用了objectNamed方法去获得一个CCDictionary,并包含了一些有关对象的有用信息,如x和y轴,宽度和高度。在教程的这一部分,你需要关心的便是x和y轴,将其设置为玩家精灵的位置。
在代码块的最后你设置了视图去明确玩家的位置。所以现在添加如下内容到HelloWorldScene.h中:
&void&setViewPointCenter(CCPoint&position);&
并添加一个新方法到HelloWorldScene.cpp(在文件的最下方最好):
void&HelloWorld::setViewPointCenter(CCPoint&position)&{&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();&&int&x&=&MAX(position.x,&winSize.width/2);&int&y&=&MAX(position.y,&winSize.height/2);&x&=&MIN(x,&(_tileMap-&getMapSize().width&*&this-&_tileMap-&getTileSize().width)&&&winSize.width&/&2);&y&=&MIN(y,&(_tileMap-&getMapSize().height&*&_tileMap-&getTileSize().height)&&&winSize.height/2);&CCPoint&actualPosition&=&ccp(x,&y);&&CCPoint&centerOfView&=&ccp(winSize.width/2,&winSize.height/2);&CCPoint&viewPoint&=&ccpSub(centerOfView,&actualPosition);&this-&setPosition(viewPoint);&}&
这是关于砖块的解释。想象这一函数设置了摄像机的中心位置。它让用户能够进入地图中x,y轴的任何位置&-但是你有可能不想呈现出某些点,如你可能不想要屏幕超过地图的边缘(那么它便只会呈现出黑边!)。
diagram(from raywenderlich)
如果摄像机的中心小于winSize.width/2或winSize.height/2,那么部分视角是否会脱离屏幕?同样的,检查最上方的界限也很重要,这也是setViewPointCenter所做的。
到目前为止这一函数被当成设置了摄像机所面对的中心位置。然而,这并不是它真正做的。这是在Cocos2D-X中操控CCNode的摄像机的一种方法,但使用它会比你将使用的解决方法(移动整个层面)更复杂。
着眼于这一图解:
diagram(from
raywenderlich)想象一个大世界,你将着眼于坐标轴,即从0到winSize.height/width。你的视图的中心是 centerOfView,你便能清楚自己想要以哪里为中心(actualPosition)。所以为了用实际位置去匹配视图中心位置,你需要做的便是向 下倾斜地图!
通过从视图中心减去实际位置你便能够做到这点,然后将HelloWorld层面设为该位置。
说了这么多理论,是时候执行它们了!创建并运行项目,如果一切运行正常,你将在屏幕上看到忍者,并且视图会不断移动去呈现他的行动。
simulator(from raywenderlich)
让忍者移动
这是个好的开始,但是你的忍者还只是站在那里!这并不像真正的忍者。你将朝着用户敲打的方向移动忍者而让他动起来。添加如下代码到HelloWorldScene.h的公共部分:
void&registerWithTouchDispatcher();&void&setPlayerPosition(CCPoint&position);&bool&ccTouchBegan(CCTouch&*touch,&CCEvent&*event);&void&ccTouchEnded(CCTouch&*touch,&CCEvent&*event);&
然后打开HelloWorldScene.cpp并将如下代码添加到init:
this-&setTouchEnabled(true);&
这将层面设置为可碰触的,所以它将关注于碰触事件。接下来添加如下方法到文件最底端:
#pragma&mark&&&handle&touches&&void&HelloWorld::registerWithTouchDispatcher()&{&CCDirector::sharedDirector()-&getTouchDispatcher()-&addTargetedDelegate(this,&0,&true);&}&&bool&HelloWorld::ccTouchBegan(CCTouch&*touch,&CCEvent&*event)&{&return&true;&}&&void&HelloWorld::setPlayerPosition(CCPoint&position)&{&_player-&setPosition(position);&}&&void&HelloWorld::ccTouchEnded(CCTouch&*touch,&CCEvent&*event)&{&CCPoint&touchLocation&=&touch-&getLocationInView();&touchLocation&=&CCDirector::sharedDirector()-&convertToGL(touchLocation);&touchLocation&=&this-&convertToNodeSpace(touchLocation);&&CCPoint&playerPos&=&_player-&getPosition();&CCPoint&diff&=&ccpSub(touchLocation,&playerPos);&&if&(&abs(diff.x)&&&abs(diff.y)&)&{&if&(diff.x&&&0)&{&playerPos.x&+=&_tileMap-&getTileSize().&}&else&{&playerPos.x&-=&_tileMap-&getTileSize().&}&}&else&{&if&(diff.y&&&0)&{&playerPos.y&+=&_tileMap-&getTileSize().&}&else&{&playerPos.y&-=&_tileMap-&getTileSize().&}&}&&&if&(playerPos.x&&=&(_tileMap-&getMapSize().width&*&_tileMap-&getTileSize().width)&&&&playerPos.y&&=&(_tileMap-&getMapSize().height&*&_tileMap-&getTileSize().height)&&&&playerPos.y&&=&0&&&&playerPos.x&&=&0&)&{&this-&setPlayerPosition(playerPos);&}&&this-&setViewPointCenter(_player-&getPosition());&}&
在此你覆盖了registerWithTouchDispatcher方法去处理目标碰触事件。这将导致ccTouchBegan/ccTouchEnded方法(单数情况)被调用,而不是ccTouchesBegan/ccTouchesEnded方法(复数情况)。
你可能会好奇单数情况和复数情况有什么区别。不过在这种情况下我们没有必要去弄清楚这些问题。但是我还是想向所有人介绍这一方法,因为它带有2个主要优势:
&你不需要处理NSSets,调度程序能够区分它们。每次调用你将获得一个UITouch。&
&你可以通过在ccTouchBegan返回YES而要求一个UITouch。要求碰触的更新只会被发送到要求它们的委托中。所以如果你删除/结束/取消更新,你就需要确保它是你的碰触。这将让你无需在执行多点碰触时做各种检查。&
不管怎样,在你的ccTouchEnded位置上,你像往常那样将位置转换成视图坐标轴,然后再转换成GL坐标轴。而新任务便是你调用了this-&convertToNodeSpace(touchLocation)。
这是因为碰触位置将提供给你用户在视口中轻敲的坐标轴(例如100,100)。但是你可能已经滚动了地图,所以它将匹配(800,800)的位置。所以调用这一方法将基于你如何移动层面而抵消碰触。
接下来你将明确碰触点和玩家位置的区别。你将基于碰触选择一个方向,所以首先你应该决定是上下移动还是左右移动。然后你将判断是正数还是复数而进行上下移动。
你将相对地调整玩家位置,然后将视图中心设置为玩家的位置,这是你在上部分便写下的内容!
注意你必须添加一个安全检查以确保不会将玩家带离地图外部!
所以创建并运行项目,然后尝试它!现在你应该能够轻敲屏幕去移动忍者了!
simulator(from raywenderlich)
这时候你已经知道如何创造地图并将其整合到游戏中了。而在第二部分教程中你将进一步学习如何添加碰撞检测到地图中以避免忍者能够轻松地穿越墙壁。【编辑推荐】【责任编辑: TEL:(010)】
关于&&的更多文章
Cocos2d是一个开源框架,用于构建2D游戏、演示程序和其他图形界
当下,移动App的开发相当火热,Android的和iOS的开发
关于App营销,了解哪类App最能引起用户关注,以及不同
在iOS开发过程中,尤其是对于新手来说,都会遇到或多
数据库技术是计算机科学中一个重要的组成部分,它正在以日新月异的速度发展。数据库的基本原理和应用技术已经成为高等院校的学生
Windows Phone专家
Android开发专家
51CTO旗下网站

我要回帖

更多关于 dmm game player更新 的文章

 

随机推荐