Unity手游制作记-制作通用基础动作管理器

本文介绍了一个用于Unity的游戏角色动作管理系统,包括攻击、防御等基本动作的管理及优先级控制。此外,还展示了如何结合自动寻路实现角色的自动攻击行为。

动作管理

用来管理单位的动作。

我将动作分别攻击、防御、奔跑、发呆、死亡这5个基本动作,一般来说给你的模型的动作也就这几项。

我们今天就写个来统一管理,并进行通用化。

Animation和Animator

Unity的话,动画就分为这二个。

一个是状态机、一个是动画帧数。

接着我们来对这二个进行操作即可。

Animation这个的话,我就用Trigger来进行控制。

Animator这个的话,直接用Play即可。

using UnityEngine;
using System.Collections;

public class BehaviorOfAni : MonoBehaviour {
    Animation animation;
    Animator animator;
    public string nowAni = "";
    int nowPriority = -1; // 当前动作优先级
    float nowAniTime = 0; // 当前动作时间
    float time = 0; // 当前动作计时
    [SerializeField]
    string []attackAniName = {"Attack1", "Attack2", "Attack3"}; // 攻击名字
    [SerializeField]
    float[] attackAniTime = {1.2f, 1.5f, 1.2f}; // 攻击时间
    public int attackPriority = 3; // 攻击优先级
    [SerializeField]
    string[] defendAniName = {"Defend"}; // 防御名字
    [SerializeField]
    float[] defendAniTime = {1.0f}; // 防御时间
    public int defendPriority = 1; // 防御优先级
    [SerializeField]
    string runAniName = "Run"; // 奔跑名字
    [SerializeField]
    float runAniTime = 1.0f; // 奔跑时间
    public int runPriority = 2; // 奔跑优先级
    [SerializeField]
    string idleAniName = "Idle"; // 发呆名字
    [SerializeField]
    float idleAniTime = 1.0f; // 发呆时间
    public int idlePriority = 0; // 发呆优先级
    [SerializeField]
    string deathAniName = "Death"; // 死亡名字
    [SerializeField]
    float deathAniTime = 2.5f; // 死亡时间
    public int deathPriority = 10; // 死亡优先级
    // Use this for initialization
    [SerializeField]
    public int nowTime = 0;
    void Start () {
        animation = GetComponent<Animation>();
        animator = GetComponent<Animator>();
        if (animation == null && animator == null) {
            Debug.LogError("未指定animation和animator中的任意一个");
        }
    }

    public float beDef() {
        if (this.nowPriority < defendPriority) {
            if (this.nowAni.Equals("Defent")) {
                nowTime = 0;
            } else {
                nowTime++;
                if (nowTime >= defendAniName.Length) {
                    nowTime = 0;
                }
            }
            if (animation) {
                animation.Play(defendAniName[nowTime]);
            } else {
                animator.SetBool(defendAniName[nowTime], true);
            }
            this.nowPriority = defendPriority;
            this.nowAniTime = defendAniTime[nowTime];
            time = 0;
            nowAni = "Defend";
            return nowAniTime;
        }
        return 0;
    }

    public float beAttack() {
        if (this.nowPriority < attackPriority) {
            if (this.nowAni.Equals("Attack")) {
                nowTime = 0;
            } else {
                nowTime++;
                if (nowTime >= attackAniName.Length) {
                    nowTime = 0;
                }
            }
            if (animation) {
                animation.Play(attackAniName[nowTime]);
            } else {
                animator.SetBool(attackAniName[nowTime], true);
            }
            this.nowPriority = attackPriority;
            this.nowAniTime = attackAniTime[nowTime];
            time = 0;
            nowAni = "Attack";
            return nowAniTime;
        }
        return 0;
    }
    public float beIdle() {
        if (this.nowPriority < idlePriority) {
            if (animation) {
                animation.Play(idleAniName);
            } else {
                animator.SetBool(idleAniName, true);
            }
            this.nowPriority = idlePriority;
            this.nowAniTime = idleAniTime;
            time = 0;
            nowAni = "Idle";
            return nowAniTime;
        }
        return 0;
    }
    public float beMustIdle() {
        if (animation) {
            animation.Play(idleAniName);
        } else {
            animator.SetBool(idleAniName, true);
        }
        this.nowPriority = idlePriority;
        this.nowAniTime = idleAniTime;
        time = 0;
        nowAni = "Idle";
        return nowAniTime;
    }
    public float beRun() {
        if (this.nowPriority < runPriority) {
            if (animation) {
                animation.Play(runAniName);
            } else {
                animator.SetBool(runAniName, true);
            }
            this.nowPriority = runPriority;
            this.nowAniTime = runAniTime;
            time = 0;
            nowAni = "Run";
            return nowAniTime;
        }
        return 0;
    }
    public float beDeath() {
        if (this.nowPriority < deathPriority) {
            if (animation) {
                animation.Play(deathAniName);
            } else {
                animator.SetBool(deathAniName, true);
            }
            this.nowPriority = deathPriority;
            this.nowAniTime = deathAniTime;
            time = 0;
            nowAni = "Death";
            return nowAniTime;
        }
        return 0;
    }
    void FixedUpdate() {
        if (nowPriority != 0) {
            time += Time.fixedDeltaTime;
            if (time >= nowAniTime) {
                time = 0;
                nowPriority = -999;
                beMustIdle();
                nowAni = "";
            }
        }
    }
}

