• 从零开始学AS3游戏开发【四】 敌人来袭!保护基地!


    注:本系列教程每周一篇,旨在引导刚刚接触FLASH的新手通过实例进行游戏开发的学习。在过程中逐步说明涉及到的类及对应的使用方法。从一个光秃秃的方块开始,根据不同的控制方式、玩法产生不同的分支,最终完善成一个个可玩的游戏。希望对各位入门的朋友有所帮助!在教程涉及的各种处理方法,可能不够完善,也希望各位高手指正:)

    转载请注名来源于天地会

    第四篇 敌人来袭!保护基地!

    上一篇的教程中。我们的主角可以发射子弹攻击敌人了。但是敌人还是傻傻的在屏幕内游荡。这显然没有什么乐趣。在本篇教程中。我们将让敌人可以发射子弹进行攻击。另外,将增加新的游戏单元——基地。并增加游戏规则。如果基地被敌人摧毁。则游戏结束。

    还是先来计划一下要实现的功能:

    1。增加基地对象,设置基地的hp为5,被敌人攻击5次后,基地会被毁灭
    2。自己发射的子弹对基地无效
    3。更改敌人的控制器,让它每10秒自动发射1颗子弹
    4。更改敌人的控制器,让它尽量向基地移动。
    5。当基地被敌人攻破时,提示游戏结束

    做好计划后,开始动手吧!
    1。增加基地对象。

    和绘制敌人的方块和自己的方块一样,我们绘制一个一样大小的方块,里面画一个五角星。导出名设置为Skin_base
    1.jpg
    (本篇教程是在家里编写的。使用的是英文版的cs3,不过有了前面的几篇,大家应该可以看的懂的,位置都一样)

    素材画好了,再建一个基地类,集成自FaceObject.为什么集成自FaseObject?第一,基地是有皮肤的。第二,基地是有hp的,完全符合FaceObject的要求。可能你会问,那控制器怎么办?难道还要给基地也写一个控制器?如果你只是想让基地傻傻的站再那里被攻击,当然可以从gameObject继承,但是,我们想有趣一些。再接下来的教程里,我们可能让基地自动攻击接近的敌人(塔防基础),因此,现在就暂时随便给它个basicController就可以了:)

    先来看一下基地的类

    1. package D5Power.Objects
    2. {
    3.         import D5Power.Controller.basicController;
    4.         
    5.         import flash.display.Sprite;
    6.         
    7.         public class Base extends FaceObject
    8.         {
    9.                 public function Base(ctrl:basicController, face:Sprite)
    10.                 {
    11.                         super(ctrl, face);
    12.                 }
    13.         }
    14. }
    复制代码

    是的,我们除了继承,什么都没做。再来看一下生成基地的代码:

    1. // 生成基地
    2.                         var base:Base = new Base(new basicController(),new Skin_base());
    3.                         base.x = obj.x+obj.width;
    4.                         base.y = obj.y;
    5.                         scene.addObject(base);
    复制代码

    我们给了基地一个最基础的控制器(其实什么也没控制),然后给了它基地的皮肤,最后,我们把基地的坐标调整到了主角的右边。并把它添加到了游戏场景中。现在,测试一下影片,并移动主角向基地开炮吧!什么?你自己吧基地打坏了?⋯⋯

    2。自己的子弹不伤害基地。

    自己把自己的基地打坏实在是太不爽了(当然,有的游戏也是这样设计的,我们小时候玩的坦克大战就是这样的)。我们还是不要打坏自己基地的好。
    分析一下,其实现在场景里是分两个阵营的(联盟和部落?),所以,我们可以根据阵营来判断目标是否可以被攻击。也就是说,游戏对象应该有阵营属性。我们可以考虑,如果场景里的一切对象都可以被摧毁,应该是一件很爽的事情,那么,很明显,我们把阵营这个属性付给gameObject,于是,所有对象都可以有阵营了。说做就做,给gameObject增加以下代码:

    1.                 /**
    2.                  * 所属阵营
    3.                  */ 
    4.                 public var part:uint=0;
    复制代码

    然后,把子弹(BulletObject)的判断攻击程序修改一下,把:

    1. if (obj.hitTestPoint(x, y, true) && obj!=_shooter && obj!=this)
    复制代码

    修改为

    1. if (obj.hitTestPoint(x, y, true) && obj.part!=_shooter.part && obj!=this)
    复制代码

    也就是说,只有阵营不一样的时候才会检测攻击。

    最后,在生成各对象的时候,给他们一个阵营id,修改Main.as

    1. ...
    2.                         var obj:Player = new Player(ctrl, new Skin1());
    3.                         obj.x = 200;
    4.                         obj.y = 200;
    5.                         obj.part = 1;//增加了这一句
    6. ...
    7.                                 var monster:Monster = new Monster(ctrl2, new Skin2());
    8.                                 monster.x = int(Math.random() * 500);
    9.                                 monster.y = int(Math.random() * 300);
    10.                                 monster.part=2;// 还有这一句
    11. ...
    12.                         var base:Base = new Base(new basicController(),new Skin_base());
    13.                         base.x = obj.x+obj.width;
    14.                         base.y = obj.y;
    15.                         base.part = obj.part;//基地的阵营和主角的是一致的
    复制代码

    现在再测试一下程序,恩,子弹从基地穿过了,但是照样可以打到敌人。舒坦了。。

    3.敌人10秒发射一颗子弹

    首先,当然是为敌人实现IShoot接口,这部分和Player的射击是完全一样的,直接上代码:

    1. ...
    2. public class Monster extends FaceObject implements IShoot
    3. ...
    4.                 /**
    5.                  * 发射子弹
    6.                  */
    7.                 public function Shoot():void
    8.                 {
    9.                         var b:BulletObject = new BulletObject(this);
    10.                         b.x = x+width/2;
    11.                         b.y = y+height/2;
    12.                         Global.scene.addObject(b);
    13.                 }
    复制代码

    10秒发射一次子弹?这个的实现思路可以参考我们第一篇教程所讲的定时渲染。修改MonsterController代码,增加以下两个属性:

    1.                 /**
    2.                  * 最后一次射击时间
    3.                  */ 
    4.                 protected var lastShoot:Date;
    5.                 /**
    6.                  * 射击间隔,单位秒
    7.                  */ 
    8.                 protected var shootSpeed:uint = 10;
    复制代码

    在构造函数中,把射击时间进行初始化:

    1.                 public function MonsterControler() 
    2.                 {
    3.                         lastShoot = new Date();
    4.                         super();
    5.                 }
    复制代码

    在控制器的自动运行函数AutoRun中,增加自动射击部分的代码:

    1.                         // 自动射击
    2.                         var now:Date = new Date();
    3.                         if(now.time-lastShoot.time>shootSpeed*1000)        // 当距离上一次射击的时间间隔超过规定时间时运行
    4.                         {
    5.                                 me.Shoot();        // 射击!
    6.                                 lastShoot = now;// 更新最后一次射击时间
    7.                         }
    复制代码

    这个实现的原理其实和定期渲染是一样的。编译测试,可以发现敌人的坦克在行进的过程中会时不时的发射子弹了!我们继续进行下一步吧!


    4。聪明的敌人,向基地开炮!

    想我们现在这样,敌人基本上永远都不会击中基地的。我们现在让敌人聪明一些,如果在巡航的过程中,与基地在同一直线上,那么有60%的几率向基地开炮,什么?你想问为什么不是100%。恩。。你觉得如果这样,玩家还有的玩么。。。

    修改AutoRun函数,增加一下代码(在“if (!me.nextCanMove) changeDir()”一句以后增加)

    1. if(Global.base!=null && (Math.abs(me.x-Global.base.x)<10 || Math.abs(me.y-Global.base.y)<10))
    2.                         {
    3.                                 if(Math.random()>0.6) return;
    4.                                 if(now.time - lastShoot.time<2000) return;
    5.                                 var tempDir:uint = me.direction; // 临时记录方向
    6.                                 // 转向基地
    7.                                 var r:Number = Math.atan2(me.y-Global.base.y,me.x-Global.base.x);
    8.                                 var a:int = r/Math.PI*180;
    9.                                 
    10.                                 if(a>80 && a<100)
    11.                                 {
    12.                                         me.direction = ActionObject.UP;
    13.                                 }else if(a>170 && a<190){
    14.                                         me.direction = ActionObject.RIGHT;
    15.                                 }else if(a>260 || a<-80){
    16.                                         me.direction = ActionObject.DOWN;
    17.                                 }else{
    18.                                         me.direction = ActionObject.LEFT;
    19.                                 }
    20.                                 me.Shoot();
    21.                                 lastShoot = now;
    22.                                 me.direction = tempDir;
    23.                         }
    复制代码

    来分析一下代码。首先,触发的条件是基地被设置了,而且敌人的坐标与基地的坐标相差不超过10。满足这个条件后,才会向基地射击。之后,取随机数,如果随机数大于0.6,则终止程序运行。其次,如果射击间隔小于2秒,也终止程序运行(Q1).接下来,把敌人当前的方向保存到tempDir中。之后计算基地与敌人的夹角。使用atan2方法返回由X 轴到(y,x) 点的角度,一定要注意,第一参数是y,第二参数是x。这个地方连flash builder 4的提示都是错的!。现在我们以基地为原点计算夹角,自然就是在敌人坐标的基础上减去基地坐标。获得弧度值后,再转换为角度值(这段如果看不懂,可能需要取翻一下高中数学的课本了,呵呵)。之后,根据角度值来判断基地所在的方位,并把敌人的方向转向基地。射击后,更新射击时间,并恢复敌人的方向(Q2)

    关于Q1:
    为什么要设置2秒的间隔。首先,敌人射击是10秒自动的,当到了10秒的时候,就会射击,然后自动更新射击时间。如果向基地射击的时间也沿用这个判断,那可能永远也不会向基地射击。而如果不加入时间判断,那可能在敌人经过基地附近的一瞬间会发射n颗子弹。

    关于Q2:
    为什么要改回敌人的方向?如果不改回,敌人将向基地方向运行,并定期发射子弹。这样难度大了些。当然,如果你喜欢,可以不改回来:)

    现在运行程序,敌人会在与基地在同一直线的时候转身给基地一炮。不用多久,基地就被干掉了。

    5。覆盖实现基地的Hurt代码:

    1.                 override public function Hurt(val:uint):void
    2.                 {
    3.                         super.Hurt(val);
    4.                         if(_hp<=0)
    5.                         {
    6.                                 Global.base=null;
    7.                                 trace('基地被摧毁!');
    8.                         }
    9.                 }
    复制代码

    当基地的hp小于0的时候,把Global.base设置为null,这样敌人就不会再对基地射击了(第一条件不满足),最后,输出信息“基地被摧毁”

    现在,我们这个小程序已经有一点意思了。至少你放再那里不管,你的基地一会就会被摧毁了。各位兄弟可以发挥自己的想象,如果除了基地外,敌人也会自动攻击我们的主角呢?我想,看完这篇也不难实现了吧!

    不过,现在的程序实在是太难看了,也不知道角色的方向到底朝向哪里。另外,好像角色间可以穿过!在下一篇教程里,我们将修改现在使用的皮肤,并引入碰撞检测。

    以下是本篇教程的最终效果和源码:

     main.swf (3.9 KB) 
     Teach.zip (22.09 KB)

  • 相关阅读:
    淘宝首页性能优化实践
    Ubuntu16.04下搭建Go语言环境
    ubuntu下nvm,node以及npm的安装与使用
    idea 新建/导入的xml 报文头报错 URI is not registered (Settings | Languages & Frameworks | Schemas and DTDs)
    Spring Boot 请求返回字符串中文乱码
    Java 从FTP下载.上传文件
    Spring boot 定时器
    spring boot集成mybatis 自动生成实体类和mapper文件、Dao层
    Registered driver with driverClassName=oracle.jdbc.driver.OracleDriver was not found
    Oracle中创建序列
  • 原文地址:https://www.cnblogs.com/keng333/p/2304961.html
Copyright © 2020-2023  润新知