ActionScript
TypeScript
JavaScript

五、子弹及飞机之间碰撞逻辑

发布时间:2016-05-14

 

上节实现了敌机飞行,本节讲实现射击,碰撞,击毁等效果。

先增加了子弹资源,并重新生成图集。

图片1.png 

为了实现射击功能,我们给角色类Role.ts增加射击相关属性:

    //射击类型
    public shootType: number = 0;
    //射击间隔
    public shootInterval: number = 500;
    //下次射击时间
    public shootTime: number = Laya.Browser.now() + 2000;
    //当前动作
    public action: string;
    //是否是子弹
    public isBullet:boolean = false;


同时增加监听动画播放完成事件,处理爆炸特效和被击特效及飞行特效切换逻辑。

         //增加动画播放完成监听
         this.body.on("complete", this, this.onPlayComplete);
 
 
        onPlayComplete(): void {
        //如果是击毁动画,则隐藏对象
        if (this.action === "down") {
            //停止动画播放
            this.body.stop();
            //隐藏显示,通过此标记,在下帧进行回收
            this.visible = false;            
        }else if(this.action==="hit"){
            //如果是被击动画播放完毕,则接着播放飞行动画
            this.playAction("fly");
        }
    }


完整代码如下:

/**
 * 角色类
 */
class Role extends Laya.Sprite {
    //是否缓存了动画
    private static cached: boolean = false;
    //定义飞机的身体
    private body: Laya.Animation;
    //定义飞机类型
    public type: string;
    //阵营,0:我方,1:敌方
    public camp: number;
    //血量
    public hp: number;
    //飞行速度
    public speed: number;
    //攻击半径
    public hitRadius: number = 0;
    //射击类型
    public shootType: number = 0;
    //射击间隔
    public shootInterval: number = 500;
    //下次射击时间
    public shootTime: number = Laya.Browser.now() + 2000;
    //当前动作
    public action: string;
    //是否是子弹
    public isBullet: boolean = false;
 
    constructor() {
        super();
    }
 
    public init(type: string, camp: number, hp: number, speed: number, hitRadius: number): void {
        //初始化角色属性
        this.type = type;
        this.camp = camp;
        this.hp = hp;
        this.speed = speed;
        this.hitRadius = hitRadius;
 
        //显示碰撞区域
        //this.graphics.clear();
        //this.graphics.drawCircle(0, 0, hitRadius, null, "#ff0000")
 
        //缓存公用动画模板,减少对象创建开销
        if (!Role.cached) {
            Role.cached = true;
            //缓存hero_fly动画
            Laya.Animation.createFrames(["war/hero_fly1.png", "war/hero_fly2.png"], "hero_fly");
            //缓存hero_down动画
            Laya.Animation.createFrames(["war/hero_down1.png", "war/hero_down2.png", "war/hero_down3.png", "war/hero_down4.png"], "hero_down");
 
            //缓存enemy1_fly动画
            Laya.Animation.createFrames(["war/enemy1_fly1.png"], "enemy1_fly");
            //缓存enemy1_down动画
            Laya.Animation.createFrames(["war/enemy1_down1.png", "war/enemy1_down2.png", "war/enemy1_down3.png", "war/enemy1_down4.png"], "enemy1_down");
 
            //缓存enemy2_fly动画
            Laya.Animation.createFrames(["war/enemy2_fly1.png"], "enemy2_fly");
            //缓存enemy2_down动画
            Laya.Animation.createFrames(["war/enemy2_down1.png", "war/enemy2_down2.png", "war/enemy2_down3.png", "war/enemy2_down4.png"], "enemy2_down");
            //缓存enemy2_hit动画
            Laya.Animation.createFrames(["war/enemy2_hit.png"], "enemy2_hit");
 
            //缓存enemy3_fly动画
            Laya.Animation.createFrames(["war/enemy3_fly1.png", "war/enemy3_fly2.png"], "enemy3_fly");
            //缓存enemy3_down动画
            Laya.Animation.createFrames(["war/enemy3_down1.png", "war/enemy3_down2.png", "war/enemy3_down3.png", "war/enemy3_down4.png", "war/enemy3_down5.png", "war/enemy3_down6.png"], "enemy3_down");
            //缓存enemy3_hit动画
            Laya.Animation.createFrames(["war/enemy3_hit.png"], "enemy3_hit");
 
            //缓存子弹动画
            Laya.Animation.createFrames(["war/bullet1.png"], "bullet1_fly");
        }
 
        if (!this.body) {
            //创建一个动画作为飞机的身体
            this.body = new Laya.Animation();
            //动画播放时间间隔
            this.body.interval = 50;
            //把机体添加到容器内
            this.addChild(this.body);
 
            //增加动画播放完成监听
            this.body.on("complete", this, this.onPlayComplete);
        }
        //播放飞行动画
        this.playAction("fly");
    }
 
