本文介绍如何实现一个物体的开关控制系统,例如门的开关控制、灯的开关控制等,一切包含打开、关闭这两种状态的物体,均可以通过继承下面的抽象类进行重写实现。
状态枚举:
namespaceSK.Framework{ /// <summary>/// 状态/// </summary>publicenumSwitchState { /// <summary>/// 开着的/// </summary>Open, /// <summary>/// 关着的/// </summary>Close, } }
接口:
namespaceSK.Framework{ /// <summary>/// 可开关物体接口/// </summary>publicinterfaceISwitchableObject { SwitchStateState { get; } voidSwitch(); voidOpen(); voidClose(); } }
抽象类:
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(); } }
开关处理器,例如我们想要通过一个开关控制多个灯时,或者通过一个开关控制一对门时,均可以通过开关处理器,同时处理多个可开关物体:
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(); } } } }
这里以门的开关控制为例,我们将门的类型分为移动门和旋转门,首先创建一个门的基类:
usingUnityEngine; namespaceSK.Framework{ /// <summary>/// 可开关门/// </summary>publicabstractclassSwitchableDoor : SwitchableObject { //开/关所用的时长 [SerializeField] protectedfloatduration=0.5f; //打开状态的值protectedVector3openValue; //关闭状态的值protectedVector3closeValue; } }
1.移动门:
参数说明:
1.State: 门的默认状态(在场景中门是开着还是关着的);
2.Duration:开关门动作的时长;
3.Direction:门从默认状态到另一个状态的移动方向;
4.Magnitude:门从默认状态到另一状态移动的长度。
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 } }
2.旋转门:
参数说明:
1.State: 门的默认状态(在场景中门是开着还是关着的);
2.Duration:开关门动作的时长;
3.Angle:门从默认状态到另一个状态的旋转角度。
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 } }
测试代码:
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(); } }