Unity【SwitchableObject】- 实现一个物体开关控制系统

简介: Unity【SwitchableObject】- 实现一个物体开关控制系统

 

本文介绍如何实现一个物体的开关控制系统,例如门的开关控制、灯的开关控制等,一切包含打开、关闭这两种状态的物体,均可以通过继承下面的抽象类进行重写实现。

状态枚举:

namespaceSK.Framework{
/// <summary>/// 状态/// </summary>publicenumSwitchState    {
/// <summary>/// 开着的/// </summary>Open,
/// <summary>/// 关着的/// </summary>Close,
    }
}

image.gif

接口:

namespaceSK.Framework{
/// <summary>/// 可开关物体接口/// </summary>publicinterfaceISwitchableObject    {
SwitchStateState { get; }
voidSwitch();
voidOpen();
voidClose();
    }
}

image.gif

抽象类:

usingUnityEngine;
namespaceSK.Framework{
publicabstractclassSwitchableObject : MonoBehaviour, ISwitchableObject    {
//默认设为关闭状态        [SerializeField] protectedSwitchStatestate=SwitchState.Close;
/// <summary>/// 当前状态/// </summary>publicSwitchStateState { get { returnstate; } }
/// <summary>/// 切换 若为打开状态则关闭 若为关闭状态则打开/// </summary>publicvoidSwitch()
        {
switch (State)
            {
caseSwitchState.Open: Close(); break;
caseSwitchState.Close: Open(); break;
            }
        }
/// <summary>/// 开门/// </summary>publicabstractvoidOpen();
/// <summary>/// 关门/// </summary>publicabstractvoidClose();
    }
}

image.gif

开关处理器,例如我们想要通过一个开关控制多个灯时,或者通过一个开关控制一对门时,均可以通过开关处理器,同时处理多个可开关物体:

usingUnityEngine;
namespaceSK.Framework{
/// <summary>/// 开关处理器(把手)/// </summary>publicclassSwitchableObjectHandler : SwitchableObject    {
        [SerializeField] privateSwitchableObject[] handleArray;
publicoverridevoidOpen()
        {
if (state==SwitchState.Open) return;
state=SwitchState.Open;
for (inti=0; i<handleArray.Length; i++)
            {
handleArray[i].Open();
            }
        }
publicoverridevoidClose()
        {
if (state==SwitchState.Close) return;
state=SwitchState.Close;
for (inti=0; i<handleArray.Length; i++)
            {
handleArray[i].Close();
            }
        }
    }
}

image.gif

这里以门的开关控制为例,我们将门的类型分为移动门和旋转门,首先创建一个门的基类:

usingUnityEngine;
namespaceSK.Framework{
/// <summary>/// 可开关门/// </summary>publicabstractclassSwitchableDoor : SwitchableObject    {
//开/关所用的时长        [SerializeField] protectedfloatduration=0.5f;
//打开状态的值protectedVector3openValue;
//关闭状态的值protectedVector3closeValue;
    }
}

image.gif

1.移动门:

image.gif

参数说明:

1.State: 门的默认状态(在场景中门是开着还是关着的);

2.Duration:开关门动作的时长;

3.Direction:门从默认状态到另一个状态的移动方向;

4.Magnitude:门从默认状态到另一状态移动的长度。

image.gif

usingUnityEngine;
usingSystem.Collections;
#ifUNITY_EDITORusingUnityEditor;
#endifnamespaceSK.Framework{
/// <summary>/// 移动门/// </summary>publicclassMoveDoor : SwitchableDoor    {
        [SerializeField] privateVector3direction; //移动方向        [SerializeField] privatefloatmagnitude=1f; //移动的长度privateCoroutineswitchCoroutine;
privatevoidStart()
        {
switch (state)
            {
caseSwitchState.Open:
openValue=transform.position;
closeValue=transform.position+direction.normalized*magnitude;
break;
caseSwitchState.Close:
openValue=transform.position+direction.normalized*magnitude;
closeValue=transform.position;
break;
            }
        }
publicoverridevoidOpen()
        {
if (state==SwitchState.Open) return;
state=SwitchState.Open;
if (switchCoroutine!=null) StopCoroutine(switchCoroutine);
switchCoroutine=StartCoroutine(OpenCoroutine());
        }
publicoverridevoidClose()
        {
if (state==SwitchState.Close) return;
state=SwitchState.Close;
if (switchCoroutine!=null) StopCoroutine(switchCoroutine);
switchCoroutine=StartCoroutine(CloseCoroutine());
        }
