unity3D 巡逻兵

2023-11-08

游戏要求

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

程序设计要求

  1. 必须使用订阅与发布模式传消息
  2. 使用工厂模式生产巡逻兵

设计过程

巡逻兵工厂

首先是巡逻兵的生产,与之前打飞碟游戏一样,设计一个类来专门负责生产巡逻兵,而在控制类中直接调用这个工厂即可。所以工厂也是需要单例化的,具体代码如下:

public class factory : MonoBehaviour {
    private static factory _instance;
    private List<GameObject> guard = new List<GameObject>();
    private GameObject cur_guard;

    public static factory getinstance()
    {
        if (_instance == null)
        {
            _instance = new factory();
        }
        return _instance;
    }

    public GameObject getguard(Vector3 po)
    {
        GameObject cur = Instantiate(Resources.Load<GameObject>("guard"), po, Quaternion.identity);
        return cur;
    }
	
}
巡逻兵移动脚本

对于巡逻兵来说,这里主要有两种运动模式
a. 自由巡逻 b. 追逐玩家

  1. 对于自由巡逻是通过行走固定路线来实现的,题目中要求的是行走一个多边形,我使用的是一个五边形路线。
    五边形路线的生成方法是首先固定设置一块正方形区域,然后随机在四条边上选取四个点,再与左下角的点连接起来,形成一个多边形。
    在这里插入图片描述
    在创建巡逻兵的时候就生成这4个点,然后让巡逻兵依次顺着这5个点行走。(这里的x[order],y[order]为目标点的坐标,order为点的序号)
    在这里插入图片描述
    每次朝着某一个点行走时只需要在最开始设置方向即可,然后一直朝着那个点走,直到走到那个点附近。
void runtopoint(float px,float pz)
    {
        float lx = this.transform.position.x;
        float lz = this.transform.position.z;
        if (System.Math.Abs(lx - px) < delta && System.Math.Abs(lz - pz) < delta)
        {
            order++;
            order %= 5;
            //Debug.Log("change dir");
            dir = new Vector3(x[order] - lx, 0, z[order] - lz);
            this.transform.rotation = Quaternion.LookRotation(dir, Vector3.up);
        }
        else
        {
            //Debug.Log("run");
            this.transform.position += dir * Time.fixedDeltaTime * 0.5f;
        }
    }
  1. 对于追逐玩家时首先需要得到玩家的坐标,然后设置方向,朝着玩家前进,直到接近玩家或者玩家逃出范围,在追逐失败之后巡逻兵继续进行自由巡逻状态,朝着追逐玩家之前朝向的目标点前进。
    首先是获取玩家坐标,这里使用private void OnTriggerEnter(Collider other)函数来进行判断,当有物体触碰到巡逻兵的Box Collider时就会触发这个函数,然后在这个函数里面判断触碰到的对象是否是玩家,这里通过tag来判断。
private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
        {
            //Debug.Log("run to player");
            mod = 2;
        }
    }

为了达到此目的,需要对巡逻兵添加Box Collider组件
在这里插入图片描述
(这里的size即为“视野”大小)

  1. 对于巡逻兵的动画设置
    由于巡逻兵只有行走这个动画,没有添加攻击,所以animator控制就非常的简单
    在这里插入图片描述
  2. 对玩家的坐标获取
    玩家首先会触发OnTriggerEnter函数表示已经进入巡逻兵视野,然后这时只要玩家一直在视野,就会不断触发OnTriggerStay函数,在这个函数里就可以获取玩家的坐标,以及设置巡逻兵的方向。
private void OnTriggerStay(Collider other)
    {
        if (other.tag == "Player")
        {
            if (t < 1f)
            {
                t += Time.deltaTime;
            }
            else
            {
                playerx = other.transform.position.x;
                playerz = other.transform.position.z;
                setdir(playerx, playerz);
                t = 0f;
            }
        }
    }

(这里并没有实时的获取坐标,而是隔一段时间获取是为了减少计算量。)
5. 恢复巡逻状态
使用OnTriggerExit函数来执行,当玩家离开视野后调用,设置下一个目标点。

