• <cocos2dx for wp7>使用cocos2dx制作基于Tile地图的游戏:不一样的战斗(回合制战斗)(四)


    本文是《<cocos2d-x for wp7>使用cocos2d-x制作基于Tile地图的游戏》教程的第四部分,也就是最后一个部分。如果你没有对cocos2d-x不了解,而且没有看过前面部分的教程。可以到我博客里面找到相关文章学习。

    本文假定你已经学习过前面的教程,并且对cocos2d-x编程有一定了解。或者你有相关的同等的经验。

    程序截图:

    遥想当年玩FC的时代,经典的遇敌方式是什么呢,我觉得应该是踩地雷式的,就是在Tile地图上走着走着,就进入战斗画面了。并且是回合制战斗。想想都怀念了。那么这类是怎么做的呢。这里,就来展示下怎么制作吧。

    在本文中,是基于<cocos2d-x for wp7>使用cocos2d-x制作基于Tile地图的游戏:碰撞检测和收集物品(二)完成的基础上继续的,如果没有保存有这个代码,或者其他的原因没有代码。可以到这里下载到需要的代码(http://dl.dbank.com/c02vzbkoyl)。

    这里,我们这些数据都硬编码上去,而且怪物只有一个,并且不能逃跑(因为只设定了一个按钮,就是攻击按钮)。当然,忍者攻击和怪物被攻击,怪物攻击,怪物死亡都是动态的。而且,最重要的是,一切攻击情况都在右下角显示出来,而且是中文的哦。遥想当年神马外星科技做的中文。呵呵。

    在这个教程中,我们需要用到的怪物图片,这个图片是我在百度图库找到。另外修改了下使之透明。图片下载:http://dl.dbank.com/c07co6i9aihttp://dl.dbank.com/c01acabz4y并且添加到images文件夹。

    添加中文支持

      为了能够实现中文,我们这里要添加中文支持。因为我觉得这种回合制游戏还是中文的爽。添加中文支持,重复的问题了。这个不想重复讨论。没有操作过的直接看这里:http://www.cnblogs.com/fengyun1989/archive/2012/04/22/2476048.html

      如上面那篇文章所操作的,我同样添加了一个FontProcessor工程,也添加了一个YaheiFont.spriteFont的字体文件到Content工程的font文件夹。唯一改的代码就是messages.txt的路径问题。不过看那文章大家应该懂得怎么做了。另外,不一样的是,我设置了那个YaheiFont文件里面的size标签为22.因为我发现用调用代码方式无法改变字体大小。

    并且,我添加了以下文字到messags.txt文件。因为我只需要用到这些中文字符。“飞镖攻击怪物忍者遭受突然遭遇受到伤害死亡胜利HP躲闪冲撞”。注意,我在显示文字的时候用到的标点都是英文标点,如果用到中文的标点,需要也添加到messages.txt。

    战斗场景

    下面我们先添加一个类BattleScene到classes文件夹。并且使之继承与CCScene。修改代码为:

        class BattleScene : CCScene
        {
            public BattleScene()
            {
                this.addChild(BattleLayer.node());
            }
        }
    
        class BattleLayer : CCLayer
        {
            public static new BattleLayer node()
            {
                BattleLayer layer = new BattleLayer();
                if (layer.init())
                    return layer;
                return null;
            }
        }

    并且修改AppDelegate类的Launch方法。使之直接运行这个BattleScene。这样方便测试。

                //TileMapScene pScene = new TileMapScene();
                BattleScene pScene = new BattleScene();
                //run
                pDirector.runWithScene(pScene);

    现在添加以下代码到BattleLayer:

            CCSprite player;
            CCSprite monster;
            CCMenu menu;
            public override bool init()
            {
                if (!base.init())
                    return false;
                CCSize winSize = CCDirector.sharedDirector().getWinSize();
                player = CCSprite.spriteWithFile(@"Resources/Player");
                player.position = new CCPoint(100, winSize.height - 100);
                this.addChild(player);
                monster = CCSprite.spriteWithFile(@"images/monster");
                monster.position = new CCPoint(winSize.width - 100, winSize.height - 100);
                this.addChild(monster);
    
                CCLabelTTF label = CCLabelTTF.labelWithString("飞镖攻击", "YaheiFont", 20);
                CCMenuItem attackItem = CCMenuItemLabel.itemWithLabel(label, this, attack);
                menu = CCMenu.menuWithItems(attackItem);
                menu.position = new CCPoint(100, 100);
                this.addChild(menu);
                return true;
            }
    
            void attack(object sender)
            { 
                
            }

    上面我们做了什么呢,添加了一个忍者和一个怪物,并且添加了一个菜单,注意,菜单是用到了我们上面做的中文字体。不是图片哦。编译运行,就能看到我们的中文菜单和忍者怪物。当然,现在点击攻击没有任何反应。

    下面添加以下声明到BattleLayer类中;

            CCLabelTTF battleMsg;
            CCLabelTTF HPLabel;
            int playerHP = 40;
            int monsterHP = 30;
            int playerA = 10;
            int monsterA = 8;
            int playerDefence = 5;
            int monsterDefence = 3;
            float playerHit = 0.9f;
            float monsterHit = 0.6f;
            float playerDodge = 0.2f;
            float monsterDodge = 0.1f;
            bool attacking = false;
         Random random = new Random();

    这里,我们硬编码了忍者和怪物的数据。上面的数据有HP,攻击力(A),防御力(Defence),命中率(Hit),躲闪率(Dodge)。下面。在init添加代码:

                battleMsg = CCLabelTTF.labelWithString("遭遇怪物","YaheiFont", 32);
                battleMsg.position = new CCPoint(winSize.width - 200, 100);
                this.addChild(battleMsg);
    
                HPLabel = CCLabelTTF.labelWithString(String.Format("HP:{0}", playerHP), "Arial", 32);
                HPLabel.position = new CCPoint(100, winSize.height - 150);
                this.addChild(HPLabel);

    这里初始化了这些label。这些用来显示相关信息的。

    添加这么一个方法:

            void updateBattle(bool isPlayerAttack)
            {
    if (isPlayerAttack) { int beingAttack = (int)(playerHit * (1.0f - monsterDodge) * 100); if (random.Next() % 100 < beingAttack) { int damage = playerA - monsterDefence; monsterHP = monsterHP - damage; if (monsterHP <= 0) { battleMsg.setString("忍者飞镖攻击,\n怪物受到伤害{0}HP\n怪物死亡,忍者胜利"); attacking = false; } else battleMsg.setString(String.Format("忍者飞镖攻击,\n怪物受到伤害{0}HP", damage)); } else battleMsg.setString("忍者飞镖攻击,\n怪物躲闪"); } else { int beingAttack = (int)(monsterHit * (1.0f - playerDodge) * 100); if (random.Next() % 100 < beingAttack) { int damage = monsterA - playerDefence; playerHP = playerHP - damage; HPLabel.setString(String.Format("HP:{0}", playerHP)); if (playerHP <= 0) { battleMsg.setString("怪物冲撞攻击,\n忍者受到伤害{0}HP\n忍者死亡"); } else battleMsg.setString(String.Format("怪物冲撞攻击,\n忍者受到伤害{0}HP", damage)); } else battleMsg.setString("怪物冲撞攻击,\n忍者躲闪"); } }

    在这个方法里面,更新了战斗的相关数据更新,我这里是这么定义命中的,攻击者的命中率*(1-被打者的躲闪率)。然后取一个随机数,如果在命中的范围,就算是命中,不然就躲闪。

    伤害=攻击力-防御力。战斗就这样简单定义了。

    下面,要做的是定义动作,忍者攻击的时候飞镖肯定是要发射出去的吧。怪物被打总得疼一下吧。那样就得有动作了。

            void playerAttack()
            {
                CCSprite projectile = CCSprite.spriteWithFile(@"images/Projectile");
                projectile.position = new CCPoint(player.position.x, player.position.y);
                projectile.runAction(CCSequence.actions(
                    CCMoveTo.actionWithDuration(1.0f, new CCPoint(monster.position.x, monster.position.y)),
                    CCCallFuncN.actionWithTarget(this, playerAttackDone)));
                this.addChild(projectile);
            }
    
            void playerAttackDone(object sender)
            {
                CCSprite sprite = sender as CCSprite;
                monster.runAction(CCBlink.actionWithDuration(0.3f, 3));
                updateBattle(true);
                this.removeChild(sprite, true);
            }

    并且在attack方法里面添加这么一行;

    playerAttack();

    上面我们做了什么呢,在忍者攻击的时候,添加了一个飞镖精灵使之从忍者位置运动到怪物位置。并且,添加了一个回调函数来实现当飞镖命中怪物的时候,怪物做一个blink的运动来闪一下表示疼。

    编译运行就发现,忍者能发射飞镖了。而且飞镖命中怪物的时候,怪物闪了一下。它感觉到疼了。

    下面,怪物被打了当然要反击了。 

            void monsterAttack()
            {
                var moveleft = CCMoveBy.actionWithDuration(0.1f, new CCPoint(-130, 0));
                var delay = CCDelayTime.actionWithDuration(0.5f);
                monster.runAction(CCSequence.actions(delay, moveleft, moveleft.reverse(), delay,
                    CCCallFunc.actionWithTarget(this, monsterAttackDone)));
            }
    
            void monsterAttackDone()
            {
                updateBattle(false);
            }

    怪物攻击只有一种,就是冲撞攻击,这个做法就是让怪物速度移动然后回来。这个效果做得有点一般,不过看起来还过得去。下面,我们要设计的是攻击流程,忍者攻击完怪物攻击。攻击期间菜单应该不能使用。那么,添加以下代码:

              //add at the end of the monsterAttackDone 
           if (attacking)
                {
                    attacking = false;
                    playerAttack();
    
                }
                else
                    menu.visible = true;
    
           //add at the end of playerAttackDone
                if (attacking)
                {
                    attacking = false;
                    monsterAttack();
                }
                else
                    menu.visible = true;
    
                //replace the attack method code of these
                int r = random.Next() % 7;
                if (r == 0)
                {
                    battleMsg.setString("怪物突然攻击");
                    attacking = true;
                    monsterAttack();
                }
                else 
                {
                    attacking = true;
                    playerAttack();
                }
                menu.visible = false;

    上面,设定了怪物有一定几率突然袭击。并且用attacking来判断是否已经开始攻击。先攻击方攻击结束后attacking会置为false,然后后攻击方攻击。那么在后攻击方攻击后,attacking为false,那么就停止攻击,menu重现,进入下一回合。

    现在,可以基本完成这个战斗了。还有一些要修改的。先添加Microsoft.phone和System.Window的引用,因为我们用PhoneApplicationService来保存TileMapScene场景的引用,用做场景返回用。其实这个疯狂的做法微软认为是不安全的。但是感觉还不错。

    添加方法:

            void returnTileScene()
            {
                TileMapScene tileScene = null;
                if (PhoneApplicationService.Current.State.ContainsKey("TileScene"))
                    tileScene = (TileMapScene)PhoneApplicationService.Current.State["TileScene"];
                if (tileScene != null)
                {
                    CCDirector.sharedDirector().replaceScene(tileScene);
                }
                else
                {
                    TileMapScene pScene = new TileMapScene();
                    PhoneApplicationService.Current.State["TileScene"] = pScene;
                }
            }

    并在updateBattle中怪物死亡的if语句最后添加:

                            monster.runAction(CCSequence.actions(CCFadeOut.actionWithDuration(0.3f),CCCallFunc.
                                actionWithTarget(this,returnTileScene)));

    这样,怪物死亡后,就会慢慢隐去,然后跳转到TileMapScene。

    不过,虽然忍者基本不会死去,但是也是有可能的,毕竟如果你运气实在是太背的话。先添加一个GameoverScene,代码如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using cocos2d;
    using Microsoft.Phone.Shell;
    namespace cocos2dTIleMapGameDemo.Classes
    {
        class GameOverScene : CCScene
        {
            public GameOverScene()
            {
                CCLabelTTF label = CCLabelTTF.labelWithString("YOU LOSE!", "Arial", 32);
                label.position = new CCPoint(400, 300);
                this.addChild(label);
                this.runAction(CCSequence.actions(CCDelayTime.actionWithDuration(3.0f), CCCallFunc.actionWithTarget(this, gameOverDone)));
            }
    
            void gameOverDone()
            {
                TileMapScene tileScene = null;
                if (PhoneApplicationService.Current.State.ContainsKey("TileScene"))
                    tileScene = (TileMapScene)PhoneApplicationService.Current.State["TileScene"];
                if (tileScene != null)
                {
                    CCDirector.sharedDirector().replaceScene(tileScene);
                }
                else
                {
                    TileMapScene pScene = new TileMapScene();
                    PhoneApplicationService.Current.State["TileScene"] = pScene;
                }
            }
    
        }
    }

    设定3秒后返回TileMapScene,满血复活,哈哈。

    那么在忍者死亡的if语句最后添加:

                            GameOverScene scene = new GameOverScene();
                            CCDirector.sharedDirector().replaceScene(scene);

    到这里,我们的战斗场景就算是设计完成了。

    然后修改AppDelegate的Launch为:

                TileMapScene pScene = new TileMapScene();
            PhoneApplicationService.Current.State["TileScene"] = pScene;
    //BattleScene pScene = new BattleScene(); //run pDirector.runWithScene(pScene);

    并且在TileMapLayer里面的ccTouchEnded的最后添加:

                    Random random = new Random();
                    if (random.Next() % 7 == 0)
                    {
                        BattleScene scene = new BattleScene();
                        CCDirector.sharedDirector().replaceScene(scene);
                    }

    设定有七分之一的概率遇敌。

    这样,到这里,我们所有的工作就算是做完了,现在,我们已经拥有了一个不错的游戏,可以吃食物,并且有踩地雷式的回合制战斗。

    到这里,我们的这个《<cocos2d-x for wp7>使用cocos2d-x制作基于Tile地图的游戏》系列教程就结束了。

    示例代码下载:http://dl.dbank.com/c0f7l0f025

    何去何从:

    • 把战斗数据重构出来,并且保存到全局变量以容易调用。
    • 添加更多的怪物,更多的战斗方式。更多的武器。
    • 添加逃跑功能。
    • 。。。。。
    专注移动开发。本博客教程采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可
  • 相关阅读:
    网址收藏
    Linux创建swap文件
    vim命令大全
    char * 和字符数组
    JSR 203终于要出来啦
    对象关系技术的探讨
    最近编码更流畅了
    孤独终止的地方,就是广场开始的地方......
    不要奢望.NET能够跨平台
    实现了HTTP多线程下载
  • 原文地址:https://www.cnblogs.com/fengyun1989/p/2484684.html
Copyright © 2020-2023  润新知