Unity3d-巡逻兵 观察者模式

作业需求

游戏规则:

创建一个地图和若干巡逻兵;

每个巡逻兵走一个3〜5个边的凸多边型,位置数据是相对地址即每次确定下一个目标位置,用自己当前位置为原点计算。

巡逻障碰撞到障碍物如树,则会自动选下一个点为目标;

巡逻兵在设定范围内感知到玩家,会自动追击玩家;

失去玩家目标后,继续巡逻;

计分:每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;

PS:源码和视频在GitHub的的上,链接如下:

https://github.com/wym199807/unity3d-hw6

扫描二维码关注公众号,回复: 857578 查看本文章

游戏制作过程

实例化部分:

两个预设:


两个实例的动作控制如下:



其中速度属性判断动作,toDie触发器触发死亡。

将两个预设加上刚体(虽然没用上物理学),和胶囊触发器,设置触发器的大小即可。

地板和墙用平面和立方体实现即可。

动作部分:

动作部分分为巡逻兵UI和主角控制两个动作,分别在两个脚本实现,最后挂在到预设中。

巡逻兵:

首先,需要实现巡逻兵的三个动作:静止,走路,追击(主角) 

我用三个继承SSSAction的基类分别实现它们,通过改动画中的速度实现动画切换。

    public class IdleAction : SSAction
    {
        private float time;
        private Animator ani;
        // 站立持续时间

        public static IdleAction GetIdleAction(float time, Animator ani)
        {
            IdleAction currentAction = ScriptableObject.CreateInstance<IdleAction>();
            currentAction.time = time;
            currentAction.ani = ani;
            return currentAction;
        }

        public override void Start()
        {
            ani.SetFloat("Speed", 0);
            // 进入站立状态
        }

        public override void Update()
        {
            if (time == -1) return;
            // 永久站立
            time -= Time.deltaTime;
            // 减去时间
            if (time < 0)
            {
                this.destory = true;
                this.callback.SSEventAction(this);
            }
        }
    }

    public class WalkAction : SSAction
    {
        private float speed;
        private Vector3 target;
        private Animator ani;
        // 移动速度和目标的地点

        public static WalkAction GetWalkAction(Vector3 target, float speed, Animator ani)
        {
            WalkAction currentAction = ScriptableObject.CreateInstance<WalkAction>();
            currentAction.speed = speed;
            currentAction.target = target;
            currentAction.ani = ani;
            return currentAction;
        }

        public override void Start()
        {
            ani.SetFloat("Speed", 0.5f);
            // 进入走路状态
        }

        public override void Update()
        {
            Quaternion rotation = Quaternion.LookRotation(target - transform.position);
            if (transform.rotation != rotation) transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * speed * 5);
            // 进行转向,转向目标方向

            this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime);
            if (this.transform.position == target)
            {
                this.destory = true;
                this.callback.SSEventAction(this);
            }
        }
    }

    public class RunAction : SSAction
    {
        private float speed;
        private Transform target;
        private Animator ani;
        // 移动速度和人物的transform

        public static RunAction GetRunAction(Transform target, float speed, Animator ani)
        {
            RunAction currentAction = ScriptableObject.CreateInstance<RunAction>();
            currentAction.speed = speed;
            currentAction.target = target;
            currentAction.ani = ani;
            return currentAction;
        }

        public override void Start()
        {
            ani.SetFloat("Speed", 1);
            // 进入跑步状态
        }

        public override void Update()
        {
            Quaternion rotation = Quaternion.LookRotation(target.position - transform.position);
            if (transform.rotation != rotation) transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * speed * 5);
            // 转向

            this.transform.position = Vector3.MoveTowards(this.transform.position, target.position, speed * Time.deltaTime);
            if (Vector3.Distance(this.transform.position, target.position) < 0.5)
            {
                this.destory = true;
                this.callback.SSEventAction(this);
            }
        }
    }
    public void SSEventAction(SSAction source, SSActionEventType events = SSActionEventType.COMPLETED, int intParam = 0, string strParam = null, Object objParam = null)
    {
        currentState = currentState > ActionState.WALKBACK ? ActionState.IDLE : (ActionState)((int)currentState + 1);
        // 改变当前状态
        switch (currentState)
        {
            case ActionState.WALKLEFT:
                walkLeft();
                break;
            case ActionState.WALKRIGHT:
                walkRight();
                break;
            case ActionState.WALKFORWARD:
                walkForward();
                break;
            case ActionState.WALKBACK:
                walkBack();
                break;
            default:
                idle();
                break;
        }
        // 执行下个动作
    }

主角

主角部分主要实现按键控制移动,在这里我用运动学实现。

    void FixedUpdate () {
        if (!ani.GetBool("isLive")) return;
        // 如果死亡,不执行所有动作

        float x = Input.GetAxis("Horizontal");
        float z = Input.GetAxis("Vertical");

        ani.SetFloat("Speed", Mathf.Max(Mathf.Abs(x), Mathf.Abs(z)));
        // 设置速度
        ani.speed = 1 + ani.GetFloat("Speed") / 3;
        // 调整跑步的时候的动画速度

        velocity = new Vector3(x, 0, z);

        // 如果处于运动,则转向
        if (x != 0 || z != 0)
        {
            Quaternion rotation = Quaternion.LookRotation(velocity);
            if (transform.rotation != rotation) transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.fixedDeltaTime * rotateSpeed);
        }

        this.transform.position += velocity * Time.fixedDeltaTime * runSpeed;
        // 主角移动
    }

逻辑交互部分:

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

public interface Publish
{
    void notify(ActorState state, int pos, GameObject actor);
    // 发布函数

    void add(Observer observer);
    // 委托添加事件

    void delete(Observer observer);
    // 委托取消事件
}

public interface Observer
{
    void notified(ActorState state, int pos, GameObject actor);
    // 实现接收函数
}

public enum ActorState { ENTER_AREA, DEATH }

public class Publisher : Publish {

    private delegate void ActionUpdate(ActorState state, int pos, GameObject actor);
    private ActionUpdate updatelist;
    // 委托定义

    /// <summary>
    /// 单实例模式
    /// </summary>
    private static Publish _instance;
    public static Publish getInstance()
    {
        if (_instance == null) _instance = new Publisher();
        return _instance;
    }

    public void notify(ActorState state, int pos, GameObject actor)
    {
        if (updatelist != null) updatelist(state, pos, actor);
        // 发布信息
    }

    public void add(Observer observer)
    {
        updatelist += observer.notified;
    }

    public void delete(Observer observer)
    {
        updatelist -= observer.notified;
    }
}
    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag("Area"))
        {
            Publish publish = Publisher.getInstance();
            int patrolType = other.gameObject.name[other.gameObject.name.Length - 1] - '0';
            publish.notify(ActorState.ENTER_AREA, patrolType, this.gameObject);
            // 进入区域后,发布消息
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Patrol") && ani.GetBool("isLive"))
        {
            ani.SetBool("isLive", false);
            ani.SetTrigger("toDie");
            // 执行死亡动作

            Publish publish = Publisher.getInstance();
            publish.notify(ActorState.DEATH, 0, null);
            // 碰撞后,发布死亡信息
        }
    }


猜你喜欢

转载自blog.csdn.net/naruto1998/article/details/80286355