Unity3d作业||模型与动画

本文介绍了使用Unity3d开发一款智能巡逻兵游戏,要求包括地图、巡逻兵动画以及订阅发布模式的应用。巡逻兵在3~5边的凸多边形路径上移动,遇到障碍或玩家会作出相应反应。游戏设计强调使用工厂模式创建巡逻兵,通过事件总线实现发布订阅机制,降低代码耦合。同时,详细说明了玩家、围墙、相机移动和UI界面的设计与实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Unity3d作业||模型与动画

1、智能巡逻兵

  • 提交要求:
  • 游戏设计要求:
    • 创建一个地图和若干巡逻兵(使用动画);
    • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
    • 巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
    • 巡逻兵在设定范围内感知到玩家,会自动追击玩家;
    • 失去玩家目标后,继续巡逻;
    • 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;
  • 程序设计要求:
    • 必须使用订阅与发布模式传消息
      • subject:OnLostGoal
      • Publisher: ?
      • Subscriber: ?
    • 工厂模式生产巡逻兵
  • 友善提示1:生成 3~5个边的凸多边型
    • 随机生成矩形
    • 在矩形每个边上随机找点,可得到 3 - 4 的凸多边型
    • 5 ?
  • 友善提示2:参考以前博客,给出自己新玩法
订阅与发布模式

在“发布者-订阅者”模式中,称为发布者的消息发送者不会将消息编程为直接发送给称为订阅者的特定接收者。这意味着发布者和订阅者不知道彼此的存在。存在第三个组件,称为代理或消息代理或事件总线,它由发布者和订阅者都知道,它过滤所有传入的消息并相应地分发它们。换句话说,pub-sub是用于在不同系统组件之间传递消息的模式,而这些组件不知道关于彼此身份的任何信息。经纪人如何过滤所有消息?实际上,有几个消息过滤过程。最常用的方法有:基于主题和基于内容的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rdSyu7nm-1572317119706)(img/1.png)]

在本次游戏中,玩家结束游戏和得分的时候都会订阅一个服务,此时只需要让结束游戏或者得分这些情况产生的有效果定义GameEventManager发布的信息,通过这种方式实现功能的分类,降低代码的耦合。

UML

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W1WN1ITP-1572317119708)(img/UML.PNG)]

  • 围墙

    由栅栏和石墙组成


    在这里插入图片描述
    在这里插入图片描述

  • 玩家

    • 自由移动
    • 碰到巡逻兵游戏结束

​ 预制体

​ 需要给玩家添加一个 Capsule Collider

​ 用于检测巡逻兵与玩家的碰撞

​	[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gNo3u6sT-1572317119709)(img/2.PNG)]

添加一个Player的脚本,控制对象的移动

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