private void OnTriggerExit(Collider other)
    {
        if (other.tag == "Player")
        {
            //Debug.Log("out of player");
            mod = 1;
            setdir(x[order], z[order]);
            rule.getinstance().addscore();
        }
    }
玩家控制

玩家的模型我是在unity的商店下载的一个,使用到的动画只有跑步,而且其自带了控制脚本,所以可以直接使用键盘对其进行操作。代码也不在这里展示了。

订阅与发布者模式
这个模式主要功能是将想订阅的时间注册到调度中心,然后事件触发的时候发布者发布该事件到调度中心,然后由调度中心调度订阅者的处理代码。

实现的方法是使用一个规则类来对分数进行记录,当巡逻兵判断玩家走出视野后就调用规则类使其记录的分数加一从而实现。这个规则类也需要单例化。

public class rule {

	int score;
    

	private static rule _instance;

	public static rule getinstance()
	{
		if (_instance == null)
		{
			_instance = new rule();
		}
		return _instance;
	}

	void Start()
	{
		init();
	}

    public void init()
	{
		score = 0;
	}

	public void addscore()
	{
		score++;
	}

    public int getscore()
	{
		return score;
	}
}
游戏的控制

对于整个游戏的进程,使用control类来进行控制,在游戏开始时初始化各种参数变量。

  1. UI界面
    对于界面的显示主要有一个按钮与一个文本框,按钮触发游戏开始,文本框显示游戏分数以及游戏的一些相关信息。这里需要注意的是位置的设置,使用screen.width来获取屏幕的宽度,从而设置其居中显示。然后如果需要对其位置大小等进行改变,只需要在设置它的时候使用变量实时改变即可,界面如下:
    在这里插入图片描述
    实现的代码如下:
private void OnGUI()
	{
		if (GUI.Button(new Rect(bux,buy,buwidth,buheight), "begin"))
		{
			director.getinstance().setbegin();
            bux = 0;
            buy = 0;
            buheight = 0;
            buwidth = 0;
			//director.getinstance().setrun();
			//Debug.Log(director.getinstance().getstate());
			
		}
		GUI.color = Color.black;
		GUI.Label(new Rect(labelx, labely, labelwid, labelhe), showlabel);
		
	}

(这里设置按钮在点击之后移到左上角隐藏)

  1. 游戏的进程
    对于游戏的控制开始与结束使用一个导演类来进行,当开始按钮点击时设置导演类的参数为开始,从而通知各个对象或者函数游戏开始,这里主要是control中update的设置。在处理这个的时候我使用了预备和进行两个状态,点击按钮之后进入准备状态,初始化巡逻兵和玩家,然后才设置为运行状态,游戏开始运行。
void Update () {
		if (director.getinstance().getstate() == 1)
		{
			//Debug.Log("create");
			guard[0] = factory.getinstance().getguard(new Vector3(2.5f, 0f, 2.5f));
			guard[1] = factory.getinstance().getguard(new Vector3(-22.5f, 0f, -22.5f));
			guard[2] = factory.getinstance().getguard(new Vector3(-22.5f, 0f, 2.5f));
			director.getinstance().setrun();
            player.SetActive(true);
            
		}
        else if (director.getinstance().isover() == true)
        {
            player.SetActive(false);
            labelx = Screen.width / 2 - labelx / 2;
            labely = Screen.height / 2;
            showlabel = "you lose and the score is " + rule.getinstance().getscore();
        }
        else
        {
            showlabel = "score: " + rule.getinstance().getscore();
        }
	}

游戏的结束在巡逻兵里面设置,一旦触发结束条件就通知导演游戏结束,并使控制类进行相关显示。
在这里插入图片描述
对于导演类同样需要单例化:

public class director {

	private static director _instance;

	public int gamestate;//0 for stop   1 for ready   2 for run   3 for over

    public void init()
	{
		gamestate = 0;
        
	}

    public void setbegin()
	{
		gamestate = 1;
	}

    public void setrun()
	{
		gamestate = 2;
	}

    public void setover()
	{
		gamestate = 3;
	}

