(七) 移动ai 移动方向相关逻辑重构 *

每个需要移动的对象都有一个controller,controller通过心跳控制移动ai更新位置.

 

移动行为分类:

(1) 垂直向上/下移动,比如打飞机游戏的子弹移动路径.

(2)朝着固定点攻击,比如打飞机游戏吃到技能时,会攻击距离玩家最近的敌人攻击.

(3)子弹需要尾随敌人,比如导弹.

 

基于上面三种分类,controller 在onStart 和onUpdate 中需要传递两个参数,目标点和移动目标的transform.

对于分类(1), 移动ai只需要判断自己的类型,是敌人、技能、障碍物 就沿着屏幕垂直向下移动.

对于分类(2), 只需要在ai的onStart里面告诉ai,初始目标点和子弹出生点,子弹每次心跳都会沿着同一个方向移动.

对于分类(3),controller心跳不需要告诉ai目标点,只需要传递移动目标的transform,移动ai在心跳里面自己计算移动朝向.

 

具体代码:

 

以子弹移动为例子:

(1)controller

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/** controller 设计规则
 *  ctr的 onUpdate 接口负责更新ai心跳, 由BattleDatas脚本驱动.
 *  所有ctr都遵从这个规则,包括玩家角色、敌人、技能、障碍物.
 * 
 * 
 */
public class CtrBase : MonoBehaviour 
{

    //内部属性
    //--------------------------------------------------------------------------