privateIEnumeratorOpenCoroutine()
        {
floatbeginTime=Time.time;
Vector3beginPos=transform.position;
for (;(Time.time-beginTime) <duration;)
            {
floatt= (Time.time-beginTime) /duration;
transform.position=Vector3.Lerp(beginPos, openValue, t);
yieldreturnnull;
            }
transform.position=openValue;
switchCoroutine=null;
        }
privateIEnumeratorCloseCoroutine()
        {
floatbeginTime=Time.time;
Vector3beginPos=transform.position;
for (; (Time.time-beginTime) <duration;)
            {
floatt= (Time.time-beginTime) /duration;
transform.position=Vector3.Lerp(beginPos, closeValue, t);
yieldreturnnull;
            }
transform.position=closeValue;
switchCoroutine=null;
        }
#ifUNITY_EDITORprivatevoidOnDrawGizmosSelected()
        {
if (!Application.isPlaying)
            {
switch (state)
                {
caseSwitchState.Open:
openValue=transform.position;
closeValue=transform.position+direction.normalized*magnitude;
break;
caseSwitchState.Close:
openValue=transform.position+direction.normalized*magnitude;
closeValue=transform.position;
break;
                }
            }
Handles.color=Color.cyan;
Handles.DrawWireCube(openValue, Vector3.one* .1f);
Handles.DrawWireCube(closeValue, Vector3.one* .1f);
Handles.DrawLine(openValue, closeValue);
Handles.Label(openValue, "Open");
Handles.Label(closeValue, "Close");
        }
#endif    }
}

image.gif

image.gif

2.旋转门:

image.gif

参数说明:

1.State: 门的默认状态(在场景中门是开着还是关着的);

2.Duration:开关门动作的时长;

3.Angle:门从默认状态到另一个状态的旋转角度。

image.gif

usingUnityEngine;
usingSystem.Collections;
#ifUNITY_EDITORusingUnityEditor;
#endifnamespaceSK.Framework{
/// <summary>/// 旋转门/// </summary>publicclassRotateDoor : SwitchableDoor    {
        [SerializeField] privatefloatangle=90f; //旋转角度privateCoroutineswitchCoroutine;
privatevoidStart()
        {
switch (state)
            {
caseSwitchState.Open:
openValue=transform.forward+transform.position;
closeValue=Quaternion.AngleAxis(angle, transform.up) *transform.forward+transform.position;
break;
caseSwitchState.Close:
openValue=Quaternion.AngleAxis(angle, transform.up) *transform.forward+transform.position;
closeValue=transform.forward+transform.position;
break;
            }
        }
publicoverridevoidOpen()
        {
if (state==SwitchState.Open) return;
state=SwitchState.Open;
if (switchCoroutine!=null) StopCoroutine(switchCoroutine);
switchCoroutine=StartCoroutine(OpenCoroutine());
        }
publicoverridevoidClose()
        {
if (state==SwitchState.Close) return;
state=SwitchState.Close;
if (switchCoroutine!=null) StopCoroutine(switchCoroutine);
switchCoroutine=StartCoroutine(CloseCoroutine());
        }
privateIEnumeratorOpenCoroutine()
        {
floatbeginTime=Time.time;
QuaternionbeginRot=transform.rotation;
QuaterniontargetRot=Quaternion.LookRotation(openValue-transform.position, transform.up);
for (; (Time.time-beginTime) <duration;)
            {
floatt= (Time.time-beginTime) /duration;
transform.rotation=Quaternion.Lerp(beginRot, targetRot, t);
yieldreturnnull;
            }
transform.rotation=targetRot;
switchCoroutine=null;
        }
privateIEnumeratorCloseCoroutine()
        {
floatbeginTime=Time.time;
QuaternionbeginRot=transform.rotation;
QuaterniontargetRot=Quaternion.LookRotation(closeValue-transform.position, transform.up);
for (; (Time.time-beginTime) <duration;)
            {
floatt= (Time.time-beginTime) /duration;
transform.rotation=Quaternion.Lerp(beginRot, targetRot, t);
yieldreturnnull;
            }
transform.rotation=targetRot;
switchCoroutine=null;
        }