    onPlayComplete(): void {
        //如果是击毁动画,则隐藏对象
        if (this.action === "down") {
            //停止动画播放
            this.body.stop();
            //隐藏显示,通过此标记,在下帧进行回收
            this.visible = false;
        } else if (this.action === "hit") {
            //如果是被击动画播放完毕,则接着播放飞行动画
            this.playAction("fly");
        }
    }
 
    playAction(action: string): void {
        //纪录当前播放动画类型
        this.action = action;
        //根据类型播放动画
        this.body.play(0, true, this.type + "_" + action);
        //获取动画大小区域
        var bound: Laya.Rectangle = this.body.getBounds();
        //设置机身剧中
        this.body.pos(-bound.width / 2, -bound.height / 2);
    }
}


Game.ts类,增加发射子弹逻辑:

//处理发射子弹逻辑
            if (role.shootType > 0) {
                //获取当前时间
                var time: number = Laya.Browser.now();
                //如果当前时间大于下次射击时间
                if (time > role.shootTime) {
                    //更新下次射击时间
                    role.shootTime = time + role.shootInterval;
                    //从对象池里面创建一个子弹
                    var bullet: Role = Laya.Pool.getItemByClass("role", Role);
                    //初始化子弹信息
                    bullet.init("bullet1", role.camp, 1, -5, 1);
                    //设置角色类型为子弹类型
                    bullet.isBullet = true;
                    //设置子弹发射初始化位置
                    bullet.pos(role.x, role.y - role.hitRadius - 10);
                    //添加到舞台上
                    Laya.stage.addChild(bullet);
                }
            }


增加子弹及飞机之间碰撞逻辑。

//检测碰撞,原理:获取角色对象,一一对比之间的位置,判断是否击中
        var n: number = Laya.stage.numChildren;
        for (var i: number = Laya.stage.numChildren - 1; i > 0; i--) {
            //获取角色对象1
            var role1: Role = Laya.stage.getChildAt(i) as Role;
            //如果角色已经死亡,则忽略
           if (role1.hp1){
           
            }
            }
           
                //如果角色已经死亡,则忽略
                if (!role1.visible) continue;
                //获取角色对象2
                var role2: Role = Laya.stage.getChildAt(j) as Role;
                //如果角色未死亡,并且阵营不同,才进行碰撞
                if (role2.hp > 0 && role1.camp != role2.camp) {
                    //计算碰撞区域
                    var hitRadius: number = role1.hitRadius + role2.hitRadius;
                    //根据距离判断是否碰撞
                    if (Math.abs(role1.x - role2.x) < hitRadius && Math.abs(role1.y - role2.y) < hitRadius) {
                        //碰撞后掉血
                        this.lostHp(role1, 1);
                        this.lostHp(role2, 1);
                    }
                }
            }
        }


角色死亡后,则停止主循环:

//如果主角死亡,则停止游戏循环
        if (this.hero.hp < 1) {
            Laya.timer.clear(this, this.onLoop);
        }


掉血逻辑处理:

lostHp(role: Role, lostHp: number): void {
        //减血
        role.hp -= lostHp;
        if (role.hp > 0) {
            //如果未死亡,则播放受击动画
            role.playAction("hit");
        } else {
            //如果死亡,则播放爆炸动画
            if (role.isBullet) {
                //如果是子弹,则直接隐藏,下次回收
                role.visible = false;
            } else {
                role.playAction("down");
            }
        }
    }


完整代码如下:

/**
* Game
*/
class Game {
    //定义英雄(主战斗机)
    private hero: Role
 
    constructor() {
        //初始化引擎,设置游戏设计宽高
        Laya.init(480, 852);
 
        //创建循环滚动的背景
        var bg: BackGround = new BackGround();
        //把背景添加到舞台上显示出来
        Laya.stage.addChild(bg);
 
        //加载图集资源
        Laya.loader.load("res/atlas/war.json", Laya.Handler.create(this, this.onLoaded), null, Laya.Loader.ATLAS);
 
        //显示FPS
        Laya.Stat.show();
    }
 
