当前位置: 首页 > 编程日记 > 正文

怎样用cocos2d-x做一个基于地图块的游戏(Part One)

怎样用cocos2d-X做一个基于地图块的游戏 (Part One)

在这个分为上下两部分的教程中,我们将介绍如何使用Cocos2D-X和地图编辑器做一款基于地图块的游戏。在这个简单的地图块游戏里,一个精灵将在沙漠里搜寻它可口的西瓜!

在教程的第一部分,我们将介绍如何创建一个地图,如何将地图添加到游戏中,如何滚动地图以跟着玩家跑,以及如何使用对象层。

在本教程的第二部分,我们将介绍如何在地图上制作碰撞区,如何使用地图块的属性,如何制作宝藏并动态的修改地图,以及如何确保你的忍者不暴饮暴食。

如果你还没有学习“如何用cocos2d-x来开发简单的Uphone游戏:(一) 下载安装和HelloWorld ”,你不妨先学习下这个教程,因为这个教程里有很多基础知识我们下面要用到。

好吧,让我们开始探险之旅吧!

创建一个工程框架

我们将首先创建一个框架工程,以确保我们拥有下面工程中所需的所有文件。

先下载并运行helloworld(具体参见教程“如何用cocos2d-x来开发简单的Uphone游戏:(一) 下载安装和HelloWorld ”)。

接下来,下载游戏所需要的zip资源文件(TileGameResource.zip)。该zip文件包含以下内容

• 一个我们将作为游戏主角的精灵。这个和教程“如何用cocos2d-x来开发简单的Uphone游戏:(二) 移动的精灵很像!
• 声音特效,用cxfr工具制作(by Wenderlich)。
• 一些背景音乐,用Garage Band制作(by Wenderlich,更多信息见post)。
• 地图块 - 这实际上是要用地图编辑器做的,但现在就把它和其他东西一起包含进来会更容易一些。
• 一些其他“特殊”的地图块,我们将在下面介绍。

好了,这些资源将在后面适当的时候添加到我们的游戏中,现在就是我们制作地图和享受乐趣的时刻了!

Cocos2DX支持由开源项目地图块图编辑器(Tiled Map Editor,国外网站,需要FQ)制作并保存为TMX格式的地图。

如果你访问上面的链接,你会看到有两个版本的编辑器 – 一种是用Qt应用程序框架编写的,另一种是用Java编写的。有两个版本的原因是因为这个编辑器最先是用Java编写的,后来他们又将它移植到Qt上来。

你要用哪个版本主要是取决于你。在本教程中,我们将介绍使用Qt版本,因为从现在开始这是编辑器的发展主线,但有些人又喜欢用Java版本,因为并不是所有的旧功能都已经被移植过来了。

不管怎样 - 如果你想跟着学习,那就下载Qt版本,然后安装运行。转到文件\新建,在对话框中填写如下:

在新建地图对话框的Orientation选项,你可以选择Orthogonal(正交视图,采用的游戏Legend of Zelda)或者Isometric(等轴视图,采用的游戏Disgaea),这里,我们选择Orthogonal。

下面你得到设定地图大小。记住这是以地图块为单位,不是像素点。我们要做一张比较小的地图,因此就选择50×50。

最后设定地图块的宽度和高度。在这里你选择多大的尺寸是依赖于你的美工制作的地图块的大小的。在本教程中,我们用的是编辑器中附带的示例地图块集,每个地图块都是32×32大小的,因此选择32×32。
  接下来,我们就在编辑器中加入地图块集。点击菜单栏的“Map”选项,选择“New Tileset…”,然后填写对话框:

图片可以点击Browse按钮得到,点击后导航到你的地图编辑器目录下的examples文件夹,将tmw_desert_spacing.png加到工程中.

宽度和高度32×32不用改,那个就是地图块的宽高。对于margin and spacing(边际和间距),还没有确切解释它们意思的文献,但它们应该这么理解:

• 边缘,指的是一个地图块的外面一圈,在这个边缘里面是实际的地图块内容。 
• 间隔,即两个地图块之间的间距(像素单位)。

如果你看一看tmw_desert_spacing.png,你会发现每个地图块有一个1像素的黑边界,这就解释了边际和间距为什么设置作为1。