    // 战斗管理器
    [SerializeFieldprotected BattleManager battle;
    // 对象回收池
    [SerializeFieldprotected BattlePool pool;
    // 准备完毕标记
    [SerializeFieldprotected bool isStart = false;
    // 初始化时的攻击位置
    [SerializeFieldprotected Vector3 orilShootPos;     
    // 初始化时的攻击位置
    [SerializeFieldprotected Transform currentShootTrans;     
    // 移动脚本 todo:除了玩家,其他对象需要该组件,所有放在基础类里面
    [SerializeFieldprotected MoveBaseAI moveAI;               

    //内部方法
    //--------------------------------------------------------------------------


    /** 根据移动ai类型,获取目标点
     * 
     */
    protected Vector3 GetUpdateTargetPos()
    {
        Vector3 pos = Vector3.zero;
        var t = this.MovingType;
        switch (t)
        {
            case MoveType.None:
                break;

            case MoveType.Player:
                break;

            case MoveType.UpOrDown:
                break;

            case MoveType.StraightLine:
                pos = this.orilShootPos;

                break;
        }

        return Vector3.zero;
    }

    /** 初始化时,根据子弹移动规律,计算出初始     攻击目标点、攻击目标trans
     *  
     */
    protected void InitTargetPos()
    {
        var t = this.MovingType;
        switch (t)
        {
            case MoveType.None:
                break;

            case MoveType.Player:
                break;

            case MoveType.UpOrDown:
                break;

            case MoveType.StraightLine:
                
                var targ = battle.battleDatas.GetNearestTarget(this);

                Debug.Log("targ: " + targ);

                if(targ != null){
                    
                    Debug.Log("targ name : " + targ.gameObject.name);

                    this.orilShootPos = targ.transform.position;
                    this.currentShootTrans = targ.transform;
                }

                break;
        }
    }



    //override方法 
    //--------------------------------------------------------------------------

    /** 碰撞检测
     * 
     */
    protected virtual void CollisionDetection() { }

    /**
     * 
     */
    public virtual void OnStart()
    {
        battle = GameMgr.instanse.BattleMgr();
        pool = GameMgr.instanse.BattlePool();

        InitTargetPos();

        isStart = true;
    }

    /** 每帧更新
    *  BattleDatas 驱动此心跳
    *  
    *  子弹移动
    *  碰撞检测逻辑
    * 
    */
    public virtual void OnUpdate(float deltaTime)
    {
        if (isStart == falsereturn;

        this.CollisionDetection();

        if(this.moveAI != null)
        {
            Vector3 targetPos = this.GetUpdateTargetPos();
            Transform targetTrans = this.currentShootTrans;
            object[] args = { targetPostargetTrans };

            this.moveAI.OnUpdate(deltaTimeargs);
        }
    }



    //公开接口
    //--------------------------------------------------------------------------

    // controller创建时,动态赋值
    //--------------------------------------------------------------------------
    /** ai移动类型
     * 
     *  用来计算移动路径
     * 
     */
    [SerializeFieldprivate MoveType moveType = MoveType.None;
    public MoveType MovingType
    {
        set { this.moveType = value; }
        get { return this.moveType; }
    }

    /** 角色是否是敌人
     * 
     */
    [SerializeFieldprivate bool isEnemy = false;
    public bool IsEnemy
    {
        set { this.isEnemy = value; }
        get { return this.isEnemy; }
    }




    // 来自配置表
    //--------------------------------------------------------------------------
    /**  角色默认移动方向
     * 
     */
    [SerializeFieldprivate DefaultMoveDirection defaultMoveDirection;
    public DefaultMoveDirection DefaultMoveDirection
    {
        set { this.defaultMoveDirection = value; }
        get { return this.defaultMoveDirection; }
    }

    /** 重复移动
     * 
     *  敌人、障碍物 出底线了,需要矫正位置后重新出现
     *  子弹、技能不需要
     * 
     */
    [SerializeFieldprivate bool repeatMove;
    public bool IsRepeatMove
    {
        set { this.repeatMove = value; }
        get { return this.repeatMove; }
    }

    /** obj类型
     *  用来计算移动路径
     * 
     */
    [SerializeFieldprivate RoleType roleType;
    public RoleType RoleType
    {
        set { this.roleType = value; }
        get { return this.roleType; }
    }


    //
    //--------------------------------------------------------------------------

}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/** 子弹控制器
 *  更新子弹移动ai,碰撞检测
 * 
 *  子弹攻击分三类:
 *  (1) 垂直方向无脑攻击(流弹型);
 *  (2) 往某个点攻击;
 *  (3) 往某个obj攻击,obj会移动(响尾蛇导弹型);
 * 
 */

public class BulletCtrBase : CtrBase
{
    //内部属性
    //--------------------------------------------------------------------------

    [SerializeFieldprotected BulletConfig bulletConf;         // 子弹配置

  
    //内部方法
    //--------------------------------------------------------------------------
 
    /** 碰撞检测
    *   子弹ctr 有个属性IsEnemy,标记子弹是玩家还是敌人发出的.
    *   如果是玩家发出的,子弹会检测所有敌人.
    *   如果是敌人发出的,子弹会检测所有玩家.
    * 
    * 
    */
    protected override void CollisionDetection()
    {
        if (this.IsEnemy)
        {
            // 检测玩家
            var player = battle.battleDatas.NearestPlayer(transform);

            if (player == null)
            {
                Debug.LogError("没有检测到玩家");

                return;
            }
            bool hitted = GlobalFunction.InFieldOfVision(player.transform.positiontransform.position);
            if (hitted)
            {
                Debug.LogError("击中玩家 " + player.gameObject.name);

                battle.battleDatas.CutHp(player10);

                if (player.GetData().Death())
                {
                    Debug.Log("玩家 " + player.gameObject.name + " 死亡!");

                    battle.battleDatas.RoleDeath(player);
                }
            }
        }
        else
        {
            // 检测敌人
            var enemy = battle.battleDatas.NearestEnemy(transform);

            if (enemy == null)
            {
                //Debug.LogError("没有检测到敌人");

                return;
            }
            bool hitted = GlobalFunction.InFieldOfVision(enemy.transform.positiontransform.position);
            if (hitted)
            {
                Debug.LogError("击中敌人 " + enemy.gameObject.name);

                battle.battleDatas.CutHp(enemythis.bulletConf.GetInt(this.bulletConf.attack));

                if (enemy.GetData().Death())
                {
                    Debug.Log("敌人 " + enemy.gameObject.name + " 死亡!");

                    battle.battleDatas.EnemyDeath(enemy);
                }
            }
        }
    }


    //override方法 
    //--------------------------------------------------------------------------

    /** BattleDatas 创建时,完成初始化(OnStart)
     * 
     * 
     * 
     */
    public override void OnStart()
    {
        object[] args = { this.transformthis.bulletConf.moveSpeedthis};
        this.moveAI.OnStart(args);

        base.OnStart();
    }


    //公开接口
    //--------------------------------------------------------------------------

    /** 移动ai
     * 
     */
    public void SetMoveAI(MoveBaseAI move)
    {
        this.moveAI = move;
    }

    /** 子弹配置
     * 
     */
    public void SetConfig(BulletConfig conf)
    {
        this.bulletConf = conf;
    }

    /** 角色id
     * 
     */
    public string Id()
    {
        return this.bulletConf.id;
    }

}
 

 

(2) 通用的移动ai(子弹、敌人、技能对象、障碍物都适用)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//TODO:重构

/** 更新子弹移动ai
 *   
 *  controller 给移动ai必须的数据, 不同行为的移动ai负责计算,并执行具体移动行为.
 * 
 * 
 */

public class MoveBaseAI : BaseAI
{
    //内部属性
    //--------------------------------------------------------------------------

    // 移动角色的controller
    [SerializeFieldprotected CtrBase ctr;
    // 移动角色 transform
    [SerializeFieldprotected Transform roleTrans;
    // 移动速度
    [SerializeFieldprotected float moveSpeed;
    [SerializeFieldprotected Vector3 moveDir;



    //内部方法
    //--------------------------------------------------------------------------
    /** 移动朝向
     * 
     * 
     */
    protected virtual Vector3 GetDir(params object[] args)
    {
        if (args == null || args.Length < 1)
        {
            Debug.LogError("移动的目标点必须传!");
         
        }

        return Vector3.zero;
    }


    //override方法 
    //--------------------------------------------------------------------------


    /** 初始化ai 
      *  
      * 移动角色 transform
      * 移动速度
      * 移动角色 controller
      * 
      */
    public override void OnStart(params object[] args)
    {

        if (args == null || args.Length < 3)
        {
            Debug.LogError("args 必须包含 移动transform 、移动速度、移动方向、目标transform、角色controller");

            return;
        }

        this.roleTrans = (Transform)args[0];
        this.moveSpeed = float.Parse(args[1].ToString());
        this.ctr = (CtrBase)args[2];

        base.OnStart(args);
    }

    /** 每帧更新(对应obj的controller)
     *
     *  参数1: deltaTime
     *  参数2: args
     * 
     */
    public virtual void OnUpdate(float deltaTimeparams object[] args)
    {
        //Debug.Log("single forward deltaTime : " + deltaTime);
        //Debug.Log("single forward isStart : " + this.isStart);
        //Debug.Log("single forward roleTrans : " + roleTrans.name);

        base.OnUpdate(deltaTime);

        if (this.roleTrans == null)
        {
            //被消灭
            return;
        }

        // 出底线了
        if (battle.IsOutSide(this.roleTrans.position))
        {
            if (this.ctr.IsRepeatMove)
            {
                // 强制矫正
                this.roleTrans.position = battle.CorrectOutSide(this.roleTrans.position);
            }
            else
            {
                // 回收
                battle.battleDatas.ObjDeath(this.ctr);
            }
        } else {


            this.moveDir = this.GetDir(args);
            this.roleTrans.position += this.moveDir * deltaTime * this.moveSpeed
        }
    }

    //公开接口
    //--------------------------------------------------------------------------

}
 

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


/** 向屏幕上方/下方移动
 * 
 */
public class SingleForwardAI : MoveBaseAI
{

    //内部属性
    //--------------------------------------------------------------------------




    //内部方法
    //--------------------------------------------------------------------------
 

    /** 移动朝向
     *   
     *  只处理默认移动方向
     * 
     */
    protected override Vector3 GetDir(params object[] args)
    {
        base.GetDir(args);

        Vector3 dir;
        if(this.ctr.DefaultMoveDirection == DefaultMoveDirection.VerticalDown)
        {
            dir = Vector3.down;
        }
        else
        {
            dir = Vector3.up;
        }
      
        return dir;
    }



    //override方法 
    //--------------------------------------------------------------------------


    //公开接口
    //--------------------------------------------------------------------------


}
 

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/** 向某个固定点移动
 * 
 */
public class StraightForwardAI : MoveBaseAI
{

    //内部属性
    //--------------------------------------------------------------------------
    [SerializeFieldprotected Vector3 orilPos;


    //内部方法
    //--------------------------------------------------------------------------

    /** 移动朝向
     *   
     *  处理心跳传过来的目标点与子弹出生点之间的向量
     * 
     */
    protected override Vector3 GetDir(params object[] args)
    {
        var target = (Vector3)args[0];
        var dir = (target - this.orilPos).normalized;

        return dir;
    }



    //override方法 
    //--------------------------------------------------------------------------

    /** 初始化ai 
    *  
    * 移动角色 transform
    * 移动速度
    * 移动角色 controller
    * 
    */
    public override void OnStart(params object[] args)
    {
        base.OnStart(args);

        this.orilPos = this.roleTrans.position;
    }

    //公开接口
    //--------------------------------------------------------------------------


}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值