    public bool isover()
    {
        if (gamestate == 3)
        {
            return true;
        }
        return false;
    }

    public int getstate()
	{
		return gamestate;
	}

    public static director getinstance()
	{
		if (_instance == null)
		{
			_instance = new director();
		}
		return _instance;
	}
}
  1. 镜头的控制
    采用的是镜头一直追踪玩家后背的形式,拷贝的网上的代码,但也挺简单的:
public class cameracontrol : MonoBehaviour
{
    public Transform target=null;
    public float distanceUp = 5f;
    public float distanceAway = 2f;
    public float smooth = 60f;//位置平滑移动值
    public float camDepthSmooth = 5f;

    //public bool begin = false;

    bool setalready;
    // Use this for initialization
    void Start()
    {
        setalready = false;
        //target = GameObject.FindGameObjectWithTag("Player").transform;
    }

    // Update is called once per frame
    void Update()
    {
        
        if (director.getinstance().getstate()==2 && setalready == false)
        {
            target = GameObject.FindGameObjectWithTag("Player").transform;
            setalready = true;
        }
        // 鼠标轴控制相机的远近
        if ((Input.mouseScrollDelta.y < 0 && Camera.main.fieldOfView >= 3) || Input.mouseScrollDelta.y > 0 && Camera.main.fieldOfView <= 80)
            {
                Camera.main.fieldOfView += Input.mouseScrollDelta.y * camDepthSmooth * Time.deltaTime;
            }
        //}
    }

    void LateUpdate()
    {
        if (director.getinstance().getstate() == 2 && setalready == false)
        {
            target = GameObject.FindGameObjectWithTag("Player").transform;
            setalready = true;
        }
        //相机的位置
        Vector3 disPos = target.position + Vector3.up * distanceUp - target.forward * distanceAway;
        transform.position = Vector3.Lerp(transform.position, disPos, Time.deltaTime * smooth);
        //相机的角度
        transform.LookAt(target.position);
    }
}
游戏最终运行截图

在这里插入图片描述

项目地址

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

unity3D 巡逻兵 的相关文章