一旦你点击OK,你就将在Tilesets窗口看见地图块集。Ok,现在你也能开始画地图了! 点击在工具栏的“Stamp”图标,点击一个地图块,然后在地图上任意一个你想要的地方点击填充地图块。

继续完成你的地图吧——但一定要发挥你的想象力噢! 但请在地图上至少画一两个建筑物,因为待会我们会让我们的精灵走进你造的建筑。

这里有几个制图的小窍门,可以记住:

• 你可以在Tileset中拖动鼠标,同时选择几个地图块,然后填充到地图上。 
• 你可以使用工具栏上的油漆桶按钮来填充一整片具有同样背景的地图,当然,用来填充的地图块是你在Tileset选择的。 
• 你可以通过“查看\放大...”和“查看\缩小...”放大和缩小地图。

一旦你绘制完地图,在图层窗口中单击当前图层(现在这个图层就是“tile layer 1”),将名称改为“background”。然后点击“File\Save”,将文件保存到你的TileMap项目资源文件夹,并命名该文件“tilemap.tmx”。

我们后面还将做更多的东西,但现在让我们把这张地图加载到游戏中!

把地图加载到Cocos2DX场景中

首先,因为我们是利用uphone的sdk开发游戏,所以我们先将资源也就是tilemap.tmx和tmw_desert_spacing.png拷贝到sdk所在目录的子目录“/UserData/”中。

然后,将HelloWorld.cpp的initCocos2d()函数用下面的代码替换:

boolHelloWorldAppDelegate::initCocos2d()
{
//init director
CCDirector::getSharedDirector()->setOpenGLView(m_pMainWnd);

// set to landscape mode

CCDirector::getSharedDirector()->setDeviceOrientation(kCCDeviceOrientationLandscapeLeft);



//load the tile map
constchar*strMapPath ="tilemap.tmx";
CCTMXTiledMap *pDesertTileMap =CCTMXTiledMap::tiledMapWithTMXFile(strMapPath);
pDesertTileMap->setPosition(CGPoint(0,0));
//create layer instance
CCLayer *pLayer =MyLayer::node();
pLayer->addChild(pDesertTileMap, 0, 1);

//add layer to scene
CCScene *pScene =CCScene::node();
pScene->addChild(pLayer);
// add scene to director
CCDirector::getSharedDirector()->runWithScene(pScene);


returntrue;

}

复制代码

在这里,我们用到了CCTMXTiledMap类,通过它加载了我们制作的地图。

现在大概的介绍下CCTMXTiledMap。这是一个CCNode,所以你可以设置它的位置,尺寸等,该节点的子节点是地图上的层,而且也有一个接口可以通过名字来获取这些层,比如这里的“background”层。每一个层都是CCSpriteSheet的子类,这也意味着每一层你只能有一个tileset。

所以我们在这里做的是保存tilemap和background的关系,并且把tilemap加到HelloWorld的layer上去。

这样就很ok了!编译和运行代码,你应该看到的是你地图的左下角:

还不赖! 但我们做的是游戏,所以我们还需要做三件事: a)一个精灵, b)精灵的出生点 和c)移动我们的视图,以便我们可以看到精灵。

这是我们会感到棘手的地方,下面我们就来解决掉它!

对象层和地图位置的设置

地图模块支持两种层,一种是地图层(就是我们上面一直在接触的),另一种是对象层。

对象层允许你在地图指定一个位置,在这个位置可以响应一些事件。比如,你希望有一个地方让精灵出生或者挂掉。在我们的教程中,我们要创建一个精灵的出生点。

那么,选择菜单栏中的“Layer\Add Object Layer…”,将该层命名为“objects”,然后单击确定。现在如果你在地图上画,你会发现它不是直接画地图块,而是画一个形状怪异的灰色框框,你可以放缩和移动这个灰色框框。

我们只是想选一个位置来放置我们的精灵,所以在地图上随便选一个位置单击。框框的大小并不重要,因为我们只需要用到x,y坐标。

然后右键单击灰色框框和点击“Properties”,命名为“spawnpoint”,点击OK :

假如你在这里设置了对象的类(Cocos2DX的类,比如CCSprite),那么代码会自动创建一个对应类型的对象。(不过GeekAndDad.com的泰勒指出,这是Cocos2DX以前的版本才具有的功能,现在已经被删除了。)