    onLoaded() {
        //创建一个主角(主战斗机)
        this.hero = new Role();
        //初始化角色
        this.hero.init("hero", 0, 5, 0, 30);
        //设置角色位置
        this.hero.pos(240, 700);
        //设置射击类型
        this.hero.shootType = 1;
        //把主角添加到舞台上
        Laya.stage.addChild(this.hero);
 
        //监听舞台的鼠标移动事件
        Laya.stage.on("mousemove", this, this.onMouseMove);
 
        //创建主循环
        Laya.timer.frameLoop(1, this, this.onLoop);
    }
 
    onLoop(): void {
        //遍历所有飞机,更改飞机状态
        for (var i: number = Laya.stage.numChildren - 1; i > 0; i--) {
            var role: Role = Laya.stage.getChildAt(i) as Role;
            if (role && role.speed) {
                //根据飞机速度更改位置
                role.y += role.speed;
 
                //如果敌人移动到显示区域以外,则移除
                if (role.y > 1000 || !role.visible || (role.isBullet && role.y  0) {
                //获取当前时间
                var time: number = Laya.Browser.now();
                //如果当前时间大于下次射击时间
                if (time > role.shootTime) {
                    //更新下次射击时间
                    role.shootTime = time + role.shootInterval;
                    //从对象池里面创建一个子弹
                    var bullet: Role = Laya.Pool.getItemByClass("role", Role);
                    //初始化子弹信息
                    bullet.init("bullet1", role.camp, 1, -5, 1);
                    //设置角色类型为子弹类型
                    bullet.isBullet = true;
                    //设置子弹发射初始化位置
                    bullet.pos(role.x, role.y - role.hitRadius - 10);
                    //添加到舞台上
                    Laya.stage.addChild(bullet);
                }
            }
        }
        //检测碰撞,原理:获取角色对象,一一对比之间的位置,判断是否击中
        var n: number = Laya.stage.numChildren;
        for (var i: number = Laya.stage.numChildren - 1; i > 0; i--) {
            //获取角色对象1
            var role1: Role = Laya.stage.getChildAt(i) as Role;
            //如果角色已经死亡,则忽略
            if (role1.hp  0; j--) {
                //如果角色已经死亡,则忽略
                if (!role1.visible) continue;
                //获取角色对象2
                var role2: Role = Laya.stage.getChildAt(j) as Role;
                //如果角色未死亡,并且阵营不同,才进行碰撞
                if (role2.hp > 0 && role1.camp != role2.camp) {
                    //计算碰撞区域
                    var hitRadius: number = role1.hitRadius + role2.hitRadius;
                    //根据距离判断是否碰撞
                    if (Math.abs(role1.x - role2.x) < hitRadius && Math.abs(role1.y - role2.y) < hitRadius) {
                        //碰撞后掉血
                        this.lostHp(role1, 1);
                        this.lostHp(role2, 1);
                    }
                }
            }
        }
        //如果主角死亡,则停止游戏循环
        if (this.hero.hp  0) {
            //如果未死亡,则播放受击动画
            role.playAction("hit");
        } else {
            //如果死亡,则播放爆炸动画
            if (role.isBullet) {
                //如果是子弹,则直接隐藏,下次回收
                role.visible = false;
            } else {
                role.playAction("down");
            }
        }
    }
 
    onMouseMove(e: Laya.Event): void {
        //始终保持影响和鼠标位置一致
        this.hero.pos(Laya.stage.mouseX, Laya.stage.mouseY);
    }
 
    //敌机血量表
    private hps: Array = [1, 2, 10];
    //敌机速度表
    private speeds: Array = [3, 2, 1];
    //敌机被击半径表
    private radius: Array = [18, 33, 80];
 
    createEnemy(num: number): void {
        for (var i: number = 0; i < num; i++) {
            //随机出现敌人
            var r: number = Math.random();
 
            //根据随机数,随机敌人   
            var type: number = r < 0.7 ? 0 : r < 0.95 ? 1 : 2;
 
            //创建敌人,从对象池创建
            var enemy: Role = Laya.Pool.getItemByClass("role", Role);
            //初始化角色
            enemy.init("enemy" + (type + 1), 1, this.hps[type], this.speeds[type], this.radius[type]);
            //随机位置
            enemy.pos(Math.random() * 400 + 40, -Math.random() * 200 - 100);
            //添加到舞台上
            Laya.stage.addChild(enemy);
        }
    }
}
 
//启动游戏
new Game();


编译后看到如下效果:

图片1.png 

 

本节课程源码下载