unity中模拟电梯运行系统
提示:需要创建三个脚本,分别是 "FloorButton"、"Doors" 和 "Elevator",需要基本模型和功能按钮,添加相机。
文章目录
前言
使用 Unity 强大的功能和 C# 语言来开发电梯模拟系统,保姆级教程,只需要按照步骤操作,就可以达到想要的结果。(原工程文件随后也会发出来,可以参考学习)
提示:以下是本篇文章正文内容,下面案例可供参考
一、模型及按钮(game object)分类准备
按照下图,将电梯的外部按键和内部按键分别存放,然后新建三个脚本,分别命名为Doors、FloorButton、和Elevator,然后按照下图将将本赋给物体。(电梯内的数字按钮和外部的Up和Down可以直接用cube,本案例没用UI按钮,鼠标点击的物体,也就是game object)
二、插件导入
为电梯的开关门做准备,在门的动画上,使用了 DOTween 插件,通过调用 DOTween 库提供的 DOLocalMove 方法,实现了开关门的动画效果,以模拟电梯门开的效果。不用担心,插件这里准备好了,直接下载,拖入到unity的Assets文件夹下就可以了(你也可以自己在DOTween官网上下载,目前用的是最新的)。
下载链接:
下载DOTween 文件夹
链接:https://pan.baidu.com/s/1eZFcDl_uCHhQUnbQBZImCQ?pwd=6m19
提取码:6m19
三、脚本
接下来就是脚本了里面可改的参数都会告诉你怎么改。
每一句的代码都有注释,解释的非常清楚,新手也完全看的懂哦。
1.“FloorButton” 的脚本
代码如下(示例):直接复制就ok
脚本中有两个静态变量,分别是 “requestDict” 和 “destinationList”。这些变量用于存储电梯的请求,其中 “requestDict” 是一个字典,用于存储向上或向下请求的状态,而 “destinationList” 则是一个列表,用于存储到达楼层的请求。
using System.Collections.Generic;
using UnityEngine;
public class FloorButton : MonoBehaviour
{
// 存储Up或Down的请求
public static Dictionary<string, int> requestDict = new Dictionary<string, int>();
// 存储到达楼层的请求
public static List<int> destinationList = new();
private void OnMouseDown()
{
// 如果鼠标点击的物体名字是Up或者Down
if (gameObject.name == "Up" || gameObject.name == "Down")
{
// 找到父级的名字
string floorName = transform.parent.gameObject.name;
// 记录请求楼层号和Up还是Down
requestDict[floorName] = gameObject.name == "Up" ? 1 : -1;
}
else
{
// 将名字当作到达楼层号
int destinationFloor = int.Parse(gameObject.name);
// 记录到达楼层请求
if (!destinationList.Contains(destinationFloor))
{
destinationList.Add(destinationFloor);
}
}
}
}
2.“Doors” 的脚本
代码如下(示例):
该脚本中有两个私有变量 “floorDoorL” 和 “floorDoorR”,分别代表电梯门的左侧和右侧 Transform 对象。还有一个公开变量 “doorWidth” 表示门移动的距离,以及两个 Vector3 类型的变量 “LOriginPos” 和 “ROriginPos”,分别存储左门和右门的原始位置。
using DG.Tweening;
using UnityEngine;
public class Doors : MonoBehaviour
{
private Transform floorDoorL; // 电梯门左侧的Transform对象
private Transform floorDoorR; // 电梯门右侧的Transform对象
public readonly float doorWidth = 0.022f; // 门移动的距离
private Vector3 LOriginPos; // 左门原始位置
private Vector3 ROriginPos; // 右门原始位置
void Start()
{
floorDoorL = transform.Find("doorL"); // 获取左门Transform对象
floorDoorR = transform.Find("doorR"); // 获取右门Transform对象
LOriginPos = floorDoorL.localPosition; // 记录原始位置
ROriginPos = floorDoorR.localPosition;
}
public void OpenDoor()
{
// 左门向左移动
floorDoorL.DOLocalMove(LOriginPos - new Vector3(doorWidth, 0, 0), 1f);
// 右门向右移动
floorDoorR.DOLocalMove(ROriginPos + new Vector3(doorWidth, 0, 0), 1f);
}
public void CloseDoor()
{
// 左门回到原始位置
floorDoorL.DOLocalMove(LOriginPos, 1f);
// 右门回到原始位置
floorDoorR.DOLocalMove(ROriginPos, 1f);
}
}
3. “Elevator” 的脚本
代码如下(示例):
该脚本中有一些公共变量,如速度、楼层高度、最底层和最高层(可根据需要进行修改)等。还有私有变量,如电梯的初始位置、所有楼层门的脚本、当前楼层数和电梯是否在运行等。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Elevator : MonoBehaviour
{
public float speed = 2; // 电梯速度
public float floorHeight = 6; // 楼层高度
public int minFloor = 1; // 最底层
public int maxFloor = 5; // 最高层
private Vector3 initialPosition; // 电梯初始位置
private Doors[] floorDoors; // 获取所有楼层门的脚本
private int currentFloor; // 当前楼层
private bool isRunning; // 电梯是否运行
void Start()
{
// 找到所有楼层门的GameObject,并获取其上的 Doors 脚本
floorDoors = FindObjectsOfType<Doors>();
//使用Lambda表达式作为参数,用游戏对象的名称进行比较排序
Array.Sort(floorDoors, (a, b) => string.Compare(a.name, b.name));
initialPosition = transform.position;
currentFloor = Mathf.FloorToInt(initialPosition.y / floorHeight) + 1;
}
void Update()
{
// 电梯未运行时,开始处理请求
if (!isRunning)
{
StartCoroutine(HandleRequests());
}
}
private IEnumerator HandleRequests()
{
isRunning = true;
// 处理Up或Down请求
foreach (KeyValuePair<string, int> request in FloorButton.requestDict)
{
int targetFloor = int.Parse(request.Key);
// 检查满足条件的楼层
if ((request.Value == 1 && targetFloor >= currentFloor) || (request.Value == -1 && targetFloor <= currentFloor)||(request.Value == 1 && targetFloor <= currentFloor) || (request.Value == -1 && targetFloor >= currentFloor))
{
yield return StartCoroutine(MoveToFloor(targetFloor));
// 打开电梯门
OpenElevatorDoor();
// 等待乘客进出电梯
yield return new WaitForSeconds(4);
// 关闭电梯门
CloseElevatorDoor();
yield return new WaitForSeconds(2);
// 移除已完成的请求
FloorButton.requestDict.Remove(request.Key);
break;
}
}
// 处理到达楼层请求
for (int i = 0; i < FloorButton.destinationList.Count; i++)
{
yield return StartCoroutine(MoveToFloor(FloorButton.destinationList[i]));
// 打开电梯门
OpenElevatorDoor();
// 等待乘客进出电梯
yield return new WaitForSeconds(4);
// 关闭电梯门
CloseElevatorDoor();
yield return new WaitForSeconds(3);
// 移除已到达的楼层
FloorButton.destinationList.RemoveAt(i);
}
// 请求列表为空时,返回初始位置
if (FloorButton.requestDict.Count == 0 && FloorButton.destinationList.Count == 0)
{
yield return new WaitForSeconds(5);
yield return StartCoroutine(MoveToFloor(Mathf.FloorToInt(initialPosition.y / floorHeight) + 1));
}
isRunning = false;
}
private IEnumerator MoveToFloor(int targetFloor)
{
// 计算目标位置
Vector3 targetPosition = new(initialPosition.x, (targetFloor - 1) * floorHeight, initialPosition.z);
// 移动电梯
while (Vector3.Distance(transform.position, targetPosition) > 0.01f)
{
transform.position = Vector3.MoveTowards(transform.position, targetPosition, speed * Time.deltaTime);
yield return null;
}
// 更新当前楼层
currentFloor = targetFloor;
}
private void OpenElevatorDoor()
{
floorDoors[currentFloor-1].OpenDoor();
}
private void CloseElevatorDoor()
{
floorDoors[currentFloor-1].CloseDoor();
}
}
总结
在本次项目中,学会了如何使用 Unity 强大的功能和 C# 语言来开发电梯模拟系统。具体而言,学到了以下内容:
-
协程:协程是一种轻量级线程,在 Unity 中可以用于处理异步操作,比如动画、网络请求等。通过使用 yield return 语句暂停协程函数的执行,我们可以在适当的时候让其他协程函数有机会运行,从而使整个程序更加高效。
-
静态变量:静态变量是在类级别上定义的,所有实例都共享同一个变量。我们可以利用静态变量来记录用户点击电梯按钮的请求及到达楼层的请求,并能够在多个脚本之间共享这些请求,从而方便地控制电梯的移动和楼层判断。
-
DOTween 库:DOTween 是 Unity 的一个插件,可以用于创建动画效果。该插件提供了 DOLocalMove 方法,可帮助我们实现电梯门打开和关闭的动画效果。通过使用该插件,我们可以更加方便地为游戏添加各种动画效果。
-
Lambda 表达式:Lambda 表达式是一种简洁的匿名函数,可以用于排序、筛选、映射等操作。在本次项目中,我们使用 Lambda 表达式对楼层门进行排序,以确保电梯能够按照正确的顺序打开和关闭门。
总之,在本次项目中,我学会了如何利用 Unity 强大的功能和 C# 语言来实现一个基本的电梯系统,并掌握了许多有用的编程技巧和工具。这些技能和知识将有助于在开发中更加自信地构建复杂的机制和系统。