不管怎样,我们将type置空,那么它将为我们创建一个NSMutableDictionary,我们可以通过它访问该对象的各个属性,包括X,Y坐标。

保存地图并返回到代码,在HelloWorld.cpp的initCocos2d()接口中,在代码:

//load the tile map
constchar*strMapPath ="tilemap.tmx";
CCTMXTiledMap *pDesertTileMap =CCTMXTiledMap::tiledMapWithTMXFile(strMapPath);
pDesertTileMap->setPosition(CGPoint(0,0));
复制代码

的下面,加上以下代码:

//load the object layer and get the coordinate of the spawnpoint
CCTMXObjectGroup *objGroup =pDesertTileMap->objectGroupNamed("objects");
CCXStringToStringDictionary *spawnPoint =objGroup->objectNamed("spawnpoint");
floatfSpawnPointX =spawnPoint->objectForKey("x")->toFloat();
floatfSpawnPointY =spawnPoint->objectForKey("y")->toFloat();
//load player image texture
constchar*strImgPath ="Player.png";
    CCTexture2D *pPlayerTexture =CCTextureCache::sharedTextureCache()->addImage(strImgPath);
//create sprite instance
CCSprite *pPlayer =CCSprite::spriteWithTexture(pPlayerTexture);
    pPlayer->setPosition(CGPoint(fSpawnPointX, fSpawnPointY));


//add the player to the tile map
pDesertTileMap->addChild(pPlayer);
复制代码

Ok,我们再解释一下对象层和对象组。在这里,我们通过CCTMXTiledMap对象的objectGroupNamed接口得到了对象层,它返回的是一个CCTMXObjectGroup对象。

然后,我们通过CCTMXObjectGroup的objectNamed方法来得到一个NSMutableDictionary,它包含了对象层的对象的一堆有用的信息,包括X和Y坐标,宽度和高度。不过这里我们关心的是X,Y坐标,所以我们就只取得X,Y坐标,并把它作为精灵的位置。

编译运行代码我们就可以看到精灵了。

下面我们再让它动一动。在代码

//add scene to director
CCDirector::getSharedDirector()->runWithScene(pScene);
复制代码

的下面,添加代码:

//move the player
CCIntervalAction *move =CCMoveBy::actionWithDuration(5, ccp(140, 120));
CCIntervalAction *back =move->reverse();
CCFiniteTimeAction *seq =CCSequence::actions(move, back,NULL);
pPlayer->runAction( CCRepeatForever::actionWithAction((CCIntervalAction*) seq) );
复制代码

这里的几个action类是对动作的封装。上面代码的含义是表示精灵将向右上方移动,相对位置是(140, 120),并在五秒钟内完成移动,然后反方向移回到原来位置。

Ok,先运行你的代码,然后欣赏你的精灵开心的跑来跑去吧

转载于:https://www.cnblogs.com/afly/archive/2012/02/13/2349733.html

相关文章:

【Code Complete】《Code Complete 》

良好编程实践的百科全书,完善编码聚焦于个人技能——所有的内容都来说明我们称之为“编写巧妙的代码”(write clean code,clean可以翻译多种意思,只能意会了,有些英语翻译成汉语会很痛苦的)。这本书就是那种…

redux示例

? 献上大概只有自己看得懂的学习笔记 结合使用实例理解更容易哦~import React from react; import ReactDom from react-dom; import {createStore} from redux; //解构一个createStore 创建状态对象//默认状态 state const defaultState{arr:[qq,bmw7], };//创建r…

18常用web开发 浮动层、提示层代码下载

1、jQuery顶部固定层页面滚动淡出淡进菜单显示 查看演示>> 2、jQuery鼠标滚动条到页面底部浮动层滑动弹出信息 查看演示>> 3、网页向导Jquery插件wlGuide功能操作步骤引导 查看演示>> 4、jQuery文字段落鼠标悬停图片突出显示和预览 查看演示>> 5、jque…

Android Handler的使用(二)

Handler的使用(二) 一、 Handler与线程的关系 Handler在默认情况下,实际上它和调用它的Activity是处于同一个线程的。 例如在Handler的使用(一)的示例1中,虽然声明了线程对象,但是在实际调用当中它并没有调用线…

【windows】cmd中的help无法使用的解决方法