随机推荐

  • events.js:183 throw er; // Unhandled 'error' event 相关问题解决办法

    在启动项目的时候可能会出现以下问题 出现原因 出现这个问题的主要原因是因为端口被占用了 解决办法 查询端口号是否被占用 netstat aon findstr 8000 找到对应程序 tasklist findstr 9060 再任务管理器
  • 2015款Mac笔记本安装Windows10系统到外置移动硬盘教程

    终于把2015年的15寸Macbook Pro笔记本安装上了Windows10操作系统 并且是在外置的移动硬盘上安装的 运行的效果和用户体验也是非常棒 之前网上购买了加密的视频 但是这个视频必须是windows系统 还不能是虚拟机里面的系统
  • QT关键问题解决之paintevent理解

    基础概念 paintEvent QPaintEvent 函数是QWidget类中的虚函数 用于ui的绘制 会在多种情况下被其他函数自动调用 比如update 时 运行时机 一个重绘事件用来重绘一个部件的全部或者部分区域 下面几个原因的任意一
  • 中移动 NB-IoT (M5310-A)学习笔记

    一 来自 M5310 A硬件设计手册V1 5 DCE Data Communication EquipmentDTE Data Terminal Equipment 模块的三种工作模式 模式 描述 Active 模块处于活动状态 所有功能正
  • CreateFile之类的几个函数

    这几天看windows API一日一练 博主给画出了道道 我来填填具体点的内容 需求 想操作文件 串口 并口 USB等等 原理层 其实很简单 就三步 打开 操作 然后关闭 实践层1 打开用CreateFile 读写操作用WriteFile
  • 宝塔面板部署nginx+springboot+netty

    nginx配置集成netty的springboot前后端分离项目 项目环境 CentOS 7 9 宝塔面板 nginx1 21 前后端分离项目按照日常部署方式部署到服务器 前往nginx配置文件nginx conf 配置TCP socket
  • 两台机器之间同步时间,并修改服务器层级

    作业a 第一台机器从阿里云同步时间 第二台机器从第一台机器同步时间 第一台机器配置 vim etc chrony conf 修改第一台机器的配置文件 将原有的pool注释掉 并添加阿里云时钟源 gt server ntp aliyun co
  • C++实现两个字符串交替组合成一个字符串

    引言 这道题来自力扣 给出两个字符串 将两个字符串交替着组合成一个字符串 如 string str1 abcd string str2 hb string str ahbbcd string str1 abcd string str2 hb
  • kafka高性能设计:内存池

    前言 Kafka的内存池是一个用于管理内存分配的缓存区域 它通过在内存上保留一块固定大小的内存池 用于分配消息缓存 批处理缓存等对象 以减少频繁调用内存分配函数的开销 Kafka内存池的实现利用了Java NIO中的 ByteBuffer
  • 罗技键盘连计算机,罗技键盘怎么连接电脑

    罗技蓝牙键盘连接电脑需装入电池 打开电源开关 转动拨盘至 1 位置 然后长按 PC 键3秒进入 搜索 模式 打开电脑 前往 设置 设备 蓝牙和其他设备 打开 蓝牙 在蓝牙搜索列表中选中罗技蓝牙键盘的名称 确认配对即可完成连接 本文以惠普光影
  • 搭建tcp客户端,双进程实现tcp服务端客户端随时收发,udp服务端客户端

    tcp客户端 include
  • HTML+CSS简易淘宝页面

    效果图 效果图中的图片可以去我微信公众号新白者 回复照片就行 HTML代码 注意这个外部链接引入css的名字与自己的相同 div div div a a div div div
  • 自动化测试开发 —— 如何封装自动化测试框架?

    封装自动化测试框架 测试人员不用关注框架的底层实现 根据指定的规则进行测试用例的创建 执行即可 这样就降低了自动化测试门槛 能解放出更多的人力去做更深入的测试工作 本篇文章就来介绍下 如何封装自动化测试框架 1 明确自动化测试框架需求 支持
  • 两数相加—思路和心得

    题目链接 https leetcode cn com problems add two numbers Definition for singly linked list public class ListNode int val List
  • Python运算符重载及其可重载运算符

    每个类型都有其独特的操作方法 例如列表类型支持直接做加法操作实现添加元素的功能 字符串类型支持直接做加法实现字符串的拼接功能 也就是说 同样的运算符对于不同序列类型的意义是不一样的 这是怎么做到的呢 其实在 Python 内部 每种序列类型
  • AJAX面试题

    1 什么是AJAX 为什么要使用Ajax 请谈一下你对Ajax的认识 什么是ajax AJAX是 Asynchronous JavaScript and XML 的缩写 他是指一种创建交互式网页应用的网页开发技术 Ajax包含下列技术 基于
  • 蓝桥杯备赛Day8——队列

    大家好 我是牛哥带你学代码 本专栏详细介绍了蓝桥杯备赛的指南 特别适合迎战python组的小白选手 专栏以天作为单位 定期更新 将会一直更新 直到所有数据结构相关知识及高阶用法全部囊括 欢迎大家订阅本专栏 队列也属于基础数据结构 队列概念
  • C#串口通信三步走

    第一步 实例化串口通讯类 SerialPort sp new SerialPort 第二步 设置串口信息并打开串口 串口设置 public void SetSP string PortName string BaudRate string
  • 项目开发总结报告(GB8567——88)(转载)

    项目开发总结报告 GB8567 88 1引言1 1编写目的说明编写这份项目开发总结报告的目的 指出预期的阅读范围 1 2背景说明 a 本项目的名称和所开发出来的软件系统的名称 b 此软件的任务提出者 开发者 用户及安装此软件的计算中心 1
  • unity3D 巡逻兵

    游戏要求 创建一个地图和若干巡逻兵 使用动画 每个巡逻兵走一个3 5个边的凸多边型 位置数据是相对地址 即每次确定下一个目标位置 用自己当前位置为原点计算 巡逻兵碰撞到障碍物 则会自动选下一个点为目标 巡逻兵在设定范围内感知到玩家 会自动追