public class Player : MonoBehaviour
{
    public void FixedUpdate()
    {
        gameObject.GetComponent<Rigidbody>().freezeRotation = true;
        gameObject.GetComponent<Rigidbody>().velocity = new Vector3(0, 0, 0);
  

    }
}

  • 巡逻兵

    要求

    • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
    • 巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
    • 巡逻兵在设定范围内感知到玩家,会自动追击玩家;

    预制体

    添加一个 Capsule Collider用于检测巡逻兵与玩家的碰撞

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sCFGYPkv-1572317119710)(img/3.PNG)]

    添加一个Boxcollider在预制体的子节点上用于检测玩家进入巡逻兵的巡逻范围

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1vVBGI5v-1572317119711)(img/7.PNG)]

    挂上AreaCollide脚本

    public class AreaCollide : MonoBehaviour
    {
        public int sign;
        public delegate void CanFollow(int state,bool isEnter);
        public static event CanFollow canFollow;
    
        public delegate void AddScore();
        public static event AddScore addScore;
        void OnTriggerEnter(Collider collider)
        {
            if (collider.gameObject.tag == "Player")
            {
                canFollow(sign,true);
            }
        }
        private void OnTriggerExit(Collider collider)
        {
            if (collider.gameObject.tag == "Player")
            {
                canFollow(sign,false);
                addScore();
            }
        }
    }
    

    挂上Patrol的脚本和一个Patrolcolide的脚本

    Patrol.cs

     public enum PatrolState { PATROL,FOLLOW};
        public int sign;        //定义巡逻兵处于那个方块
        public bool isFollowPlayer = false;//此时是否检测到玩家,是否跟随
        public GameObject player=null;       
        public Vector3 startPos,nextPos;//位置
        private float minPosX,minPosZ;  // 定义可以移动的范围
        private bool isMoving = false;//是否移动
        private float distance;//记录距离
        private float speed = 1.2f;//速度
        PatrolState state = PatrolState.PATROL;//此时的状态,是否有玩家进入巡逻范围
    
    //每个巡逻兵开始初始化,定义初始位置
    private void Start()
        {
            minPosX = startPos.x - 2.5f;
            minPosZ = startPos.z - 2.5f;
            isMoving = false;
            AreaCollide.canFollow += changeStateToFollow;//改变状态
        }
    
     //定义巡逻兵的操作
     public void FixedUpdate()
        {
            if((SSDirector.getInstance().currentScenceController as FirstController).gameState == GameState.END)
            {
                return;//未初始化直接返回
            }
            if(state == PatrolState.PATROL)
            {
                GoPatrol();//自主巡逻状态Patrol
            }
            else if(state == PatrolState.FOLLOW)
            {
                Follow();//跟随玩家状态
            }
        }
    
    //自主巡逻
    public void GoPatrol()
        {
            if (isMoving)//定义巡逻的路线
            {
                transform.position = Vector3.MoveTowards(this.transform.position, nextPos, speed * Time.deltaTime);
                distance = Vector3.Distance(this.transform.position, nextPos);
                if(distance < 0.5)
                {
                    isMoving = false;
                }
                return;
            }
            float posX = Random.Range(0f, 5f);
            float posZ = Random.Range(0f, 5f);
            nextPos = new Vector3(minPosX+posX, 0, minPosZ+posZ);
            isMoving = true;    
        }
    
    //跟随玩家
    public void Follow()
        {
            if(player != null)
            {
                nextPos = player.transform.position;
                transform.position = Vector3.MoveTowards(this.transform.position, nextPos, speed * Time.deltaTime);
            }
        }
    
     //检测是否玩家进入巡逻范围,进行状态的变化
     public void changeStateToFollow(int sign_,bool isEnter)
        {
            if(sign == sign_ )
            {
                if (isEnter)
                {
                    state = PatrolState.FOLLOW;
                    player = (SSDirector.getInstance().currentScenceController as FirstController).player;
                    isFollowPlayer = true;
                }           
                else
                {
                    isFollowPlayer = false;
                    state = PatrolState.PATROL;
                    player = null;
                    isMoving = false;
                }
            }
            
        }
    

    patrolCollide.cs

    进行信号的捕捉

    public class PatrolCollide:MonoBehaviour
    {
        public delegate void CatchSuccess();
        public static event CatchSuccess catchSuccess;
        void OnTriggerEnter(Collider collider)
        {
            if (collider.gameObject.tag == "Player")
            {
                catchSuccess();        
            }
    
        }
    }
    

    巡逻兵的管理也使用工厂模式

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using UnityEngine;
    
    public class PatrolFactory:MonoBehaviour
    {
        private List<GameObject> used = new List<GameObject>();    // the used patrol
        private Vector3[] PatrolPos = new Vector3[3];
        private bool isProduce = false;
        FirstController firstController;
        private void Start()
        {
            firstController = SSDirector.getInstance().currentScenceController as FirstController;
        }
        public List<GameObject> GetPatrols()
        {
            firstController = SSDirector.getInstance().currentScenceController as FirstController;
            if (!isProduce)
            {
                int index = 0;
                float[] posZ = { 3.75f, -3.75f };
                float[] posX = { 3.75f, -3.75f };
                for (int i = 0; i < 2; i++)
                {
                    for (int j = 0; j < 2; j++)
                    {
                        if(posX[j] > 0 && posZ[i] > 0)
                        {
                            continue;
                        }
                        PatrolPos[index++] = new Vector3(posX[j], 0, posZ[i]);
                    }
                }
                for (int i = 0; i < 3; i++)
                {
                    GameObject patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol"));
                    patrol.transform.parent = firstController.plane.transform;
                    patrol.transform.position = PatrolPos[i];
                    patrol.GetComponent<Patrol>().sign = i + 1;
                    patrol.GetComponent<Patrol>().startPos = PatrolPos[i];
                    used.Add(patrol);
                }
                isProduce = true;
            }     
            return used;
        }
    
        public void destoryFactory()
        {
            foreach(var a in used)
            {
                DestroyImmediate(a);
            }
            used = new List<GameObject>();
            isProduce = false;
        }
    }
    
    
  • 相机移动部分

    相机规定要跟随玩家的移动

        public GameObject player;
        public FirstController sceneController;
        private Vector3 offset;
        // Use this for initialization  
        void Start()
        {
            sceneController = (FirstController)SSDirector.getInstance().currentScenceController;
            player = sceneController.player;
            offset = player.transform.position - this.transform.position;
        }
    
        // Update is called once per frame  
        void Update()
        {
            player = sceneController.player;
            if (sceneController.gameState == GameState.BEGIN)
                this.transform.position = player.transform.position - offset;
        }
    
  • FirstControl

    定义变量,巡逻兵工厂,列表控制巡逻兵产生和毁灭,游戏状态和UI配合,得分计数器

     private PatrolFactory patrolFactory;
        public GameObject player;
        public List<GameObject> patrols;
        public GameObject plane;
        public GameState gameState;
        public ScoreRecorder scoreRecorder;
    
     //进行初始整体布局
     void Awake()
        {
            SSDirector director = SSDirector.getInstance();
            director.currentScenceController = this;
            patrolFactory = Singleton<PatrolFactory>.Instance;
            LoadResources();
            gameState = GameState.BEGIN;
            PatrolCollide.catchSuccess += Gameover;
            scoreRecorder = Singleton<ScoreRecorder>.Instance;
        }
        //加载资源,包括墙壁玩家
        public void LoadResources()
        {
            plane = Instantiate(Resources.Load<GameObject>("Prefabs/Plane"));
            plane.transform.position = Vector3.zero;
            patrols = patrolFactory.GetPatrols();
            player= Instantiate(Resources.Load<GameObject>("Prefabs/Player"));
            player.transform.position = new Vector3(1.8f, -0.35f,1.8f);
            //player.transform.Rotate(0, 180, 0);
        }
        //重新开始游戏
        public void ReStart()
        {     
            rush = player;
            rush.SetActive(false);
            player = null;
            player = Instantiate(Resources.Load<GameObject>("Prefabs/Player"));
            player.transform.position = new Vector3(1.8f, -0.35f, 1.8f);
            patrolFactory.destoryFactory();
            patrols = patrolFactory.GetPatrols();
            DestroyImmediate(rush);
    
            gameState = GameState.BEGIN;
        }
        //结束游戏
        public void Gameover()
        {
            gameState = GameState.END;
        }
    	//控制玩家移动
        public void PlayerMove(Vector3 pos) 
        {
                player.transform.Translate(pos);
        }
    
  • ISceneController

    接口控制

  • ScoreRecorder

    得分计数器

        int Score = 0;
        void SetScore()
        {
            Score++;
        }
        // Use this for initialization  
        void Start()
        {
            AreaCollide.addScore += SetScore;//订阅事件  
        }
    
        public int GetScore()
        {
            return Score;
        }
    
  • Singleton

    单例模式

    public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
    {
        protected static T instance;
        public static T Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = (T)FindObjectOfType(typeof(T));
                    if (instance == null)
                    {
                        Debug.LogError("An instance of " + typeof(T)
                            + " is needed in the scene, but there is none.");
                    }
                }
                return instance;
            }
        }
    }
    
  • SSDirector

    导演

    public class SSDirector : System.Object {
    
        private static SSDirector _instance;
    
        public ISceneController currentScenceController { get; set; }
        private SSDirector() { }
        public static SSDirector getInstance()
        {
            if (_instance == null)
            {
                _instance = new SSDirector();
            }
             return _instance;
        }
    
        public int getFPS()
        {
            return Application.targetFrameRate;
        }
        public void setFPS(int fps)
        {
            Application.targetFrameRate = fps;
        }
    }
    
    
  • UI

    在这里插入图片描述UI界面

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值