今天使用黑框时遇到了一个问题,我在检查tomcat端口是否被占用时发现 黑框中竟然无法识别 netstat,随后试了一下help也无法识别。查了论坛后大名大佬给出的解决方案。 一、首先确定C:\WINDOWS\system32目录下有help.exe这个文件。 二、依次打开右键单击…

对微软Web Deploy的一次艰难调试

2011年初开始做一个项目,开始体验使用微软网站发布工具来发布网站。在服务器端安装发布服务后,可以在Visual Studio界面中右键点击Web项目,再点发布,第一次填好发布设置,以后就可以实现一键发布,虽然还有不…

vue总结 08状态管理vuex

状态管理 类 Flux 状态管理的官方实现 由于状态零散地分布在许多组件和组件之间的交互中,大型应用复杂度也经常逐渐增长。为了解决这个问题,Vue 提供 vuex:我们有受到 Elm 启发的状态管理库。vuex 甚至集成到 vue-devtools,无需配…

java8 lambda 表达式详解

lambada 表达式实质上是一个匿名方法,但该方法并非独立执行,而是用于实现由函数式接口定义的唯一抽象方法使用 lambda 表达式时,会创建实现了函数式接口的一个匿名类实例可以将 lambda 表达式视为一个对象,可以将其作为参数传递1.…

【基础知识】win10常用快捷键

