unity3d之巡逻兵

本周作业

游戏设计要求: 
创建一个地图和若干巡逻兵(使用动画); 
每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算; 
巡逻兵碰撞到障碍物,则会自动选下一个点为目标; 
巡逻兵在设定范围内感知到玩家,会自动追击玩家; 
失去玩家目标后,继续巡逻; 
计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束,玩家失败;玩家得分大于等于5,游戏结束,玩家胜利。

作业过程:

1.素材获取: http://www.aigei.com/3d/人物模型来自于该网站,而场景则直接搬运大佬的,最终如上。
2.准备阶段:首先我们先把下载好的模型,拖入项目中,然后将动画模式改为Generic,这些都是上课时讲到过的,然后调整模型大小,加刚体和胶囊球碰撞盒,并设置最重要的 AnimatorController,如下:

这两个对象的动作都很简单,actor由玩家自己控制移动,而巡逻兵有3个动作,idle(一段时间内站着不动),walk和run,巡逻兵在没有发现主角到自己区域的时候不会移动的很快,只会缓慢移动,而主角到了巡逻兵所在的区域,巡逻兵会加快速度。

当巡逻兵是idle状态的时候,(刚开始游戏或者由于主角离开了一个区域,到了新的区域),我们就会调用idle动作,当站着不动的时间消耗完毕的时候,我们就会调用回调函数,从而让巡逻兵做预先设置好的下一个动作,空闲->往左走->往上走->往右走->往下走->如此循环,就实现了巡逻的功能。


再来就是这次游戏中所用到的一些模式,都是我们前几次作业中涉及到的。

单例模式:

这个没什么区别,直接套用之前模板即可,不做过多解释。

门面模式:

门面模式中很多东西同样可以直接复用,只更改了userGUI和Scenecontrol。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SceneController : MonoBehaviour, Observer
{
    public Text scoreText;
    public Text centerText;

    private ScoreRecorder record;
    private UIController UI;
    private ObjectFactory fac;

    private float[] posx = { -5, 7, -5, 5 };
    private float[] posz = { -5, -7, 5, 5 };
    // 开始位置

    void Start()
    {
        record = new ScoreRecorder();
        record.scoreText = scoreText;
        UI = new UIController();
        UI.centerText = centerText;
        fac = Singleton<ObjectFactory>.Instance;

        Publish publisher = Publisher.getInstance();
        publisher.add(this);
        // 添加事件

        LoadResources();
    }

    private void LoadResources()
    {
        Instantiate(Resources.Load("prefabs/actor"), new Vector3(2, 0, -2), Quaternion.Euler(new Vector3(0, 0, 0)));
        // 初始化主角
        ObjectFactory fac = Singleton<ObjectFactory>.Instance;
        for (int i = 0; i < posx.Length; i++)
        {
            GameObject patrol = fac.setObjectOnPos(new Vector3(posx[i], 0, posz[i]), Quaternion.Euler(new Vector3(-90, 0, 0)));
            patrol.name = "Patrol" + (i + 1);
            // 初始化巡逻兵
        }
    }

    /// <summary>
    /// 如果角色死亡,显示LOSE
    /// </summary>
    /// <param name="state">订阅状态</param>
    /// <param name="pos"></param>
    public void notified(ActorState state, int pos, GameObject actor)
    {
        if (state == ActorState.ENTER_AREA) record.addScore(1);
        else UI.loseGame();
    }
}


工厂模式:

类似于飞碟中的工厂模式,在游戏开始时生成巡逻兵和actor在指定的位置,并在之后free掉它们

public class ObjectFactory : MonoBehaviour {

    //工厂模式就负责生产,其他的不负责,所以可以看到这个类只和角色有关系和其他所有代码都没有关系
    private GameObject actor = null;                                      //玩家
    private GameObject patrol = null;                                     //巡逻兵
    private List<GameObject> patrolList = new List<GameObject>();        //正在被使用的巡逻兵
    private Vector3[] vec = new Vector3[4];                             //保存每个巡逻兵的初始位置

    public GameObject LoadActor()
    {
        player = Instantiate(Resources.Load("Prefabs/actor"), new Vector3(0, 4, 0), Quaternion.identity) as GameObject;
        return actor;
    }

    public List<GameObject> LoadPatrol()
    {
        int[] pos_x = { -6, 4, 13 };
        int[] pos_z = { -4, 6, -13 };
        int index = 0;

        for(int i=0;i < 2;i++)
        {
            for(int j=0;j < 2;j++)
            {
                vec[index] = new Vector3(pos_x[i], 0, pos_z[j]);
                index++;
            }
        }
        for(int i=0; i < 4; i++)
        {
            patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol"));
            patrol.transform.position = vec[i];
            patrol.GetComponent<PatrolData>().sign = i + 1;
            patrol.GetComponent<PatrolData>().start_position = vec[i];
            patrolList.Add(patrol);
        }   
        return patrolList;
    }

    //游戏结束的时候会暂停所有动作
    public void StopPatrol()
    {
        for (int i = 0; i < patrolList.Count; i++)
        {
            patrolList[i].gameObject.GetComponent<Animator>().SetBool("run", false);
        }
    }
}

观察者模式:

然后就是本次作业要求用到的观察者模式,当Actor触发了某些规则,就可以发布相应的信息,然后通过信息的参数,订阅者就会做出相应的动作。比如,当Actor进入到某个巡逻兵的区域,他就会发布信息publish.notify(ActorState.ENTER_AREA, patrol, this.gameObject);通知对应区域的巡逻兵,巡逻兵收到消息,就可以开始追踪主角。

 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);
            // 进入区域后,发布消息
        }
    }
同理actor死亡时,也会发布死亡的消息,由于设计较为简单,只有这两种情况需要使用到观察者模式。

再来就是这次作业中遇到的问题:首先就是对胶囊碰撞盒的设置,一开始设置actor为y-Axis时,老是会出现卡墙的bu,后来干脆从z-Axis着手,就解决了这个问题,可能是模型的问题。而巡逻兵则会在地板摩擦或者东倒西歪。后来经过大佬的指点,将高度提高了一丢丢,就解决了这个问题。这次作业可以说是对前面那么多次作业的一次总结,还是需要多加理解,才能明白整个设计流程,不至于抓不到方向。

视频链接:点击打开链接


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值