如何策略一个游戏_百度知道求高手 一个策略游戏_百度知道求一个策略游戏_百度知道热门游戏:
当前位置: >
> 六大基本功教你如何做一个牛逼的媒介
六大基本功教你如何做一个牛逼的媒介
来源:游戏陀螺&&&&日期: 16:34:54
摘要:那手游媒介的工作职责是什么呢?对此,我自己也尝试着总结了手游媒介的六大基本功,只有这六项基本功你 都一一攻克了,我想,此时的你便是一个绝对够格的媒介!
首先,我想问下大家,什么是手游媒介呢?经过我多方考察学术资料,含义大概是这样。手游圈的媒介:从字面上的意思看,是充分了解游戏媒体的运作规律
及编辑需求,并在传播策划的基础上,将自己公司信息或者游戏产品信息准确的传递给媒体,最终将事先策划好的信息变成媒体的新闻、攻略、评测、礼包、发号、
专区、专访、论坛活动等传播形式,我们称这种工作的人为手游媒介。
那手游媒介的工作职责是什么呢?纵观各大手游企业的招聘信息,我们可以总结出这样的规律:1.根据公司游戏产品制定媒介执行计划;2.发布公司游戏
产品新闻、预约媒体重点位置;3.监测媒体效果,括新闻回链收集、软性配合、专区活动等;4.了解手游媒体,并拥有丰富的媒体资源及良好的关系;5.负责
公司品牌宣传,有危机处理能力等。了解了“手游媒介职责”,这也清晰了本文的思路,对此,我自己也尝试着总结了手游媒介的六大基本功,只有这六项基本功你
都一一攻克了,我想,此时的你便是一个绝对够格的媒介!
手游媒介基本功一:挑战自己
媒介,本身就是沟通多的工作种类,不像文案、不像研发,顾好自己便可全盘交差!对于新媒介来说,挑战自己是需首要攻克的难题,万丈高楼起于平地,靠
得是坚实的地基,手游媒介也是如此,挑战自己就如同地基一样,地基打不好,日子一长终究会被无形的压力压垮,就像本文开头介绍的妹子。在挑战自己的过程
中,多数人还没尝到甜头就放弃了,所以此章节又分为两小项“提高心里素质”及“掌握行业知识”分别介绍,手游媒介如何挑战自己!
提高心里素质
很多新入行的媒介或者做了几年的媒介,有时联系媒体都会有一种莫名其妙的恐惧感,生怕打搅人家的工作生活、生怕被媒体拒绝。如果你不主动,你便会错
过一个又一个的机会,也会觉着媒体离你越来越远!如果你想成为一名优秀的手游媒介,这时候,请你放下身架,放下你的小情绪与敏感,勇敢的走出去,与媒体进
行广泛的沟通。挑战自己,就是挑战自己的心里,这是你媒介工作的一个初步的台阶瓶颈,只有不断在行业里日积月累,你才会发现当初的恐惧与懦弱是多么的可
掌握行业知识
知识好比自己的底气,知识越多底气越足!而手游媒介对基础知识的了解尤其重要,这便是媒介妹子与媒体沟通的话题、沟通的资本。媒介妹子可以多到行业
媒体学习干货。当然精力充沛的话,可以自己整理一份行业名词,无论是渠道的、投放的、媒体的、市场的、产品的,都可以整理出来,保存在手机里,这样你就可
以随时随地的充电学习!挑战自己,从基础学起,点点滴滴,日积月累,相信不用三月你便是媒介小达人!
手游媒介基本功二:了解媒体
对手游媒体的了解程度,直接影响媒介妹子的工作质量,那到底是不是呢?从媒体诞生之日起,媒体就深深了落下了自己的属性,过去对于媒体的分类大致为
电视媒体、广播媒体、平面媒体、网络媒体,但随着互联网时代的崛起,网络媒体的地位愈发凸显,所以针对手游媒介来说,往往日常工作基本都是与手游行业的网
络媒体打交道。对媒体了解越深,你就越有把握利用每个媒体的优势,充分宣传自家的手游产品,这就是了解媒体的重要性!
了解媒体属性
媒体的属性,其实就是媒体的定位。手游圈媒体一共就那么多家,但凡哪家能发展壮大,那么必然有它的特色所在,了解媒体特色就是了解媒体属性!从大类
来说,手游媒体分为TOC媒体及TOB媒体,TOC媒体受众是广泛的手游玩家,TOB媒体受众是手游圈的从业者,所以在推广游戏产品时,你需要与TOC的
媒体合作;在包装公司发行实力、技术实力时,你需要与TOB媒体合作。对于TOC媒体,你需要去了解每家的特色、例如每家媒体的UV、PV是多少?每家媒
体投放广告的效果?每家媒体礼包发放效果?每家论坛活动的效果?每家微信发布的效果等,这只能通过你的媒介实施计划一步步去验证,相信了解了媒体的属性,
你的媒介工作就已经到了一个层次!
了解媒体功能
如果说,手游媒体的属性是自身的特色,那媒体的功能就是它核心的卖点!新闻、评测、攻略、入库、发号是现阶段TOC媒体基本功能,以此基础上,又延
展出视频解说、视频攻略、视频介绍、游戏专题、论坛专区、游戏推荐、美图欣赏等,所以了解TOC媒体功能,你就能知道与媒体合作点及推广方式!那对于
TOB媒体来说,除了常规的游戏圈新闻、资讯、专访外,很多媒体又开展了各有特色功能,例如开放日活动、沙龙活动、产品demo秀、产品投资引荐、行业招
聘等,这些细分也让你在包装公司品牌的时有的放矢!
手游媒介基本功三:建设媒体
手游媒介前两基本功都是为后续的基本功做铺垫的,所以媒介妹子们,请先戒骄戒躁,按照自己的职业规划一步步的去实施!建设媒体,作为媒介的专属职能,如何有效的开拓媒体资源,如何维护媒体关系是本节的重点!
开拓媒体资源
新入行的媒介妹子,总是有时候哭着喊着向你问道:XX家的主编有联系方式有木有,还附带个委屈的表情!这其实就是开拓媒体资源的一种方式,那如何高
效的开拓呢?根据我的经验可分为:1.根据媒体网站寻找联系方式,无论是哪家媒体,必定会在自己的网站上留有***或者QQ,只要你细心绝对可以找到关键人
物!2.行业人打听,手游圈总有比你资源多的前辈,多虚心打听,这必定是极好的方式!3.行业媒体名录,手游圈的小伙伴,通常会分享给你她制作的媒体名
录,这时候你不要窃喜,因为名录毕竟只是个名录,距离成为你的资源还很远!
维护媒体关系
开拓媒体只是为了后续合作,那合作的顺畅与否、免费与否还得看媒介妹子与媒体哥哥们的关系,当然最重要的是,看你是否了解每家媒体的利益点!维护媒
体第一层是保姆式,意思是甘愿做媒体的厂商小助理,媒体需要啥你就提供啥,目的只有一个为了获得媒体的信任与好感!第二层是销售式,到达第二层面的媒介,
一般都是很了解媒体的规律与特色,媒体有困难媒介会用各种方式去帮助,利用空档期去请客吃饭喝酒,节日会送出代表公司的小礼物等!第三层为朋友式,媒介与
媒体到了朋友地步,基本上是无话不谈,从价值观、爱好、乡情方面找到共鸣,以保持与媒体私下密切的沟通,这时候媒体与媒介双方互助,或形影不离!第四层为
策略式,到了这个层面的媒介,一般都是总监以上的级别了,媒介们熟悉手游市场推广的流程,可与媒体站在公司与公司的层面上进行合作交流,对于媒体来说,企
业是新闻来源,对于企业来说,媒体是新闻传播途径。
手游媒介基本功四:媒介计划
我之前碰到过做媒介很多年的人,一直很努力的在做执行,竟然连媒介计划都不知道,更不知道如何去策划!其实这个现场很普遍也和合情合理,对于企业来
说媒介计划一般都不会单独策划,是放在年度市场计划中一起策划出来的,因为媒介作为个体是无法单独完成手游的市场推广工作的!那今天正好在这里分享下手游
媒介如何制定媒介计划?
确定媒介目标
手游媒介制定准确的目标,方才能有的放矢,例如利用手游媒体你要达到一个什么样的宣传效果,通过媒介工作,游戏达到一个什么样的指标,这个是媒介目标!
制定媒介策略
媒介策略包括确定目标人群,根据目标人群确定手游媒体的选择,根据手游媒体选择确定与媒体合作的方式,是专区、是发包、是论坛活动、还是什么,这些
策略需要根据你对媒体的理解与产品的特性的理解来制订,如何利用这些媒体资源最大化的宣传你家游戏产品,这个是需要你去考虑的!
制定执行方案
根据媒介策略来撰写执行方案,从时间点出发,以天为单位进行规划媒介执行的事情!制定执行方案可直接使用数据图标,使执行人员对在某一期内所需要处理的工作有一个全面的了解!
手游媒介基本功五:媒介推广
无论是了解媒体、还是开拓媒体、还是制定媒介计划,无非都是为了推广产品或包装公司品牌形象做准备,以便更顺利的开展工作,那今天我就根据媒介日常
工作,会碰到的具体事项进行解读,因为大多数公司媒介工作,既肩负着产品营销的工作还要肩负着公司品牌建设的工作,所以本小节分为游戏产品推广及公司品牌
游戏产品推广
●游戏收录:收录通常是手游媒介工作的第一步,大致做法是媒体为该款手游制作主题站,其中包含游戏新闻、攻略、介绍、截图、公告、礼包、下载等,以
便让用户更为全面的了解该款游戏,但作为条件,媒体一般会要求在该游戏官网加入友情链接,游戏企业提供必要的素材,以支撑媒体收录!
●游戏新闻:手游的市场推广中,新闻营销绝对是必不可少的重要一环,游戏新闻也称之为游戏软文,是由手游企业市场部撰写的“文字广告”,主要目的是
在潜移默化中宣传自家游戏产品,赢得口碑或导入更多用户。新闻种类分为新闻、介绍、评测、攻略、心得、更新、公告、开服、礼包、活动等形式,具体可参考我
博客中之前撰写的《手游文案指南----手游新闻类型知多少?》一文!
●游戏发号:手游发号,其实是沿用端游营销的方式,通常是游戏企业给到媒体通用的或者独家的激活码、新手礼包、节日礼包等形式的序列号,以拉动玩家注册。发号一般都是配合新闻炒作同时进行的,数量可多可少,可根据运营活动单独定制!
●预约重点:手游媒介在发布新闻的时候,通常会和媒体编辑预约重点新闻,意思是媒体将妹子家的新闻推荐到首页焦点图或者置顶加亮,多数媒体会要求新闻质量,目前来说手游媒体预约重点新闻会有固定的时间,这个需要媒介去实践操作!
●游戏活动:游戏厂商通常会联系媒体,一起举办评测活动、互动活动、运营活动、节日活动、庆祝活动等。由游戏企业提供礼品或者资源,由媒体提供举办活动场所,例如论坛、专区等,目的是活跃玩家、媒体炒作,通常也是需要提前预约!
公司品牌推广
●企业新闻:企业包装通常离不开新闻曝光,新闻可以说企业的活动新闻、企业会议新闻、企业领导专访新闻、企业战略新闻、企业声明新闻、企业营销新闻等,目的都是包装企业品牌,提升手游企业的知名度,彰显企业技术示例,为渠道合作、游戏推广、争抢研发游戏、争抢IP做准备!
●企业参展:每年手游圈的各种大会确实不少,例如上方网大会、GMGC大会、chinajoy大会、媒体开发日、手游沙龙等,一家新手游企业如果想迅速出名,最好的做法是参加手游行业大会,并且邀请企业领导人做干货演讲,相信圈里知名度会迅速提高!
手游媒介基本功六:为人处事
●切忌傲慢不逊 目空一切:大公司的手游媒介,很可能会傲慢不逊,似乎常理的对小媒体爱搭不理,给媒体造成严重的心里落差,其实大可不必,手游圈子就这么大,风雨飘荡,几年后说不定他就是你的领导,所以多为自己积德,放下身段,怀着一颗平常心去交流!
●穿着举止谈吐到位:手游媒介是游戏公司对外宣传的接口人,时时刻刻代表着公司的品牌形象,所以时刻需要注意自己的言谈举止,嘴要甜,平常不要吝惜
你的喝彩声。好的夸奖,会让人产生愉悦感,但不要过头到令人反感!而媒介穿着好坏直接影响公司品牌形象,不求华丽高端只求朴素整洁!
●居安思危 奋进好学:手游媒介,尤其是有过2-3年经验的老同事,总觉得这几年一路顺风顺水,这时候你要多家小心了,时刻保持一颗清醒的心态,保持奋进好学的态度,这样你才不会在人才的涛涛江海中被淘汰!
●宽宏大方 友爱互助:做手游媒介,免不了经常聚会,大方一点,不会大方就学大方一点,如果大方真的让你很心痛,那就装大方一点;把媒体当作朋友,一方有难八方支援,友爱互助,只有这样,媒介妹子才能走的更长更久!
●信守承诺 手高眼低:我见过很多媒介妹子特别的浮躁,觉得做了2年的媒介,似乎可以顶上一个市场总监,其实不然,做事踏踏实实,要手高眼低,对于承诺的一定要努力做到,当然不要把别人对你的承诺一直记在心上并信以为真。
●空杯心态 善待人事:手游媒介和人交流多了,什么事情烦事、烂事都可能遇到,更应该怀有一颗平常心,没什么大不了的,好事要往坏处想,坏事要往好处想,做到待上以敬,待下以宽。
●严己律人
善于分享:手游媒介做到一定级别,会管理一个团队,对自己更应该严格要求,以做表率。在总结工作时要把错误都揽在自己身上,把功劳都记在下属身上。当上司
和下属同时在场时要记得及时表扬你的下属,批评一定要在只有你们两个人的情况下才能进行。要时刻保持分享的态度,与同事或下属分享你的成功果实及经验,不
要吝惜,因为帮助同事成长是一件最大的福利善事!
相信媒介妹子们看了手游媒介的六大基本功,也会有所顿悟,此时的我想起几年前在我的博客写的一段话,作为结尾送给大家:人生最重要的并不是努力,而
是方向,是在充满诱惑和迷茫的路途中,还能保持清醒的自己。压力并不是有人比你有钱、比你努力,而是比你牛B几倍的人依然比你努力。即使迷茫的你看不到希望、看不到未来,也不要随波逐流,安逸享乐的过完一生。因为第二天叫醒我的不止是闹钟,还有我的梦想!
将此文分享给好友
相关阅读:
由于欧盟委员会即将对发起反垄断指控,因此该公司将面临..
虽然玩手游的人在增加是现实,靠手游提高收益的公司也存..
虽然页游有着五年的黄金时期,但自从手游出现之后,在后..
记者日前从国家新闻出版广电总局数字出版司召开的网络游..
“任天堂不做手机游戏!”在数月之前,任天堂的社长岩田..
很多家长将电子游戏视为洪水猛兽,认为玩儿游戏会耽误青..
近日,成立刚满三年的UCloud正式对外宣布,已经完..
“主机游戏没落论”想必大家也不是第一次听到了,最近日..
约翰?范尼斯(Yoan Fanise)为全球知名游戏..
离职对于我来说是预料之中的,好像是我计划了的,但又不..“指”上谈兵-如何制作一款回合制策略游戏2
原文链接:
继续上一部分的学习,我们很快就会搞定一个完整的简单回合制策略游戏了!
在第一部分的教程中,我们学习了如何加载瓦片地图,初始化军事单位(陆战队员,大炮和直升机),以及如何使用A*寻路算法让他们在瓦片地图上运动。
而在这部分的教程中(同时也是最后一部分),我们将让这些军事单位完成自己的使命-开战!
在接下来的内容里,我们将向地图中添加建筑,然后添加游戏的赢输机制。此外,我们还将添加逻辑机制来切换玩家的操控顺序,同时还会增加一些锦上添花的音乐和音效。
当然,最让人开心的是,这个项目预留了扩展的空间,这样你就可以基于它来制作属于自己的回合制策略游戏!
在继续学习之前,确保你已经准备好了上一部分教程结束时的工程()。
然后,前进!冲锋!
“给我上,你们这帮猿人!想他妈活一辈子老不死吗?”
---- 一位无名副排长,1918年
添加弹出菜单
在进入战斗之前,让我们先花个几分钟时间来添加一个弹出菜单。当玩家移动完某个军事单位后,可以使用弹出菜单来选择其行为,如停留在新的位置无所事事,返回原来的位置,或者(可能的话)攻击旁边的敌军单位。
首先来添加一些辅助方法。当然,在此之前还是要先添加几个实例变量的声明。在Xcode中切换到HelloWorldLayer.h,然后添加以下实例变量:
& CCMenu *actionsM
& CCSprite *contextMenuB
以上我们定义了一个代表弹出菜单的CCMenu实例变量,并创建了一个精灵实例变量,指向菜单的背景。
为了从Unit类中访问菜单和前一个选中的军事单位,我们需要将actionMenu和selectedUnit定义为属性变量:
@property(nonatomic,assign) Unit *selectedU
@property(nonatomic,assign) CCMenu *actionsM
当然,别忘了在HelloWorldLayer.m中合成这两个属性:
@synthesize selectedU
@synthesize actionsM
再次切换到HelloWorldLayer.h,并添加两个辅助方法的定义:
-(void) showActionsMenu:(Unit*)unit
canAttack:(BOOL)canA
-(void) removeActionsM
接下来在HelloWorldLayer.m中实现这两个方法:
-(void)showActionsMenu:(Unit *)unit canAttack:(BOOL)canAttack
&&& // 1 -
Get the window size
&&& CGSize
wins = [[CCDirector sharedDirector] winSize];
&&& // 2 -
Create the menu background
contextMenuBck = [CCSprite spriteWithFile:@"popup_bg.png"];
addChild:contextMenuBck z:19];
&&& // 3 -
Create the menu option labels
CCLabelBMFont * stayLbl = [CCLabelBMFont labelWithString:@"Stay"
fntFile:@"Font_dark_size15.fnt"];
CCMenuItemLabel * stayBtn = [CCMenuItemLabel itemWithLabel:stayLbl
target:unit selector:@selector(doStay)];
CCLabelBMFont * attackLbl = [CCLabelBMFont
labelWithString:@"Attack" fntFile:@"Font_dark_size15.fnt"];
CCMenuItemLabel * attackBtn = [CCMenuItemLabel
itemWithLabel:attackLbl target:unit
selector:@selector(doAttack)];
CCLabelBMFont * cancelLbl = [CCLabelBMFont
labelWithString:@"Cancel" fntFile:@"Font_dark_size15.fnt"];
CCMenuItemLabel * cancelBtn = [CCMenuItemLabel
itemWithLabel:cancelLbl target:unit
selector:@selector(doCancel)];
&&& // 4 -
Create the menu
actionsMenu = [CCMenu menuWithItems:nil];
&&& // 5 -
Add Stay button
[actionsMenu addChild:stayBtn];
&&& // 6 -
Add the Attack button only if the current unit can attack
(canAttack) {
[actionsMenu addChild:attackBtn];
&&& // 7 -
Add the Cancel button
[actionsMenu addChild:cancelBtn];
&&& // 8 -
Add the menu to the layer
addChild:actionsMenu z:19];
&&& // 9 -
Position menu
[actionsMenu alignItemsVerticallyWithPadding:5];
(unit.mySprite.position.x & wins.width/2) {
[contextMenuBck setPosition:ccp(100,wins.height/2)];
[actionsMenu setPosition:ccp(100,wins.height/2)];
&&& } else
[contextMenuBck setPosition:ccp(wins.width-100,wins.height/2)];
[actionsMenu setPosition:ccp(wins.width-100,wins.height/2)];
-(void)removeActionsMenu {
Remove the menu from the layer and clean up
[contextMenuBck.parent removeChild:contextMenuBck cleanup:YES];
contextMenuBck =
[actionsMenu.parent removeChild:actionsMenu cleanup:YES];
actionsMenu =
以上两个方法分别用于创建弹出菜单,以及在不再需要的时候删除它。现在该菜单仅允许玩家取消或确认军事单位的行动,但最后将提供第三个选项以攻击某个敌军单位。很快我们就会添加该功能。
在Xcode中切换到Unit.m,找到popStepAndAnimate:方法,并使用以下代码替代if循环部分:
& // Check if there remain path steps to go
& if ([movementPath count] == 0) {
&&& moving =
unMarkPossibleMovement];
enemiesAreInRange = NO;
&&& // You'll
determine later if there is a nearby enemy to attack.
&&& [theGame
showActionsMenu:self canAttack:enemiesAreInRange];
& // Get the next step to move toward
在以上代码中,我们调用showActionsMenu:canAttack:这个辅助方法来显示弹出菜单。此时我们将enemiesAreInRange的属性硬性规定为NO,这样就不会在菜单中显示Attack选项。
接下来在ccTouchBegan:方法的顶部添加以下代码:
// If the action menu is showing, do not handle any touches
if (theGame.actionsMenu)
&&& return
// If the current unit is the selected unit, do not handle
any touches
if (theGame.selectedUnit == self)
&&& return
// If this unit has moved already, do not handle any
if (movedThisTurn)
&&& return
然后我们需要添加新的方法,以便处理玩家通过弹出菜单所选的行动。不够有一种行动需要我们判断当前的军事单位是否属于Solider(很快我们就会接触这段代码)。所以我们需要在Unit.m的顶部导入Solider单位的头文件:
#import "Unit_Soldier.h"
最后让我们在Unit.m的底部添加以下方法来实现弹出菜单中设定的各种行为:
// Stay on the current tile
-(void)doStay {
&&& // 1 -
Remove the context menu since we've taken an action
&&& [theGame
removeActionsMenu];
movedThisTurn = YES;
&&& // 2 -
Turn the unit tray to indicate that it has moved
&&& [mySprite
setColor:ccGRAY];
&&& [theGame
unselectUnit];
&&& // 3 -
Check for victory conditions
&&& if ([self
isKindOfClass:[Unit_Soldier class]]) {
// If this is a Soldier unit and it is standing over an enemy
building, the player wins.
// We'll handle this situation in detail later
// Attack another unit
-(void)doAttack {
You'll handle attack later
// Cancel the move for the current unit and go back to
previous position
-(void)doCancel {
Remove the context menu since we've taken an action
&&& [theGame
removeActionsMenu];
Move back to the previous tile
mySprite.position = [theGame
positionForTileCoord:tileDataBeforeMovement.position];
& &&[theGame
unselectUnit];
搞定收工!
编译运行项目,现在我们可以在单位移动完成后看到该菜单,并和它进行交互。
解决回合制的问题
如果你足够细心,会发现几个让人不爽的事情。首先,一旦你移动完某个单位后,就再也不能移动它了。而且你没法在玩家间切换,也就是说两方的单位都可以随时移动!
虽然这些不会影响游戏性,但这篇教程的主题是回合制策略游戏。所以,为了让这个主题得以实现,我们将在屏幕的顶部添加一个HUD,用于说明当前该哪个玩家来操控。HUD中还会提供一个按钮,从而把控制权交给另一个玩家。当然,每个玩家只能操控属于自己的单位。
为了实现上面的目的,我们需要添加几个实例变量。切换到HelloWorldLayer.h,并添加以下代码:
& CCMenuItemImage *endTurnB
& CCLabelBMFont *turnL
然后添加以下方法的定义:
-(void)addM
-(void)doEndT
-(void)setPlayerTurnL
-(void)showEndTurnT
-(void)beginT
-(void)removeLayer:(CCNode *)n;
-(void)activateUnits:(
切换到HelloWorldLayer.m,并在init的底部添加以下代码:
&&& //Set up
playerTurn = 1;
接下来,在文件的底部添加以上方法的实现代码:
// Add the user turn menu
-(void)addMenu {
&&& // Get
window size
&&& CGSize
wins = [[CCDirector sharedDirector] winSize];
&&& // Set
up the menu background and position
&&& CCSprite
* hud = [CCSprite spriteWithFile:@"uiBar.png"];
addChild:hud];
setPosition:ccp(wins.width/2,wins.height-[hud
boundingBox].size.height/2)];
&&& // Set
up the label showing the turn
&&& turnLabel
= [CCLabelBMFont labelWithString:[
stringWithFormat:@"Player %d's turn",playerTurn]
fntFile:@"Font_dark_size15.fnt"];
addChild:turnLabel];
[turnLabel setPosition:ccp([turnLabel boundingBox].size.width/2 +
5,wins.height-[hud boundingBox].size.height/2)];
&&& // Set
the turn label to display the current turn
setPlayerTurnLabel];
Create End Turn button
endTurnBtn = [CCMenuItemImage
itemFromNormalImage:@"uiBar_button.png"
selectedImage:@"uiBar_button.png" target:self
selector:@selector(doEndTurn)];
&&& CCMenu *
menu = [CCMenu menuWithItems:endTurnBtn, nil];
addChild:menu];
setPosition:ccp(0,0)];
[endTurnBtn setPosition:ccp(wins.width - 3 - [endTurnBtn
boundingBox].size.width/2, wins.height - [endTurnBtn
boundingBox].size.height/2 - 3)];
// End the turn, passing control to the other player
-(void)doEndTurn {
not do anything if a unit is selected
(selectedUnit)
Switch players depending on who's currently selected
(playerTurn ==1) {
playerTurn = 2;
&&& } else if
(playerTurn ==2) {
playerTurn = 1;
a transition to signify the end of turn
showEndTurnTransition];
&&& // Set
the turn label to display the current turn
setPlayerTurnLabel];
// Set the turn label to display the current turn
-(void)setPlayerTurnLabel {
&&& // Set
the label value for the current player
[turnLabel setString:[
stringWithFormat:@"Player %d's turn",playerTurn]];
Change the label colour based on the player
(playerTurn ==1) {
[turnLabel setColor:ccRED];
&&& } else if
(playerTurn == 2) {
[turnLabel setColor:ccBLUE];
// Fancy transition to show turn switch/end
-(void)showEndTurnTransition {
Create a black layer
&&& ccColor4B
c = {0,0,0,0};
CCLayerColor *layer = [CCLayerColor layerWithColor:c];
addChild:layer z:20];
&&& // Add
a label showing the player turn to the black layer
CCLabelBMFont * turnLbl = [CCLabelBMFont labelWithString:[
stringWithFormat:@"Player %d's turn",playerTurn]
fntFile:@"Font_silver_size17.fnt"];
&&& [layer
addChild:turnLbl];
&&& [turnLbl
setPosition:ccp([CCDirector
sharedDirector].winSize.width/2,[CCDirector
sharedDirector].winSize.height/2)];
&&& // Run
an action which fades in the black layer, calls the beginTurn
method, fades out the black layer, and finally removes it
&&& [layer
runAction:[CCSequence actions:[CCFadeTo actionWithDuration:1
opacity:150],[CCCallFunc actionWithTarget:self
selector:@selector(beginTurn)],[CCFadeTo actionWithDuration:1
opacity:0],[CCCallFuncN actionWithTarget:self
selector:@selector(removeLayer:)], nil]];
// Begin the next turn
-(void)beginTurn {
Activate the units for the active player
(playerTurn ==1) {
[self activateUnits:p2Units];
&&& } else if
(playerTurn ==2) {
[self activateUnits:p1Units];
// Remove the black layer added for the turn change
transition
-(void)removeLayer:(CCNode *)n {
&&& [n.parent
removeChild:n cleanup:YES];
此时你会发现我们少了一个方法activateUnits:的实现代码,这是因为该方法将激活所有属于当前玩家的军事单位。遗憾的是在Unit类中还没有方法实现它。
所以让我们切换到Unit.h,添加一个新的辅助方法的定义:
-(void)startT
接下来当然是在Unit.m中添加该方法的实现代码:
// Activate this unit for play
-(void)startTurn {
& // Mark the unit as not having moved for this
& movedThisTurn = NO;
& // Mark the unit as not having attacked this
& attackedThisTurn = NO;
& // Change the unit overlay colour from gray
(inactive) to white (active)
& [mySprite setColor:ccWHITE];
现在辅助方法就绪了,接下来让我们切换回HelloWorldLayer.m,并在文件的最后添加activateUnits:的实现代码:
// Activate all the units in the specified array (called from
beginTurn passing the units for the active player)
-(void)activateUnits:(
&&& for (Unit
*unit in units) {
[unit startTurn];
如你所见,该方法将会遍历当前玩家的军事单位数组,并使用startTurn方法来依次激活每个军事单位。
现在搞定了没?差不多了~不过还有一个小小的问题需要解决,此时我们还无法阻止玩家选择不属于自己的军事单位。
要修复这个问题很简单。在Xcode中切换到Unit.m,然后在ccTouchBegan:方法的顶部添加以下代码:
// Was a unit belonging to the non-active player touched? If
yes, do not handle the touch
if (([theGame.p1Units containsObject:self]
&& theGame.playerTurn == 2) ||
([theGame.p2Units containsObject:self]
&& theGame.playerTurn == 1))
&&& return
以上代码的作用是检查所触碰的单位是否属于玩家1或2,如果所触碰的单位不属于当前的玩家,则什么也不做。
编译运行游戏,我们会看到右上角有个按钮,可以让当前玩家将操控权交给下一个玩家。每个玩家都可以在自己的操控时间内移动属于自己的某个单位,然后该单位在下一回合前将不可用。
当你结束一个回合时,将可以看到切换效果。
攻击其它军事单位
搞了大半天,我们的战争机器还没有真正运转起来,不打仗还叫军事单位?首先我们来设定下逻辑:
1.当某个军事单位移动完成后,如果它在攻击范围内(临近某个敌军单位,大炮除外,因为其攻击范围很广),那么它可以攻击到的单位将使用红色标出。
2.如果玩家选择某个使用红色标出的单位,则战斗开始。
3.攻击单位将会首先开火,而如果防御单位幸免于难,则将开火反击。
4.每个单位所造成的伤害将由双方的单位类型来决定,和剪刀石头布的游戏有点像,每种单位都有自己的克星,同时也克制着另外几种单位。
首先要做的事情就是在移动完成后检查周边的敌军单位。为此,切换到Unit.m,并使用以下代码替换popStepAndAnimate:方法的#1部分:
// 1 - Check if the unit is done moving
if ([movementPath count] == 0) {
&&& // 1.1
- Mark the unit as not moving
&&& moving =
unMarkPossibleMovement];
&& // 1.2 - Mark the tiles
that can be attacked
markPossibleAction:kACTION_ATTACK];
&&& // 1.3
- Check for enemies in range
enemiesAreInRange = NO;
(TileData *td in theGame.tileDataArray) {
if (td.selectedForAttack) {
&&&&&&&&&&&
enemiesAreInRange = YES;
&&&&&&&&&&&
&&& // 1.4
- Show the menu and enable the Attack option if there are enemies
&&& [theGame
showActionsMenu:self canAttack:enemiesAreInRange];
在以上代码中的1.3部分,我们检查了tileDataArray,该数组保存了背景层中所有显示的瓦片,并判断是否有瓦片被标记为selectedForAttack。那么一个瓦片是如何被设置为selectedForAttack的呢?实际上在1.2部分中当我们调用markPossibleAction:时就已完成了设置。不过既然我们到现在为止还没有真正实施攻击,就必须将该功能添加到markPossibleAction:方法中。
在实际修改该方法前,还需要添加一些辅助方法。为什么呢?因为目前我们还没办法来检查包含敌军单位的瓦片。和之前一样,当我们所需的辅助方法在游戏区域中时,会把这些方法添加到HelloWorldLayer中。
首先在HelloWorldLayer.h中添加以下方法定义:
-(BOOL)checkAttackTile:(TileData *)tData
unitOwner:(int)
-(BOOL)paintAttackTile:(TileData *)tD
-(void)unPaintAttackT
-(void)unPaintAttackTile:(TileData *)tileD
然后切换到HelloWorldLayer.m,并在文件的最后添加方法的实现代码如下:
// Check the specified tile to see if it can be
-(BOOL)checkAttackTile:(TileData *)tData unitOwner:(int)owner
this tile already marked for attack, if so, we don't need to do
anything further
not, does the tile contain an enemy unit? If yes, we can attack
(!tData.selectedForAttack && [self
otherEnemyUnitInTile:tData unitOwner:owner]!= nil) {
tData.selectedForAttack = YES;
return NO;
&&& return
// Paint the given tile as one that can be attacked
-(BOOL)paintAttackTile:(TileData *)tData {
&&& CCSprite
* tile = [bgLayer tileAt:tData.position];
setColor:ccRED];
&&& return
// Remove the attack marking from all tiles
-(void)unPaintAttackTiles {
(TileData * td in tileDataArray) {
[self unPaintAttackTile:td];
// Remove the attack marking from a specific tile
-(void)unPaintAttackTile:(TileData *)tileData {
&&& CCSprite
* tile = [bgLayer tileAt:tileData.position];
setColor:ccWHITE];
ok辅助方法已经完全准备好了,接下来我们可以让Unit对象调用checkAttackTile:unitOwner方法,以检查完成移动的军事单位附近的每个瓦片。
我们需要在Unit.m的markPossibleAction:方法中几次调用该方法。首先,将之前注释的else
if(action ==kAction_ATTACK)语句替换为以下语句:
else if (action == kACTION_ATTACK) {
&&& [theGame
checkAttackTile:startTileData unitOwner:owner];
然后替换for循环中的被注释的else if条件语句:
else if (action == kACTION_ATTACK) {
&&& [theGame
checkAttackTile:_neighbourTile unitOwner:owner];
最后,紧接着上面的语句还有一个else if条件语句,不过它并没有被注释,使用以下语句替换其中的内容:
else if (action == kACTION_ATTACK) {
the tile not in attack range?
([_neighbourTile getGScoreForAttack]& attackRange)
// Ignore it
我们还需要实现doAttack:方法,当玩家从弹出菜单中选择Attack选项时将调用该方法。使用以下代码替换doAttack方法的内容:
// Attack another unit
-(void)doAttack {
&&& // 1 -
Remove the context menu since we've taken an action
&&& [theGame
removeActionsMenu];
&&& // 2 -
Check if any tile has been selected for attack
(TileData *td in theGame.tileDataArray) {
if (td.selectedForAttack) {
&&&&&&&&&&&
// 3 - Mark the selected tile as attackable
&&&&&&&&&&&
[theGame paintAttackTile:td];
selectingAttack = YES;
一旦某个军事单位完成了当前的回合,我们将会取消瓦片上的可攻击标记。让我们来添加一个方法来实现这一点。和之前一样,首先在Unit.h中添加一个方法定义:
-(void)unMarkPossibleA
然后在Unit.m中添加该方法的实现代码:
// Remove attack selection marking from all tiles
-(void)unMarkPossibleAttack {
(TileData *td in theGame.tileDataArray) {
[theGame unPaintAttackTile:td];
td.parentTile =
td.selectedForAttack = NO;
最后,在unselectUnit方法的结尾处添加代码调用刚才的方法:
[self unMarkPossibleAttack];
编译运行游戏。如果你移动某个单位靠近敌军单位,并从弹出菜单中选择attack,敌军单位就会变红。
可是现在又有了一些小问题。当我们触碰要攻击的敌军单位时,并没有发生其它事情。此外,一旦某个单位被选择用于攻击,就无法结束当前的回合。
接下来,我们将学习如何处理真实的火力交锋,伤害计算和单位的损伤,以及解决回合结束的问题。
首先还是要在HelloWorldLayer中添加一个新的辅助方法,用于判断攻击中所受到的伤害。在HelloWorldLayer.h中添加以下方法定义:
-(int)calculateDamageFrom:(Unit *)attacker onDefender:(Unit
然后切换到HelloWorldLayer.m,首先导入所需的单位类型头文件:
#import "Unit_Soldier.h"
#import "Unit_Tank.h"
#import "Unit_Cannon.h"
#import "Unit_Helicopter.h"
然后添加该方法的实现代码:
// Calculate the damage inflicted when one unit attacks
another based on the unit type
-(int)calculateDamageFrom:(Unit *)attacker onDefender:(Unit
*)defender {
([attacker isKindOfClass:[Unit_Soldier class]]) {
if ([defender isKindOfClass:[Unit_Soldier class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Helicopter class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Tank class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Cannon class]]) {
&&&&&&&&&&&
&&& } else if
([attacker isKindOfClass:[Unit_Tank class]]) {
if ([defender isKindOfClass:[Unit_Soldier class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Helicopter class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Tank class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Cannon class]]) {
&&&&&&&&&&&
&&& } else if
([attacker isKindOfClass:[Unit_Helicopter class]]) {
if ([defender isKindOfClass:[Unit_Soldier class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Helicopter class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Tank class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Cannon class]]) {
&&&&&&&&&&&
&&& } else if
([attacker isKindOfClass:[Unit_Cannon class]]) {
if ([defender isKindOfClass:[Unit_Soldier class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Helicopter class]]) {
&&&&&return
} else if ([defender isKindOfClass:[Unit_Tank class]]) {
&&&&&&&&&&&
} else if ([defender isKindOfClass:[Unit_Cannon class]]) {
&&&&&&&&&&&
&&& return
接下来,我们需要在Unit类中添加方法来执行攻击和处理攻击。切换到Unit.h,并添加以下方法定义:
-(void)doMarkedAttack:(TileData *)targetTileD
-(void)attackedBy:(Unit *)attacker
firstAttack:(BOOL)firstA
-(void)dealDamage:(
-(void)removeExplosion:(CCSprite *)e;
然后切换到Unit.m,并在文件的最后添加以上方法的实现代码:
// Attack the specified tile
-(void)doMarkedAttack:(TileData *)targetTileData {
Mark the unit as having attacked this turn
attackedThisTurn = YES;
&&& // Get
the attacked unit
*attackedUnit = [theGame otherEnemyUnitInTile:targetTileData
unitOwner:owner];
&&& // Let
the attacked unit handle the attack
[attackedUnit attackedBy:self firstAttack:YES];
Keep this unit in the curren location
// Handle the attack from another unit
-(void)attackedBy:(Unit *)attacker firstAttack:(BOOL)firstAttack
Create the damage data since we need to pass this information on to
another method
*damageData = [
dictionaryWithCapacity:2];
[damageData setObject:attacker forKey:@"attacker"];
[damageData setObject:[
numberWithBool:firstAttack] forKey:@"firstAttack"];
Create explosion sprite
&&& CCSprite
*explosion = [CCSprite spriteWithFile:@"explosion_1.png"];
addChild:explosion z:10];
[explosion setPosition:mySprite.position];
Create explosion animation
CCAnimation *animation = [CCAnimation animation];
&&& for (int
i=1;i&=7;i++) {
[animation addFrameWithFilename: [
stringWithFormat:@"explosion_%d.png", i]];
&&& id action
= [CCAnimate actionWithDuration:0.5 animation:animation
restoreOriginalFrame:NO];
&&& // Run
the explosion animation, call method to remove explosion once it's
done and finally calculate damage from
[explosion runAction: [CCSequence actions: action,
[CCCallFuncN actionWithTarget:self
selector:@selector(removeExplosion:)],
[CCCallFuncO actionWithTarget:self selector:@selector(dealDamage:)
object:damageData],
// Calculate damage from attack
-(void)dealDamage:(
*)damageData {
&&& // 1 -
Get the attacker from the passed in data dictionary
*attacker = [damageData objectForKey:@"attacker"];
&&& // 2 -
Calculate damage
[theGame calculateDamageFrom:attacker onDefender:self];
&&& // 3 -
Is the unit dead?
// 4 - Unit is dead - remove it from game
[self.parent removeChild:self cleanup:YES];
if ([theGame.p1Units containsObject:self]) {
&&&&&&&&&&&
[theGame.p1Units removeObject:self];
} else if ([theGame.p2Units containsObject:self]) {
&&&&&&&&&&&
[theGame.p2Units removeObject:self];
&&& } else
// 5 - Update HP for unit
[self updateHpLabel];
// 6 - Call attackedBy: on the attacker so that damage can be
calculated for the attacker
if ([[damageData objectForKey:@"firstAttack"] boolValue]
&& !attacker.hasRangedWeapon
&& !self.hasRangedWeapon) {
&&&&&&&&&&&
[attacker attackedBy:self firstAttack:NO];
// Clean up after explosion
-(void)removeExplosion:(CCSprite *)e {
Remove the explosion sprite
&&& [e.parent
removeChild:e cleanup:YES];
接下来,我们需要判断所选择的单位要移动或是攻击。在Unit类中已经有了相关的实例变量,但它们不是属性变量,因此无法从HelloWorldLayer中访问。因此首先需要创建属性。
切换到Unit.h,并添加以下属性声明:
@property (nonatomic,readwrite) BOOL selectingM
@property (nonatomic,readwrite) BOOL selectingA
接下来在Unit.m中合成这两个属性:
@synthesize selectingAttack,selectingM
最后,我们需要在HelloWorldLayer.m的ccTouchesBegan方法中添加一个else条件:
else if(td.selectedForAttack) {
Attack the specified tile
[selectedUnit doMarkedAttack:td];
Deselect the unit
unselectUnit];
Tapped a non-marked tile. What do we do?
(selectedUnit.selectingAttack) {
// Was in the process of attacking - cancel attack and show
selectedUnit.selectingAttack = NO;
[self unPaintAttackTiles];
[self showActionsMenu:selectedUnit canAttack:YES];
& &&} else if
(selectedUnit.selectingMovement) {
// Was in the process of moving - just remove marked tiles and
await further action
selectedUnit.selectingMovement = NO;
[selectedUnit unMarkPossibleMovement];
[self unselectUnit];
好了,现在我们可以攻击并摧毁敌军单位了!
编译运行游戏,来享受战斗的乐趣吧!
游戏机制的最后一环是,当所有的军事单位都消失后,是否该发生点什么?如果你对第一部分还记忆犹新,那么该记得其中提到过,最后应该显示游戏胜利的场景。
那么我们该如何判断游戏胜利了呢?其实很简单。在每次销毁单位后,程序都会判断是否该单位是玩家的最后一个军事单位。如果是,那么程序会显示一个庆祝画面,并重启游戏。
为了检查是否有更多的敌军单位,以及显示庆祝游戏胜利的消息,还得再添加几个辅助方法。切换到HelloWorldLayer.h,并添加以下方法定义:
-(void)checkForMoreU
-(void)showEndGameMessageWithWinner:(int)winningP
-(void)restartG
紧接着在HelloWorldLayer.m中添加以上方法的实现代码:
// Check if each player has run out of units
-(void)checkForMoreUnits {
([p1Units count]== 0) {
[self showEndGameMessageWithWinner:2];
&&& } else
if([p2Units count]== 0) {
[self showEndGameMessageWithWinner:1];
// Show winning message for specified player
-(void)showEndGameMessageWithWinner:(int)winningPlayer {
Create black layer
&&& ccColor4B
c = {0,0,0,0};
CCLayerColor * layer = [CCLayerColor layerWithColor:c];
addChild:layer z:20];
&&& // Add
background image to new layer
&&& CCSprite
* bck = [CCSprite spriteWithFile:@"victory_bck.png"];
&&& [layer
addChild:bck];
setPosition:ccp([CCDirector
sharedDirector].winSize.width/2,[CCDirector
sharedDirector].winSize.height/2)];
Create winning message
CCLabelBMFont * turnLbl = [CCLabelBMFont labelWithString:[
stringWithFormat:@"Player %d
wins!",winningPlayer]&
fntFile:@"Font_dark_size15.fnt"];
&&& [layer
addChild:turnLbl];
&&& [turnLbl
setPosition:ccp([CCDirector
sharedDirector].winSize.width/2,[CCDirector
sharedDirector].winSize.height/2-30)];
Fade in new layer, show it for 2 seconds, call method to remove
layer, and finally, restart game
&&& [layer
runAction:[CCSequence actions:[CCFadeTo actionWithDuration:1
opacity:150],[CCDelayTime actionWithDuration:2],[CCCallFuncN
actionWithTarget:self selector:@selector(removeLayer:)],[CCCallFunc
actionWithTarget:self selector:@selector(restartGame)], nil]];
// Restart game
-(void)restartGame {
[[CCDirector sharedDirector] replaceScene:[CCTransitionJumpZoom
transitionWithDuration:1 scene:[HelloWorldLayer scene]]];
为了调用以上所定义的方法,我们需要切换到Unit.m,并在dealDamage:方法的#4部分的最后添加一条代码以调用checkForMoreUnits方法:
[theGame checkForMoreUnits];
你可能会奇怪为什么我们会在#4部分添加checkForMoreUnits方法,而且#6部分调用了attackedBy:方法。注意attackedBy:方法是在攻击方单位上调用的,而接下来将会对被攻击单位调用dealDamage:方法。这样在二次运行代码的时候将再次调用checkForMoreUnits方法。
记住,每当有某个单位攻击另一个单位时,都需要checkForMoreUnits。
现在编辑运行游戏,然后消灭某一方的全部单位。此时你会看到一个庆祝胜利法赢得游戏的画面,然后过一会儿游戏会重新启动。
在教程的开始我曾经提到过,有两种方式赢得游戏:消灭对方的所有军事单位,或是让己方的陆战队员占领对方的总部。现在就可以来实现第二种方式了。
在项目中添加一个新的Building类,并让其继承自CCNode。
使用以下代码替代Building.h中的内容:
&Foundation/Foundation.h&
#import "cocos2d.h"
#import "HelloWorldLayer.h"
#import "GameConfig.h"
@class HelloWorldL
@interface Building : CCNode {
HelloWorldLayer *theG
&&& CCSprite
@property (nonatomic,assign)CCSprite *myS
@property (nonatomic,readwrite)
-(void)createSprite:(
然后使用以下代码替代Building.m中的内容:
#import "Building.h"
@implementation Building
@synthesize mySprite,
-(id)init {
((self=[super init])) {
&&& return
-(void)createSprite:(
*)tileDict {
&&& // Get
the sprite position and dimension from tile data
&&& int x =
[[tileDict valueForKey:@"x"] intValue]/[theGame spriteScale];
&&& int y =
[[tileDict valueForKey:@"y"] intValue]/[theGame spriteScale];
&&& int width
= [[tileDict valueForKey:@"width"] intValue]/[theGame
spriteScale];
height = [[tileDict valueForKey:@"height"] intValue];
&&& // Get
the height of the building in tiles
heightInTiles = height/[theGame getTileHeightForRetina];
Calculate x and y values
(heightInTiles * [theGame getTileHeightForRetina]/(2*[theGame
spriteScale]));
Create building sprite and position it
&&& mySprite
= [CCSprite spriteWithFile:[
stringWithFormat:@"%@_P%d.png",[tileDict
valueForKey:@"Type"],owner]];
addChild:mySprite];
mySprite.userData =
mySprite.position = ccp(x,y);
如你所见,以上代码非常简单,和之前Unit类的内容区别不大。
现在我们需要创建作为Building子类的Building_HQ类。
在项目中通过iOS\Cocoa Touch\Objective-C
class模板添加Building_HQ类,并让其继承自Building类。
使用以下代码替代Building_HQ.h中的内容:
&Foundation/Foundation.h&
#import "cocos2d.h"
#import "Building.h"
@interface Building_HQ : Building {
+(id)nodeWithTheGame:(HelloWorldLayer *)_game tileDict:(
*)tileDict owner:(int)_
-(id)initWithTheGame:(HelloWorldLayer *)_game tileDict:(
*)tileDict owner:(int)_
然后使用代码替换Building_HQ.m中的内容:
#import "Building_HQ.h"
@implementation Building_HQ
+(id)nodeWithTheGame:(HelloWorldLayer *)_game tileDict:(
*)tileDict owner:(int)_owner {
&&& return
[[[self alloc] initWithTheGame:_game tileDict:tileDict
owner:_owner] autorelease];
-(id)initWithTheGame:(HelloWorldLayer *)_game tileDict:(
*)tileDict owner:(int)_owner {
((self=[super init])) {
theGame = _
[self createSprite:tileDict];
[theGame addChild:self z:1];
&&& return
Building_HQ类更简单,仅仅是创建而已。
接下来我们将为双方玩家添加建筑。
切换到HelloWorldLayer.h,并添加以下实例变量代表双方建筑:
在HelloWorldLayer.h的顶部导入Building类的头文件,并预定义该类:
#import "Building.h"
我们还需要添加几个辅助方法来加载建筑,并使用特定的瓦片来标识。在HelloWorldLayer.h中添加以下方法定义:
-(void)loadBuildings:(int)
-(Building *)buildingInTile:(TileData *)
切换到HelloWorldLayer.m,并在init方法中的[self addMenu]代码前添加以下代码:
// Create building arrays
p1Buildings = [[
alloc] initWithCapacity:10];
p2Buildings = [[
alloc] initWithCapacity:10];
// Load buildings
[self loadBuildings:1];
[self loadBuildings:2];
同时别忘了释放数组所占用的内存。在dealloc中添加以下代码:
[p1Buildings release];
[p2Buildings release];
当然,也别忘了实现之前所定义的方法。在文件的最后添加以下代码:
// Load buildings for layer
-(void)loadBuildings:(int)player {
&&& // Get
building object group from tilemap
CCTMXObjectGroup *buildingsObjectGroup = [tileMap
objectGroupNamed:[
stringWithFormat:@"Buildings_P%d",player]];
&&& // Get
the correct building array based on the current player
*buildings =
(player == 1)
buildings = p1B
(player == 2)
buildings = p2B
Iterate over the buildings in the array, adding them to the
*buildingDict in [buildingsObjectGroup objects]) {
// Get the building type
dictionaryWithDictionary:buildingDict];
*buildingType = [d objectForKey:@"Type"];
// Get the right building class based on type
*classNameStr = [
stringWithFormat:@"Building_%@",buildingType];
Class theClass = NSClassFromString(classNameStr);
// Create the building
Building *building = [theClass nodeWithTheGame:self tileDict:d
owner:player];
[buildings addObject:building];
// Return the first matching building (if any) on the given
-(Building *)buildingInTile:(TileData *)tile {
Check player 1's buildings
(Building *u in p1Buildings) {
if (CGPointEqualToPoint([self
tileCoordForPosition:u.mySprite.position], tile.position))
&&&&&&&&&&&
Check player 2's buildings
(Building *u in p2Buildings) {
if (CGPointEqualToPoint([self
tileCoordForPosition:u.mySprite.position], tile.position))
&&&&&&&&&&&
&&& return
需要注意的是,buildingInTile:会返回它在指定瓦片上找到的第一个建筑。如果两个建筑在同一个瓦片上,这样做可能会导致麻烦。好在我们的游戏不是这样设定的,所以暂且无需为这一点而担忧。
最后一步是确保当陆战队单位移动到敌军总部时让游戏结束。切换到Unit.m,在doStay:方法的#3部分的if循环中添加以下代码:
// Get the building on the current tile
Building *buildingBelow = [theGame buildingInTile:[theGame
getTileData:[theGame tileCoordForPosition:mySprite.position]]];
// Is there a building?
if (buildingBelow) {
the building owned by the other player?
(buildingBelow.owner != self.owner) {
NSLog(@"Building captured!!!");
// Show end game message
[theGame showEndGameMessageWithWinner:self.owner];
终于搞定了!编译运行游戏,你会看到屏幕的两端都会出现敌军总部。试着把某个陆战队员单位移动到敌军总部上,游戏届时将会结束。
音乐和音效
整个项目差不多完工了。不过如果一款游戏没有音乐和音效,对玩家的吸引力也会大打折扣。我从中搞了点音乐,然后使用CXFR制作了音效。
首先在HelloWorldLayer.m中导入SimpleAudioEngine的头文件:
#import "SimpleAudioEngine.h"
在init方法的最后,在[self addMenu]语句之后添加以下代码:
// Play background music
[[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"Five
Armies.mp3" loop:YES];
最后让我们给不同事件添加一些音效。
首先是当玩家选择”End Turn”按钮的时候,在doEndTurn方法的第一个if语句后添加以下代码:
// Play sound
[[SimpleAudioEngine sharedEngine] playEffect:@"btn.wav"];
然后在Unit.m的顶部添加代码:
#import "SimpleAudioEngine.h"
,并在attackedBy:firstAttack:方法的最后一行代码(播放爆炸动画)前添加以下代码:
// Play damage sound
[[SimpleAudioEngine sharedEngine] playEffect:@"hurt.wav"];
在dealDamage:方法的#4部分的开始处添加以下代码:
// Unit destroyed sound
[[SimpleAudioEngine sharedEngine]
playEffect:@"explosion.wav"];
最后的最后的最后,在doStay,doAttack和doCancel方法的顶部添加以下代码:
// Play menu selection sound
[[SimpleAudioEngine sharedEngine] playEffect:@"btn.wav"];
于是,终于,我们完全搞定了!编译运行游戏,一款简单但完整的回合制策略游戏就大功告成了!
接下来做什么?
首先这里是本教程的完整示例:
不过现在还不是自鸣得意的时候。即便游戏的雏形已经有了,但如果要把它变成一款真正的产品,还需要很多事情要做:
1.何不添加一些新的更有趣的单位?比如,只能在水里移动的舰艇,可以搭载士兵的单位,可以治疗其它单位的医护兵或救护车,等等。
2.何不添加一些其它类型的建筑,比如可以使用现金来修建坦克的工厂,或是医院。
3.何不多提供几种地图选择?玩家可以选择其它地形,或是可以被摧毁的物体?
4.你也可以完全改变游戏的风格,比如一个中世纪魔幻风格的游戏,或是充满了外星异形战士的科幻题材游戏。
尽情发挥你的创意和想象力吧,期待着你的佳作!
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。