#ifUNITY_EDITORprivatevoidOnDrawGizmosSelected()
        {
if (!Application.isPlaying)
            {
switch (state)
                {
caseSwitchState.Open:
openValue=transform.forward+transform.position;
closeValue=Quaternion.AngleAxis(angle, transform.up) *transform.forward+transform.position;
break;
caseSwitchState.Close:
openValue=Quaternion.AngleAxis(angle, transform.up) *transform.forward+transform.position;
closeValue=transform.forward+transform.position;
break;
                }
            }
Handles.color=Color.cyan;
Handles.DrawWireCube(openValue, Vector3.one* .1f);
Handles.DrawWireCube(closeValue, Vector3.one* .1f);
Handles.DrawLine(transform.position, openValue);
Handles.DrawLine(transform.position, closeValue);
Handles.Label(openValue, "Open");
Handles.Label(closeValue, "Close");
        }
#endif    }
}

image.gif

image.gif

测试代码:

usingUnityEngine;
usingSK.Framework;
publicclassExample : MonoBehaviour{
    [SerializeField] privateSwitchableObjectdoor;
privatevoidOnGUI()
    {
if (GUILayout.Button("Open", GUILayout.Width(200f), GUILayout.Height(50f))) door.Open();
if (GUILayout.Button("Close", GUILayout.Width(200f), GUILayout.Height(50f))) door.Close();
if (GUILayout.Button("Switch", GUILayout.Width(200f), GUILayout.Height(50f))) door.Switch();
    }
}

image.gif

目录
相关文章
|
图形学
Unity 获取鼠标位置下的UGUI或3D物体
本文总结了两种检测方法,分别用于UGUI和3D物体的检测。第一种方法`GetOverUIobj`专门用于检测鼠标悬停的UGUI元素,通过`GraphicRaycaster`实现。第二种方法`GetOverWordGameObject`则同时适用于UI和3D物体检测,利用`PhysicsRaycaster`进行射线检测。两者均返回悬停对象或null。
|
图形学
unity 物体震动
在Unity中实现物体震动效果,主要通过改变物体的位置、旋转或缩放属性来模拟震动。以下是位置震动的实现原理及代码示例:通过随机生成微小偏移量并累加到物体位置上,在短时间内不断改变位置产生震动效果。生成随机偏移,并结合时间控制持续震动。
|
前端开发 图形学
unity UGUI跟随3D物体的坐标转换
在 Unity 中实现 UGUI 元素跟随 3D 物体,关键是将 3D 物体的世界坐标转换为屏幕或画布坐标。通过 Camera.WorldToScreenPoint 方法,可将 3D 物体位置映射到屏幕上,再更新 UGUI 元素的位置。代码示例展示了如何使用该方法,使 UGUI 图像跟随 3D 模型,并提供文字显示、图像和线条的显示/隐藏功能。
|
存储 图形学 索引
unity 使物体跟随路径点自动移动位置
在Unity中,物体沿路径点自动移动的核心原理是通过预设路径点,控制物体依次移动。路径点可用空对象或三维向量数组定义,并按顺序存储。移动时,计算当前位置与下一个路径点的向量差以确定方向,使用`Vector3.MoveTowards`逐步靠近目标点。代码实现包括路径点设置、移动控制及插值计算,确保物体平滑移动和旋转。
|
图形学
Unity 射线移动物体Ray
在Unity中,通过射线检测实现3D物体的拖拽和移动。射线由起点和方向组成,使用`Physics.Raycast`检测与物体的交点。点击物体时,记录位置偏移量,拖动过程中更新物体位置。代码包括基本拖拽和上下拖动功能,适用于正交摄像机场景。测试时为物体设置特定标签(如&quot;JQR&quot;)以便区分和操作。 示例代码展示了如何通过鼠标事件控制物体移动,并结合层级掩码优化射线检测。具体实现包括:点击选中物体、拖动更新位置、释放鼠标取消选择。此外,提供了上下拖动的额外功能,通过按键切换模式。
|
图形学 开发者
unity 从工具栏拖动生成物体
在 Unity 中实现从工具栏拖动生成物体的功能,基于编辑器扩展、事件系统和预制体实例化。通过自定义编辑器窗口、处理鼠标事件(按下、移动、释放)及使用 Instantiate 方法,可实现拖动并生成预制体物体。代码示例展示了如何检测鼠标事件并在指定位置实例化物体。
|
前端开发 图形学
Unity精华☀️UI和物体可见性的判断方法
Unity精华☀️UI和物体可见性的判断方法
小功能⭐️获取Unity游戏物体上,所挂载组件的名称
小功能⭐️获取Unity游戏物体上,所挂载组件的名称
小功能⭐️解决Unity无法对一个物体上的所有材质球进行更改
小功能⭐️解决Unity无法对一个物体上的所有材质球进行更改

热门文章

最新文章

下一篇
开通oss服务