1、winctrlD 创建虚拟桌面 2、winctrl左右箭头 切换桌面 winctrlF4 关闭当前虚拟桌面 (按wintab 可以通过鼠标操作虚拟桌面的创建、切换与删除) 3、winM 全部窗口最小化 (winD 可在最小化全部窗口与恢复全部窗口状态 间自由切换 4、win数字 打开任务栏中从左到右的应用&#xf…

ASP.NET2.0-防止同一用户同时登陆

要防止同一用户同时登陆,首页应该记录在线用户的信息(这里与用户名为例),然后判断正在登陆的用户里面是否已存在。在这里使用一个cache存放已经登陆的用户名,但是还有一个问题就是要知道用户是什么时候离开系统的呢?这就要定期清除cache中的内容了&#…

如何选择真正的万兆防火墙?

http://www.cnw.com.cn/security-10g-firewall/htm2011/20110526_225444.shtml 随着“三网合一”、 “P2P视频”、“高清宽带”、“云时代”等逐渐成为人们关注的焦点,网络带宽的需求产生了几何级别的增长。目前,“千兆到桌面、万兆做骨干”对于交换机和…

Salesforce Lightning开发学习(二)Component组件开发实践

lightning的组件区分标准组件、自定义组件和AppExchange组件。标准组件由SF提供,自定义组件由developer自行开发,AppExchange组件由合作伙伴建立。下面我们写一个简单的列表demo来学习下自定义开发组件Component。 第一步:新建一个对象&#…

转 java c++互传arraylist

Java JNI由浅入深(包括:Java和C互传ArrayList泛型对象参数) 2010-11-25 09:57 1694人阅读 评论(1) 收藏 举报我们知道Java是一个运行在虚拟机里面的高级的编程语言,如果要调用系统的动态链接库的话,就要先声明native修饰的方法(类似接口里面的…

GoAccess安装及分析nginx实时日志

GoAccess是一个基于终端的快速日志分析器。其核心思想是实时快速分析和查看Web服务器统计信息,而无需使用您的浏览器(如果您希望通过SSH快速分析访问日志,或者只是喜欢在终端中工作),终端输出是默认输出,但…

在写游戏时钟类时,应确保时钟的计算是以某个固定的CPU为标准的

在写游戏时钟类时,应确保时钟的计算是以某个固定的CPU为标准的。下面通过一个例子来阐述如何实现这一思想: // 记录特定进程的affinity maskunsigned long lProcessAffinityMask;// 记录系统的affinity maskunsigned long lSystemAffinityMask;::GetProc…

学习Spring Boot

Spring boot 是什么 ? 简单说, spring boot 是一个构建项目的工具, 一个脚手架. Spring boot 能干什么? spring boot 做非常少的配置就可以构建生产级别的单体应用. Spring boot 怎么干的? 下面让我们来用spring boot 做一个hello world. 环境准备, 需要保证你的机器上已经有…

Linux添加用户组和添加用户

1.用户组 添加组:groupadd 组名 [rootServer-n93yom ~]# groupadd dev [rootServer-n93yom ~]# cat /etc/group | grep dev dev:x:10011: [rootServer-n93yom ~]# 删除组:groupdel 组名 [rootServer-n93yom ~]# groupdel dev [rootServer-n93yom ~]# cat…

用javascript实现仿163的js广告向下挤压页面的效果

本次实现一个js小小特效,效果就是广告从页面的上方出来将基本页面挤压下去。 实现的思路是将两个div(广告div1和div2)。将div1的高度不断增加,增加的同时div2的y轴坐标变大,div1的高度增加多少相对应的y轴坐标加大多少…

【eclipse】快速调整eclipse背景和格式的方法

第一步 第二步 第三步 :选择相应的格式 效果如图

对做C#自定义控件的一点心得

近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装这个COM组件,中间遇到很多曲折,研究了一个星期,终于完成了 下面总结一下我做DSOFramer这个自定义控件的注意地方: 1、在创建一个Windows窗体控件库的时候&am…

numpy知识点

1、nonzero 对于一维数据来说,将返回符合条件的 下标 >>> b1 np.array([True, False, True, False]) >>> np.nonzero(b1)(array([0, 2]),)对于二维数据来说,将返回两维 元组, 第一维是符合条件的 x的索引,第二…

Excel导入异常Cannot get a text value from a numeric cell解决

POI操作Excel时偶尔会出现Cannot get a text value from a numeric cell的异常错误。 异常原因:Excel数据Cell有不同的类型,当我们试图从一个数字类型的Cell读取出一个字符串并写入数据库时,就会出现Cannot get a text value from a numeric …

【蓝桥java】递归基础之反向输出字符串

题目:输入一个字符串,要求将该字符串反向输出 代码实现: package xn.zzunit.recurrence;/*** 递归方法反向输出字符串* author tyrantForever**/ public class Project1 {public static void main(String[] args) {String testString "…

SQL Server中灾难时备份结尾日志(Tail of log)的两种方法

简介 在数据库数据文件因各种原因发生损坏时,如果日志文件没有损坏。可以通过备份结尾日志(Tail of log)使得数据库可以恢复到灾难发生时的状态。 例如: 上图中。在DB_1中做了完整备份,在Log_1,Log_2处做了日志备份。在Log_2备份之后不久&…

C examples

最近在看David R. Hanson 的《C Interfaces and Implementations》&#xff0c;文中第一章提到了Literate Programming 作者举了一个例子&#xff1a; 功能&#xff1a;用于检测输入中相邻且相同的单词 #include<stdio.h> #include<math.h> #include<errno.h&g…

特征工程:特征生成,特征选择(三)

转自&#xff1a;https://blog.csdn.net/cymy001/article/details/79169862 特征生成 特征工程中引入的新特征&#xff0c;需要验证它确实能提高预测得准确度&#xff0c;而不是加入一个无用的特征增加算法运算的复杂度。 1. 时间戳处理 时间戳属性通常需要分离成多个维度比如年…

JAVA设计模式之不变模式

在阎宏博士的《JAVA与模式》一书中开头是这样描述不变&#xff08;Immutable&#xff09;模式的&#xff1a; 一个对象的状态在对象被创建之后就不再变化&#xff0c;这就是所谓的不变模式。 不变模式的结构 不变模式可增强对象的强壮型(robustness)。不变模式允许多个对象共享…

【蓝桥java】递归基础之智力训练

题目&#xff1a; 匪警请拨110,即使手机欠费也可拨通&#xff01; 为了保障社会秩序&#xff0c;保护人民群众生命财产安全&#xff0c;警察叔叔需要与罪犯斗智斗勇&#xff0c;因而需要经常性地进行体力训练和智力训练&#xff01; 某批警察叔叔正在进行智力训练&#xff1a; …

EF-Entity Framework 相关技术点收集贴

不定期、偶尔、添加更新 在网络上看到或者自己开发过程中碰到的EF&#xff0d;Entity Framework相关技术点 本文地址:http://www.cnblogs.com/vnii/archive/2012/02/28/2371736.html 1.数据表字段有默认值&#xff0c;比如DateTime类型的字段CreateTime默认值为数据新增的时间g…