本文介绍如何实现一个物体的开关控制系统,例如门的开关控制、灯的开关控制等,一切包含打开、关闭这两种状态的物体,均可以通过继承下面的抽象类进行重写实现。
状态枚举:
代码语言:javascript复制namespace SK.Framework
{
/// <summary>
/// 状态
/// </summary>
public enum SwitchState
{
/// <summary>
/// 开着的
/// </summary>
Open,
/// <summary>
/// 关着的
/// </summary>
Close,
}
}
接口:
代码语言:javascript复制namespace SK.Framework
{
/// <summary>
/// 可开关物体接口
/// </summary>
public interface ISwitchableObject
{
SwitchState State { get; }
void Switch();
void Open();
void Close();
}
}
抽象类:
代码语言:javascript复制using UnityEngine;
namespace SK.Framework
{
public abstract class SwitchableObject : MonoBehaviour, ISwitchableObject
{
//默认设为关闭状态
[SerializeField] protected SwitchState state = SwitchState.Close;
/// <summary>
/// 当前状态
/// </summary>
public SwitchState State { get { return state; } }
/// <summary>
/// 切换 若为打开状态则关闭 若为关闭状态则打开
/// </summary>
public void Switch()
{
switch (State)
{
case SwitchState.Open: Close(); break;
case SwitchState.Close: Open(); break;
}
}
/// <summary>
/// 开门
/// </summary>
public abstract void Open();
/// <summary>
/// 关门
/// </summary>
public abstract void Close();
}
}
开关处理器,例如我们想要通过一个开关控制多个灯时,或者通过一个开关控制一对门时,均可以通过开关处理器,同时处理多个可开关物体:
代码语言:javascript复制using UnityEngine;
namespace SK.Framework
{
/// <summary>
/// 开关处理器(把手)
/// </summary>
public class SwitchableObjectHandler : SwitchableObject
{
[SerializeField] private SwitchableObject[] handleArray;
public override void Open()
{
if (state == SwitchState.Open) return;
state = SwitchState.Open;
for (int i = 0; i < handleArray.Length; i )
{
handleArray[i].Open();
}
}
public override void Close()
{
if (state == SwitchState.Close) return;
state = SwitchState.Close;
for (int i = 0; i < handleArray.Length; i )
{
handleArray[i].Close();
}
}
}
}
这里以门的开关控制为例,我们将门的类型分为移动门和旋转门,首先创建一个门的基类:
代码语言:javascript复制using UnityEngine;
namespace SK.Framework
{
/// <summary>
/// 可开关门
/// </summary>
public abstract class SwitchableDoor : SwitchableObject
{
//开/关所用的时长
[SerializeField] protected float duration = 0.5f;
//打开状态的值
protected Vector3 openValue;
//关闭状态的值
protected Vector3 closeValue;
}
}
1.移动门:
参数说明:
1.State:门的默认状态(在场景中门是开着还是关着的);
2.Duration:开关门动作的时长;
3.Direction:门从默认状态到另一个状态的移动方向;
4.Magnitude:门从默认状态到另一状态移动的长度。
代码语言:javascript复制using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace SK.Framework
{
/// <summary>
/// 移动门
/// </summary>
public class MoveDoor : SwitchableDoor
{
[SerializeField] private Vector3 direction; //移动方向
[SerializeField] private float magnitude = 1f; //移动的长度
private Coroutine switchCoroutine;
private void Start()
{
switch (state)
{
case SwitchState.Open:
openValue = transform.position;
closeValue = transform.position direction.normalized * magnitude;
break;
case SwitchState.Close:
openValue = transform.position direction.normalized * magnitude;
closeValue = transform.position;
break;
}
}
public override void Open()
{
if (state == SwitchState.Open) return;
state = SwitchState.Open;
if (switchCoroutine != null) StopCoroutine(switchCoroutine);
switchCoroutine = StartCoroutine(OpenCoroutine());
}
public override void Close()
{
if (state == SwitchState.Close) return;
state = SwitchState.Close;
if (switchCoroutine != null) StopCoroutine(switchCoroutine);
switchCoroutine = StartCoroutine(CloseCoroutine());
}
private IEnumerator OpenCoroutine()
{
float beginTime = Time.time;
Vector3 beginPos = transform.position;
for (;(Time.time - beginTime) < duration;)
{
float t = (Time.time - beginTime) / duration;
transform.position = Vector3.Lerp(beginPos, openValue, t);
yield return null;
}
transform.position = openValue;
switchCoroutine = null;
}
private IEnumerator CloseCoroutine()
{
float beginTime = Time.time;
Vector3 beginPos = transform.position;
for (; (Time.time - beginTime) < duration;)
{
float t = (Time.time - beginTime) / duration;
transform.position = Vector3.Lerp(beginPos, closeValue, t);
yield return null;
}
transform.position = closeValue;
switchCoroutine = null;
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
switch (state)
{
case SwitchState.Open:
openValue = transform.position;
closeValue = transform.position direction.normalized * magnitude;
break;
case SwitchState.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
}
}
2.旋转门:
参数说明:
1.State:门的默认状态(在场景中门是开着还是关着的);
2.Duration:开关门动作的时长;
3.Angle:门从默认状态到另一个状态的旋转角度。
代码语言:javascript复制using UnityEngine;
using System.Collections;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace SK.Framework
{
/// <summary>
/// 旋转门
/// </summary>
public class RotateDoor : SwitchableDoor
{
[SerializeField] private float angle = 90f; //旋转角度
private Coroutine switchCoroutine;
private void Start()
{
switch (state)
{
case SwitchState.Open:
openValue = transform.forward transform.position;
closeValue = Quaternion.AngleAxis(angle, transform.up) * transform.forward transform.position;
break;
case SwitchState.Close:
openValue = Quaternion.AngleAxis(angle, transform.up) * transform.forward transform.position;
closeValue = transform.forward transform.position;
break;
}
}
public override void Open()
{
if (state == SwitchState.Open) return;
state = SwitchState.Open;
if (switchCoroutine != null) StopCoroutine(switchCoroutine);
switchCoroutine = StartCoroutine(OpenCoroutine());
}
public override void Close()
{
if (state == SwitchState.Close) return;
state = SwitchState.Close;
if (switchCoroutine != null) StopCoroutine(switchCoroutine);
switchCoroutine = StartCoroutine(CloseCoroutine());
}
private IEnumerator OpenCoroutine()
{
float beginTime = Time.time;
Quaternion beginRot = transform.rotation;
Quaternion targetRot = Quaternion.LookRotation(openValue - transform.position, transform.up);
for (; (Time.time - beginTime) < duration;)
{
float t = (Time.time - beginTime) / duration;
transform.rotation = Quaternion.Lerp(beginRot, targetRot, t);
yield return null;
}
transform.rotation = targetRot;
switchCoroutine = null;
}
private IEnumerator CloseCoroutine()
{
float beginTime = Time.time;
Quaternion beginRot = transform.rotation;
Quaternion targetRot = Quaternion.LookRotation(closeValue - transform.position, transform.up);
for (; (Time.time - beginTime) < duration;)
{
float t = (Time.time - beginTime) / duration;
transform.rotation = Quaternion.Lerp(beginRot, targetRot, t);
yield return null;
}
transform.rotation = targetRot;
switchCoroutine = null;
}
#if UNITY_EDITOR
private void OnDrawGizmosSelected()
{
if (!Application.isPlaying)
{
switch (state)
{
case SwitchState.Open:
openValue = transform.forward transform.position;
closeValue = Quaternion.AngleAxis(angle, transform.up) * transform.forward transform.position;
break;
case SwitchState.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
}
}
测试代码:
代码语言:javascript复制using UnityEngine;
using SK.Framework;
public class Example : MonoBehaviour
{
[SerializeField] private SwitchableObject door;
private void OnGUI()
{
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();
}
}