加上执行时间和优先级,即可实现动作管理,用起来也非常的方便。

测试
    void Start () {
        StartCoroutine(fun());
    }

    IEnumerator fun() {
        yield return new WaitForSeconds(1.0f);
        float time = behaviorOfAni.beAttack();
        TextControl.getIns().set3dText("正在执行攻击-" + time, behaviorOfAni.gameObject, Vector3.up * 2, time * 0.75f);
        yield return new WaitForSeconds(time * 1.25f);
        time = behaviorOfAni.beRun();
        TextControl.getIns().set3dText("正在执行跑步-" + time, behaviorOfAni.gameObject, Vector3.up * 4, time * 0.75f);
        yield return new WaitForSeconds(time * 1.25f);
        time = behaviorOfAni.beIdle();
        TextControl.getIns().set3dText("正在执行呆立-" + time, behaviorOfAni.gameObject, Vector3.up * 6, time * 0.75f);
        yield return new WaitForSeconds(time * 1.25f);
        time = behaviorOfAni.beDeath();
        TextControl.getIns().set3dText("正在执行死亡-" + time, behaviorOfAni.gameObject, Vector3.up * 8, time * 0.75f);
        yield return new WaitForSeconds(time * 1.25f);
        behaviorOfAni.gameObject.AddComponent<AutoDeath>().maxTime = time;
    }

这里写图片描述

这里写图片描述

自动寻路攻击

接着就是配套的自动寻路攻击。

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(NavMeshAgent))]
public class BehaviorOfMove : MonoBehaviour {
    NavMeshAgent agent;
    float stoppingDistance;
    // Use this for initialization
    void Start() {
        init();
        stoppingDistance = agent.stoppingDistance;
    }

    protected void init() {
        agent = GetComponent<NavMeshAgent>();
    }
    public void moveTo(Vector3 pos) {
        if (!isFinish(pos)) {
            agent.destination = pos;
        }
    }

    public bool isFinish(Vector3 pos) {
        if (Vector3.Distance(this.transform.position, pos) <= stoppingDistance) {
            return true;
        } else {
            return false;
        }
    }
}

具体的话,根据怪物的不同,我们需要继承下面这个类,进行逻辑设计。

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(BehaviorOfMove))]
public class BehaviorAutoAttack : MonoBehaviour {
    protected BehaviorOfMove move;
    protectedGameObject des; // 攻击目标
    [SerializeField, TooltipAttribute("刷新周期(次/秒)")]
    protected int defTime = 10; // 周期
    protected float time; // 记录周期
    protected float maxTime; // 记录周期

    protected virtual void Start() {
        init();
    }
    protected virtual void init() {
        move = this.GetComponent<BehaviorOfMove>();
        maxTime = 1.0f / defTime;
    }

    public void setAttack(GameObject att) {
        des = att;
    }

    protected virtual void attack() {
    }

    protected virtual void run() {
    }

    protected virtual void idle() {
    }

    protected virtual void doThing() {
        time += Time.fixedDeltaTime;
        if (time >= maxTime) {
            time = 0;
            if (des) {
                move.moveTo(des.transform.position);
                if (move.isFinish(des.transform.position)) {
                    this.transform.LookAt(des.transform.position);
                    attack();
                } else {
                    run();
                }
            }
        }
    }
}

以这个弓箭手为例(一般的远程也通用,近战也一样- -就是射程掌握好即可)

using UnityEngine;
using System.Collections;

[RequireComponent(typeof(BehaviorOfAni))]
public class Archer : BehaviorAutoAttack {
    BehaviorOfAni ani;
    [SerializeField]
    float firingRange = 1.5f; // 射程

    protected override void Start () {
        init();
    }

    protected override void init() {
        base.init();
        ani = this.GetComponent<BehaviorOfAni>();
        this.move.setStoppingDistance(firingRange);
    }

    protected override void attack() {
        ani.beAttack();
    }

    protected override void run() {
        ani.beRun();
    }

    protected override void idle() {
        ani.beIdle();
    }

    void FixedUpdate () {
        doThing();
    }
}
测试

这里写图片描述

可以看到弓箭手自动去攻击另外一个弓箭手

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值