| @@ -0,0 +1,199 @@ | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using UnityEngine; | ||
| #if UNITY_EDITOR | ||
| using UnityEditor; | ||
| #endif | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class AutoMobileShaderSwitch : MonoBehaviour | ||
| { | ||
| [SerializeField] private ReplacementList m_ReplacementList; | ||
|
|
||
| // Use this for initialization | ||
| private void OnEnable() | ||
| { | ||
| #if UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 || UNITY_TIZEN || UNITY_STV | ||
| var renderers = FindObjectsOfType<Renderer>(); | ||
| Debug.Log (renderers.Length+" renderers"); | ||
| var oldMaterials = new List<Material>(); | ||
| var newMaterials = new List<Material>(); | ||
|
|
||
| int materialsReplaced = 0; | ||
| int materialInstancesReplaced = 0; | ||
|
|
||
| foreach(ReplacementDefinition replacementDef in m_ReplacementList.items) | ||
| { | ||
| foreach(var r in renderers) | ||
| { | ||
| Material[] modifiedMaterials = null; | ||
| for(int n=0; n<r.sharedMaterials.Length; ++n) | ||
| { | ||
| var material = r.sharedMaterials[n]; | ||
| if (material.shader == replacementDef.original) | ||
| { | ||
| if (modifiedMaterials == null) | ||
| { | ||
| modifiedMaterials = r.materials; | ||
| } | ||
| if (!oldMaterials.Contains(material)) | ||
| { | ||
| oldMaterials.Add(material); | ||
| Material newMaterial = (Material)Instantiate(material); | ||
| newMaterial.shader = replacementDef.replacement; | ||
| newMaterials.Add(newMaterial); | ||
| ++materialsReplaced; | ||
| } | ||
| Debug.Log ("replacing "+r.gameObject.name+" renderer "+n+" with "+newMaterials[oldMaterials.IndexOf(material)].name); | ||
| modifiedMaterials[n] = newMaterials[oldMaterials.IndexOf(material)]; | ||
| ++materialInstancesReplaced; | ||
| } | ||
| } | ||
| if (modifiedMaterials != null) | ||
| { | ||
| r.materials = modifiedMaterials; | ||
| } | ||
| } | ||
| } | ||
| Debug.Log (materialInstancesReplaced+" material instances replaced"); | ||
| Debug.Log (materialsReplaced+" materials replaced"); | ||
| for(int n=0; n<oldMaterials.Count; ++n) | ||
| { | ||
| Debug.Log (oldMaterials[n].name+" ("+oldMaterials[n].shader.name+")"+" replaced with "+newMaterials[n].name+" ("+newMaterials[n].shader.name+")"); | ||
| } | ||
| #endif | ||
| } | ||
|
|
||
|
|
||
| [Serializable] | ||
| public class ReplacementDefinition | ||
| { | ||
| public Shader original = null; | ||
| public Shader replacement = null; | ||
| } | ||
|
|
||
| [Serializable] | ||
| public class ReplacementList | ||
| { | ||
| public ReplacementDefinition[] items = new ReplacementDefinition[0]; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| namespace UnityStandardAssets.Utility.Inspector | ||
| { | ||
| #if UNITY_EDITOR | ||
| [CustomPropertyDrawer(typeof (AutoMobileShaderSwitch.ReplacementList))] | ||
| public class ReplacementListDrawer : PropertyDrawer | ||
| { | ||
| const float k_LineHeight = 18; | ||
| const float k_Spacing = 4; | ||
|
|
||
| public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) | ||
| { | ||
| EditorGUI.BeginProperty(position, label, property); | ||
|
|
||
| float x = position.x; | ||
| float y = position.y; | ||
| float inspectorWidth = position.width; | ||
|
|
||
| // Don't make child fields be indented | ||
| var indent = EditorGUI.indentLevel; | ||
| EditorGUI.indentLevel = 0; | ||
|
|
||
| var items = property.FindPropertyRelative("items"); | ||
| var titles = new string[] {"Original", "Replacement", ""}; | ||
| var props = new string[] {"original", "replacement", "-"}; | ||
| var widths = new float[] {.45f, .45f, .1f}; | ||
| const float lineHeight = 18; | ||
| bool changedLength = false; | ||
| if (items.arraySize > 0) | ||
| { | ||
| for (int i = -1; i < items.arraySize; ++i) | ||
| { | ||
| var item = items.GetArrayElementAtIndex(i); | ||
|
|
||
| float rowX = x; | ||
| for (int n = 0; n < props.Length; ++n) | ||
| { | ||
| float w = widths[n]*inspectorWidth; | ||
|
|
||
| // Calculate rects | ||
| Rect rect = new Rect(rowX, y, w, lineHeight); | ||
| rowX += w; | ||
|
|
||
| if (i == -1) | ||
| { | ||
| // draw title labels | ||
| EditorGUI.LabelField(rect, titles[n]); | ||
| } | ||
| else | ||
| { | ||
| if (props[n] == "-" || props[n] == "^" || props[n] == "v") | ||
| { | ||
| if (GUI.Button(rect, props[n])) | ||
| { | ||
| switch (props[n]) | ||
| { | ||
| case "-": | ||
| items.DeleteArrayElementAtIndex(i); | ||
| items.DeleteArrayElementAtIndex(i); | ||
| changedLength = true; | ||
| break; | ||
| case "v": | ||
| if (i > 0) | ||
| { | ||
| items.MoveArrayElement(i, i + 1); | ||
| } | ||
| break; | ||
| case "^": | ||
| if (i < items.arraySize - 1) | ||
| { | ||
| items.MoveArrayElement(i, i - 1); | ||
| } | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| else | ||
| { | ||
| SerializedProperty prop = item.FindPropertyRelative(props[n]); | ||
| EditorGUI.PropertyField(rect, prop, GUIContent.none); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| y += lineHeight + k_Spacing; | ||
| if (changedLength) | ||
| { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // add button | ||
| var addButtonRect = new Rect((x + position.width) - widths[widths.Length - 1]*inspectorWidth, y, | ||
| widths[widths.Length - 1]*inspectorWidth, lineHeight); | ||
| if (GUI.Button(addButtonRect, "+")) | ||
| { | ||
| items.InsertArrayElementAtIndex(items.arraySize); | ||
| } | ||
|
|
||
| y += lineHeight + k_Spacing; | ||
|
|
||
| // Set indent back to what it was | ||
| EditorGUI.indentLevel = indent; | ||
| EditorGUI.EndProperty(); | ||
| } | ||
|
|
||
|
|
||
| public override float GetPropertyHeight(SerializedProperty property, GUIContent label) | ||
| { | ||
| SerializedProperty items = property.FindPropertyRelative("items"); | ||
| float lineAndSpace = k_LineHeight + k_Spacing; | ||
| return 40 + (items.arraySize*lineAndSpace) + lineAndSpace; | ||
| } | ||
| } | ||
| #endif | ||
| } |
| @@ -0,0 +1,41 @@ | ||
| using System; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class AutoMoveAndRotate : MonoBehaviour | ||
| { | ||
| public Vector3andSpace moveUnitsPerSecond; | ||
| public Vector3andSpace rotateDegreesPerSecond; | ||
| public bool ignoreTimescale; | ||
| private float m_LastRealTime; | ||
|
|
||
|
|
||
| private void Start() | ||
| { | ||
| m_LastRealTime = Time.realtimeSinceStartup; | ||
| } | ||
|
|
||
|
|
||
| // Update is called once per frame | ||
| private void Update() | ||
| { | ||
| float deltaTime = Time.deltaTime; | ||
| if (ignoreTimescale) | ||
| { | ||
| deltaTime = (Time.realtimeSinceStartup - m_LastRealTime); | ||
| m_LastRealTime = Time.realtimeSinceStartup; | ||
| } | ||
| transform.Translate(moveUnitsPerSecond.value*deltaTime, moveUnitsPerSecond.space); | ||
| transform.Rotate(rotateDegreesPerSecond.value*deltaTime, moveUnitsPerSecond.space); | ||
| } | ||
|
|
||
|
|
||
| [Serializable] | ||
| public class Vector3andSpace | ||
| { | ||
| public Vector3 value; | ||
| public Space space = Space.Self; | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,58 @@ | ||
| using System; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class CameraRefocus | ||
| { | ||
| public Camera Camera; | ||
| public Vector3 Lookatpoint; | ||
| public Transform Parent; | ||
|
|
||
| private Vector3 m_OrigCameraPos; | ||
| private bool m_Refocus; | ||
|
|
||
|
|
||
| public CameraRefocus(Camera camera, Transform parent, Vector3 origCameraPos) | ||
| { | ||
| m_OrigCameraPos = origCameraPos; | ||
| Camera = camera; | ||
| Parent = parent; | ||
| } | ||
|
|
||
|
|
||
| public void ChangeCamera(Camera camera) | ||
| { | ||
| Camera = camera; | ||
| } | ||
|
|
||
|
|
||
| public void ChangeParent(Transform parent) | ||
| { | ||
| Parent = parent; | ||
| } | ||
|
|
||
|
|
||
| public void GetFocusPoint() | ||
| { | ||
| RaycastHit hitInfo; | ||
| if (Physics.Raycast(Parent.transform.position + m_OrigCameraPos, Parent.transform.forward, out hitInfo, | ||
| 100f)) | ||
| { | ||
| Lookatpoint = hitInfo.point; | ||
| m_Refocus = true; | ||
| return; | ||
| } | ||
| m_Refocus = false; | ||
| } | ||
|
|
||
|
|
||
| public void SetFocusPoint() | ||
| { | ||
| if (m_Refocus) | ||
| { | ||
| Camera.transform.LookAt(Lookatpoint); | ||
| } | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,54 @@ | ||
| using System; | ||
| using UnityEngine; | ||
|
|
||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| [Serializable] | ||
| public class CurveControlledBob | ||
| { | ||
| public float HorizontalBobRange = 0.33f; | ||
| public float VerticalBobRange = 0.33f; | ||
| public AnimationCurve Bobcurve = new AnimationCurve(new Keyframe(0f, 0f), new Keyframe(0.5f, 1f), | ||
| new Keyframe(1f, 0f), new Keyframe(1.5f, -1f), | ||
| new Keyframe(2f, 0f)); // sin curve for head bob | ||
| public float VerticaltoHorizontalRatio = 1f; | ||
|
|
||
| private float m_CyclePositionX; | ||
| private float m_CyclePositionY; | ||
| private float m_BobBaseInterval; | ||
| private Vector3 m_OriginalCameraPosition; | ||
| private float m_Time; | ||
|
|
||
|
|
||
| public void Setup(Camera camera, float bobBaseInterval) | ||
| { | ||
| m_BobBaseInterval = bobBaseInterval; | ||
| m_OriginalCameraPosition = camera.transform.localPosition; | ||
|
|
||
| // get the length of the curve in time | ||
| m_Time = Bobcurve[Bobcurve.length - 1].time; | ||
| } | ||
|
|
||
|
|
||
| public Vector3 DoHeadBob(float speed) | ||
| { | ||
| float xPos = m_OriginalCameraPosition.x + (Bobcurve.Evaluate(m_CyclePositionX)*HorizontalBobRange); | ||
| float yPos = m_OriginalCameraPosition.y + (Bobcurve.Evaluate(m_CyclePositionY)*VerticalBobRange); | ||
|
|
||
| m_CyclePositionX += (speed*Time.deltaTime)/m_BobBaseInterval; | ||
| m_CyclePositionY += ((speed*Time.deltaTime)/m_BobBaseInterval)*VerticaltoHorizontalRatio; | ||
|
|
||
| if (m_CyclePositionX > m_Time) | ||
| { | ||
| m_CyclePositionX = m_CyclePositionX - m_Time; | ||
| } | ||
| if (m_CyclePositionY > m_Time) | ||
| { | ||
| m_CyclePositionY = m_CyclePositionY - m_Time; | ||
| } | ||
|
|
||
| return new Vector3(xPos, yPos, 0f); | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,96 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class DragRigidbody : MonoBehaviour | ||
| { | ||
| const float k_Spring = 50.0f; | ||
| const float k_Damper = 5.0f; | ||
| const float k_Drag = 10.0f; | ||
| const float k_AngularDrag = 5.0f; | ||
| const float k_Distance = 0.2f; | ||
| const bool k_AttachToCenterOfMass = false; | ||
|
|
||
| private SpringJoint m_SpringJoint; | ||
|
|
||
|
|
||
| private void Update() | ||
| { | ||
| // Make sure the user pressed the mouse down | ||
| if (!Input.GetMouseButtonDown(0)) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var mainCamera = FindCamera(); | ||
|
|
||
| // We need to actually hit an object | ||
| RaycastHit hit = new RaycastHit(); | ||
| if ( | ||
| !Physics.Raycast(mainCamera.ScreenPointToRay(Input.mousePosition).origin, | ||
| mainCamera.ScreenPointToRay(Input.mousePosition).direction, out hit, 100, | ||
| Physics.DefaultRaycastLayers)) | ||
| { | ||
| return; | ||
| } | ||
| // We need to hit a rigidbody that is not kinematic | ||
| if (!hit.rigidbody || hit.rigidbody.isKinematic) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| if (!m_SpringJoint) | ||
| { | ||
| var go = new GameObject("Rigidbody dragger"); | ||
| Rigidbody body = go.AddComponent<Rigidbody>(); | ||
| m_SpringJoint = go.AddComponent<SpringJoint>(); | ||
| body.isKinematic = true; | ||
| } | ||
|
|
||
| m_SpringJoint.transform.position = hit.point; | ||
| m_SpringJoint.anchor = Vector3.zero; | ||
|
|
||
| m_SpringJoint.spring = k_Spring; | ||
| m_SpringJoint.damper = k_Damper; | ||
| m_SpringJoint.maxDistance = k_Distance; | ||
| m_SpringJoint.connectedBody = hit.rigidbody; | ||
|
|
||
| StartCoroutine("DragObject", hit.distance); | ||
| } | ||
|
|
||
|
|
||
| private IEnumerator DragObject(float distance) | ||
| { | ||
| var oldDrag = m_SpringJoint.connectedBody.drag; | ||
| var oldAngularDrag = m_SpringJoint.connectedBody.angularDrag; | ||
| m_SpringJoint.connectedBody.drag = k_Drag; | ||
| m_SpringJoint.connectedBody.angularDrag = k_AngularDrag; | ||
| var mainCamera = FindCamera(); | ||
| while (Input.GetMouseButton(0)) | ||
| { | ||
| var ray = mainCamera.ScreenPointToRay(Input.mousePosition); | ||
| m_SpringJoint.transform.position = ray.GetPoint(distance); | ||
| yield return null; | ||
| } | ||
| if (m_SpringJoint.connectedBody) | ||
| { | ||
| m_SpringJoint.connectedBody.drag = oldDrag; | ||
| m_SpringJoint.connectedBody.angularDrag = oldAngularDrag; | ||
| m_SpringJoint.connectedBody = null; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private Camera FindCamera() | ||
| { | ||
| if (GetComponent<Camera>()) | ||
| { | ||
| return GetComponent<Camera>(); | ||
| } | ||
|
|
||
| return Camera.main; | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,51 @@ | ||
| using System; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class DynamicShadowSettings : MonoBehaviour | ||
| { | ||
| public Light sunLight; | ||
| public float minHeight = 10; | ||
| public float minShadowDistance = 80; | ||
| public float minShadowBias = 1; | ||
| public float maxHeight = 1000; | ||
| public float maxShadowDistance = 10000; | ||
| public float maxShadowBias = 0.1f; | ||
| public float adaptTime = 1; | ||
|
|
||
| private float m_SmoothHeight; | ||
| private float m_ChangeSpeed; | ||
| private float m_OriginalStrength = 1; | ||
|
|
||
|
|
||
| private void Start() | ||
| { | ||
| m_OriginalStrength = sunLight.shadowStrength; | ||
| } | ||
|
|
||
|
|
||
| // Update is called once per frame | ||
| private void Update() | ||
| { | ||
| Ray ray = new Ray(Camera.main.transform.position, -Vector3.up); | ||
| RaycastHit hit; | ||
| float height = transform.position.y; | ||
| if (Physics.Raycast(ray, out hit)) | ||
| { | ||
| height = hit.distance; | ||
| } | ||
|
|
||
| if (Mathf.Abs(height - m_SmoothHeight) > 1) | ||
| { | ||
| m_SmoothHeight = Mathf.SmoothDamp(m_SmoothHeight, height, ref m_ChangeSpeed, adaptTime); | ||
| } | ||
|
|
||
| float i = Mathf.InverseLerp(minHeight, maxHeight, m_SmoothHeight); | ||
|
|
||
| QualitySettings.shadowDistance = Mathf.Lerp(minShadowDistance, maxShadowDistance, i); | ||
| sunLight.shadowBias = Mathf.Lerp(minShadowBias, maxShadowBias, 1 - ((1 - i)*(1 - i))); | ||
| sunLight.shadowStrength = Mathf.Lerp(m_OriginalStrength, 0, i); | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,21 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using UnityEngine; | ||
| using UnityEngine.EventSystems; | ||
|
|
||
| public class EventSystemChecker : MonoBehaviour | ||
| { | ||
| //public GameObject eventSystem; | ||
|
|
||
| // Use this for initialization | ||
| void Awake () | ||
| { | ||
| if(!FindObjectOfType<EventSystem>()) | ||
| { | ||
| //Instantiate(eventSystem); | ||
| GameObject obj = new GameObject("EventSystem"); | ||
| obj.AddComponent<EventSystem>(); | ||
| obj.AddComponent<StandaloneInputModule>().forceModuleActive = true; | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,73 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| [Serializable] | ||
| public class FOVKick | ||
| { | ||
| public Camera Camera; // optional camera setup, if null the main camera will be used | ||
| [HideInInspector] public float originalFov; // the original fov | ||
| public float FOVIncrease = 3f; // the amount the field of view increases when going into a run | ||
| public float TimeToIncrease = 1f; // the amount of time the field of view will increase over | ||
| public float TimeToDecrease = 1f; // the amount of time the field of view will take to return to its original size | ||
| public AnimationCurve IncreaseCurve; | ||
|
|
||
|
|
||
| public void Setup(Camera camera) | ||
| { | ||
| CheckStatus(camera); | ||
|
|
||
| Camera = camera; | ||
| originalFov = camera.fieldOfView; | ||
| } | ||
|
|
||
|
|
||
| private void CheckStatus(Camera camera) | ||
| { | ||
| if (camera == null) | ||
| { | ||
| throw new Exception("FOVKick camera is null, please supply the camera to the constructor"); | ||
| } | ||
|
|
||
| if (IncreaseCurve == null) | ||
| { | ||
| throw new Exception( | ||
| "FOVKick Increase curve is null, please define the curve for the field of view kicks"); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| public void ChangeCamera(Camera camera) | ||
| { | ||
| Camera = camera; | ||
| } | ||
|
|
||
|
|
||
| public IEnumerator FOVKickUp() | ||
| { | ||
| float t = Mathf.Abs((Camera.fieldOfView - originalFov)/FOVIncrease); | ||
| while (t < TimeToIncrease) | ||
| { | ||
| Camera.fieldOfView = originalFov + (IncreaseCurve.Evaluate(t/TimeToIncrease)*FOVIncrease); | ||
| t += Time.deltaTime; | ||
| yield return new WaitForEndOfFrame(); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| public IEnumerator FOVKickDown() | ||
| { | ||
| float t = Mathf.Abs((Camera.fieldOfView - originalFov)/FOVIncrease); | ||
| while (t > 0) | ||
| { | ||
| Camera.fieldOfView = originalFov + (IncreaseCurve.Evaluate(t/TimeToDecrease)*FOVIncrease); | ||
| t -= Time.deltaTime; | ||
| yield return new WaitForEndOfFrame(); | ||
| } | ||
| //make sure that fov returns to the original size | ||
| Camera.fieldOfView = originalFov; | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,38 @@ | ||
| using System; | ||
| using UnityEngine; | ||
| using UnityEngine.UI; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| [RequireComponent(typeof (Text))] | ||
| public class FPSCounter : MonoBehaviour | ||
| { | ||
| const float fpsMeasurePeriod = 0.5f; | ||
| private int m_FpsAccumulator = 0; | ||
| private float m_FpsNextPeriod = 0; | ||
| private int m_CurrentFps; | ||
| const string display = "{0} FPS"; | ||
| private Text m_Text; | ||
|
|
||
|
|
||
| private void Start() | ||
| { | ||
| m_FpsNextPeriod = Time.realtimeSinceStartup + fpsMeasurePeriod; | ||
| m_Text = GetComponent<Text>(); | ||
| } | ||
|
|
||
|
|
||
| private void Update() | ||
| { | ||
| // measure average frames per second | ||
| m_FpsAccumulator++; | ||
| if (Time.realtimeSinceStartup > m_FpsNextPeriod) | ||
| { | ||
| m_CurrentFps = (int) (m_FpsAccumulator/fpsMeasurePeriod); | ||
| m_FpsAccumulator = 0; | ||
| m_FpsNextPeriod += fpsMeasurePeriod; | ||
| m_Text.text = string.Format(display, m_CurrentFps); | ||
| } | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,18 @@ | ||
| using System; | ||
| using UnityEngine; | ||
|
|
||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class FollowTarget : MonoBehaviour | ||
| { | ||
| public Transform target; | ||
| public Vector3 offset = new Vector3(0f, 7.5f, 0f); | ||
|
|
||
|
|
||
| private void LateUpdate() | ||
| { | ||
| transform.position = target.position + offset; | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,18 @@ | ||
| using System; | ||
| using UnityEngine; | ||
| using UnityEngine.SceneManagement; | ||
| using UnityStandardAssets.CrossPlatformInput; | ||
|
|
||
| [RequireComponent(typeof (GUITexture))] | ||
| public class ForcedReset : MonoBehaviour | ||
| { | ||
| private void Update() | ||
| { | ||
| // if we have forced a reset ... | ||
| if (CrossPlatformInputManager.GetButtonDown("ResetObject")) | ||
| { | ||
| //... reload the scene | ||
| SceneManager.LoadScene(SceneManager.GetSceneAt(0).name); | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,45 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| [Serializable] | ||
| public class LerpControlledBob | ||
| { | ||
| public float BobDuration; | ||
| public float BobAmount; | ||
|
|
||
| private float m_Offset = 0f; | ||
|
|
||
|
|
||
| // provides the offset that can be used | ||
| public float Offset() | ||
| { | ||
| return m_Offset; | ||
| } | ||
|
|
||
|
|
||
| public IEnumerator DoBobCycle() | ||
| { | ||
| // make the camera move down slightly | ||
| float t = 0f; | ||
| while (t < BobDuration) | ||
| { | ||
| m_Offset = Mathf.Lerp(0f, BobAmount, t/BobDuration); | ||
| t += Time.deltaTime; | ||
| yield return new WaitForFixedUpdate(); | ||
| } | ||
|
|
||
| // make it move back to neutral | ||
| t = 0f; | ||
| while (t < BobDuration) | ||
| { | ||
| m_Offset = Mathf.Lerp(BobAmount, 0f, t/BobDuration); | ||
| t += Time.deltaTime; | ||
| yield return new WaitForFixedUpdate(); | ||
| } | ||
| m_Offset = 0f; | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,57 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using System.Collections.Generic; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class ObjectResetter : MonoBehaviour | ||
| { | ||
| private Vector3 originalPosition; | ||
| private Quaternion originalRotation; | ||
| private List<Transform> originalStructure; | ||
|
|
||
| private Rigidbody Rigidbody; | ||
|
|
||
| // Use this for initialization | ||
| private void Start() | ||
| { | ||
| originalStructure = new List<Transform>(GetComponentsInChildren<Transform>()); | ||
| originalPosition = transform.position; | ||
| originalRotation = transform.rotation; | ||
|
|
||
| Rigidbody = GetComponent<Rigidbody>(); | ||
| } | ||
|
|
||
|
|
||
| public void DelayedReset(float delay) | ||
| { | ||
| StartCoroutine(ResetCoroutine(delay)); | ||
| } | ||
|
|
||
|
|
||
| public IEnumerator ResetCoroutine(float delay) | ||
| { | ||
| yield return new WaitForSeconds(delay); | ||
|
|
||
| // remove any gameobjects added (fire, skid trails, etc) | ||
| foreach (var t in GetComponentsInChildren<Transform>()) | ||
| { | ||
| if (!originalStructure.Contains(t)) | ||
| { | ||
| t.parent = null; | ||
| } | ||
| } | ||
|
|
||
| transform.position = originalPosition; | ||
| transform.rotation = originalRotation; | ||
| if (Rigidbody) | ||
| { | ||
| Rigidbody.velocity = Vector3.zero; | ||
| Rigidbody.angularVelocity = Vector3.zero; | ||
| } | ||
|
|
||
| SendMessage("Reset"); | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,62 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using UnityEngine; | ||
| using Random = UnityEngine.Random; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class ParticleSystemDestroyer : MonoBehaviour | ||
| { | ||
| // allows a particle system to exist for a specified duration, | ||
| // then shuts off emission, and waits for all particles to expire | ||
| // before destroying the gameObject | ||
|
|
||
| public float minDuration = 8; | ||
| public float maxDuration = 10; | ||
|
|
||
| private float m_MaxLifetime; | ||
| private bool m_EarlyStop; | ||
|
|
||
|
|
||
| private IEnumerator Start() | ||
| { | ||
| var systems = GetComponentsInChildren<ParticleSystem>(); | ||
|
|
||
| // find out the maximum lifetime of any particles in this effect | ||
| foreach (var system in systems) | ||
| { | ||
| m_MaxLifetime = Mathf.Max(system.main.startLifetime.constant, m_MaxLifetime); | ||
| } | ||
|
|
||
| // wait for random duration | ||
|
|
||
| float stopTime = Time.time + Random.Range(minDuration, maxDuration); | ||
|
|
||
| while (Time.time < stopTime || m_EarlyStop) | ||
| { | ||
| yield return null; | ||
| } | ||
| Debug.Log("stopping " + name); | ||
|
|
||
| // turn off emission | ||
| foreach (var system in systems) | ||
| { | ||
| var emission = system.emission; | ||
| emission.enabled = false; | ||
| } | ||
| BroadcastMessage("Extinguish", SendMessageOptions.DontRequireReceiver); | ||
|
|
||
| // wait for any remaining particles to expire | ||
| yield return new WaitForSeconds(m_MaxLifetime); | ||
|
|
||
| Destroy(gameObject); | ||
| } | ||
|
|
||
|
|
||
| public void Stop() | ||
| { | ||
| // stops the particle system early | ||
| m_EarlyStop = true; | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,107 @@ | ||
| using System; | ||
| using UnityEngine; | ||
| #if UNITY_EDITOR | ||
| using UnityEditor; | ||
| #endif | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| #if UNITY_EDITOR | ||
|
|
||
| [ExecuteInEditMode] | ||
| #endif | ||
| public class PlatformSpecificContent : MonoBehaviour | ||
| { | ||
| private enum BuildTargetGroup | ||
| { | ||
| Standalone, | ||
| Mobile | ||
| } | ||
|
|
||
| [SerializeField] private BuildTargetGroup m_BuildTargetGroup; | ||
| [SerializeField] private GameObject[] m_Content = new GameObject[0]; | ||
| [SerializeField] private MonoBehaviour[] m_MonoBehaviours = new MonoBehaviour[0]; | ||
| [SerializeField] private bool m_ChildrenOfThisObject; | ||
|
|
||
| #if !UNITY_EDITOR | ||
| void OnEnable() | ||
| { | ||
| CheckEnableContent(); | ||
| } | ||
| #endif | ||
|
|
||
| #if UNITY_EDITOR | ||
|
|
||
| private void OnEnable() | ||
| { | ||
| EditorApplication.update += Update; | ||
| EditorUserBuildSettings.activeBuildTargetChanged += Update; | ||
| } | ||
|
|
||
|
|
||
| private void OnDisable() | ||
| { | ||
| EditorApplication.update -= Update; | ||
| EditorUserBuildSettings.activeBuildTargetChanged -= Update; | ||
| } | ||
|
|
||
| private void Update() | ||
| { | ||
| CheckEnableContent(); | ||
| } | ||
| #endif | ||
|
|
||
|
|
||
| private void CheckEnableContent() | ||
| { | ||
| #if (UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 || UNITY_TIZEN || UNITY_STV ) | ||
| if (m_BuildTargetGroup == BuildTargetGroup.Mobile) | ||
| { | ||
| EnableContent(true); | ||
| } else { | ||
| EnableContent(false); | ||
| } | ||
| #endif | ||
|
|
||
| #if !(UNITY_IPHONE || UNITY_ANDROID || UNITY_WP8 || UNITY_TIZEN || UNITY_STV ) | ||
| if (m_BuildTargetGroup == BuildTargetGroup.Mobile) | ||
| { | ||
| EnableContent(false); | ||
| } | ||
| else | ||
| { | ||
| EnableContent(true); | ||
| } | ||
| #endif | ||
| } | ||
|
|
||
|
|
||
| private void EnableContent(bool enabled) | ||
| { | ||
| if (m_Content.Length > 0) | ||
| { | ||
| foreach (var g in m_Content) | ||
| { | ||
| if (g != null) | ||
| { | ||
| g.SetActive(enabled); | ||
| } | ||
| } | ||
| } | ||
| if (m_ChildrenOfThisObject) | ||
| { | ||
| foreach (Transform t in transform) | ||
| { | ||
| t.gameObject.SetActive(enabled); | ||
| } | ||
| } | ||
| if (m_MonoBehaviours.Length > 0) | ||
| { | ||
| foreach (var monoBehaviour in m_MonoBehaviours) | ||
| { | ||
| monoBehaviour.enabled = enabled; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,183 @@ | ||
| %YAML 1.1 | ||
| %TAG !u! tag:unity3d.com,2011: | ||
| --- !u!1 &100000 | ||
| GameObject: | ||
| m_ObjectHideFlags: 0 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| serializedVersion: 4 | ||
| m_Component: | ||
| - 224: {fileID: 22409990} | ||
| - 223: {fileID: 22323452} | ||
| - 114: {fileID: 11403178} | ||
| - 114: {fileID: 11448042} | ||
| m_Layer: 5 | ||
| m_Name: FramerateCounter | ||
| m_TagString: Untagged | ||
| m_Icon: {fileID: 0} | ||
| m_NavMeshLayer: 0 | ||
| m_StaticEditorFlags: 0 | ||
| m_IsActive: 1 | ||
| --- !u!1 &167734 | ||
| GameObject: | ||
| m_ObjectHideFlags: 0 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| serializedVersion: 4 | ||
| m_Component: | ||
| - 224: {fileID: 22488988} | ||
| - 222: {fileID: 22250932} | ||
| - 114: {fileID: 11410038} | ||
| - 114: {fileID: 11400482} | ||
| m_Layer: 5 | ||
| m_Name: Text | ||
| m_TagString: Untagged | ||
| m_Icon: {fileID: 0} | ||
| m_NavMeshLayer: 0 | ||
| m_StaticEditorFlags: 0 | ||
| m_IsActive: 1 | ||
| --- !u!114 &11400482 | ||
| MonoBehaviour: | ||
| m_ObjectHideFlags: 1 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| m_GameObject: {fileID: 167734} | ||
| m_Enabled: 1 | ||
| m_EditorHideFlags: 0 | ||
| m_Script: {fileID: 11500000, guid: 22bbf57ec543cee42a5aa0ec2dd9e457, type: 3} | ||
| m_Name: | ||
| m_EditorClassIdentifier: | ||
| --- !u!114 &11403178 | ||
| MonoBehaviour: | ||
| m_ObjectHideFlags: 1 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| m_GameObject: {fileID: 100000} | ||
| m_Enabled: 1 | ||
| m_EditorHideFlags: 0 | ||
| m_Script: {fileID: 1980459831, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
| m_Name: | ||
| m_EditorClassIdentifier: | ||
| m_UiScaleMode: 0 | ||
| m_ReferencePixelsPerUnit: 100 | ||
| m_ScaleFactor: 1 | ||
| m_ReferenceResolution: {x: 800, y: 600} | ||
| m_ScreenMatchMode: 0 | ||
| m_MatchWidthOrHeight: 0 | ||
| m_PhysicalUnit: 3 | ||
| m_FallbackScreenDPI: 96 | ||
| m_DefaultSpriteDPI: 96 | ||
| m_DynamicPixelsPerUnit: 1 | ||
| --- !u!114 &11410038 | ||
| MonoBehaviour: | ||
| m_ObjectHideFlags: 1 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| m_GameObject: {fileID: 167734} | ||
| m_Enabled: 1 | ||
| m_EditorHideFlags: 0 | ||
| m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
| m_Name: | ||
| m_EditorClassIdentifier: | ||
| m_Material: {fileID: 0} | ||
| m_Color: {r: .196078435, g: .196078435, b: .196078435, a: 1} | ||
| m_FontData: | ||
| m_Font: {fileID: 12800000, guid: b51a3e520f9164da198dc59c8acfccd6, type: 3} | ||
| m_FontSize: 18 | ||
| m_FontStyle: 0 | ||
| m_BestFit: 0 | ||
| m_MinSize: 10 | ||
| m_MaxSize: 40 | ||
| m_Alignment: 4 | ||
| m_RichText: 0 | ||
| m_HorizontalOverflow: 0 | ||
| m_VerticalOverflow: 0 | ||
| m_LineSpacing: 1 | ||
| m_Text: 'FPS | ||
| ' | ||
| --- !u!114 &11448042 | ||
| MonoBehaviour: | ||
| m_ObjectHideFlags: 1 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| m_GameObject: {fileID: 100000} | ||
| m_Enabled: 1 | ||
| m_EditorHideFlags: 0 | ||
| m_Script: {fileID: 1301386320, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3} | ||
| m_Name: | ||
| m_EditorClassIdentifier: | ||
| m_IgnoreReversedGraphics: 1 | ||
| m_BlockingObjects: 0 | ||
| m_BlockingMask: | ||
| serializedVersion: 2 | ||
| m_Bits: 4294967295 | ||
| --- !u!222 &22250932 | ||
| CanvasRenderer: | ||
| m_ObjectHideFlags: 1 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| m_GameObject: {fileID: 167734} | ||
| --- !u!223 &22323452 | ||
| Canvas: | ||
| m_ObjectHideFlags: 1 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| m_GameObject: {fileID: 100000} | ||
| m_Enabled: 1 | ||
| serializedVersion: 2 | ||
| m_RenderMode: 0 | ||
| m_Camera: {fileID: 0} | ||
| m_PlaneDistance: 100 | ||
| m_PixelPerfect: 0 | ||
| m_ReceivesEvents: 1 | ||
| m_OverrideSorting: 0 | ||
| m_OverridePixelPerfect: 0 | ||
| m_SortingLayerID: 0 | ||
| m_SortingOrder: 0 | ||
| --- !u!224 &22409990 | ||
| RectTransform: | ||
| m_ObjectHideFlags: 1 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| m_GameObject: {fileID: 100000} | ||
| m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
| m_LocalPosition: {x: 0, y: 0, z: 0} | ||
| m_LocalScale: {x: 0, y: 0, z: 0} | ||
| m_Children: | ||
| - {fileID: 22488988} | ||
| m_Father: {fileID: 0} | ||
| m_RootOrder: 0 | ||
| m_AnchorMin: {x: 0, y: 0} | ||
| m_AnchorMax: {x: 0, y: 0} | ||
| m_AnchoredPosition: {x: 0, y: 0} | ||
| m_SizeDelta: {x: 0, y: 0} | ||
| m_Pivot: {x: 0, y: 0} | ||
| --- !u!224 &22488988 | ||
| RectTransform: | ||
| m_ObjectHideFlags: 1 | ||
| m_PrefabParentObject: {fileID: 0} | ||
| m_PrefabInternal: {fileID: 100100000} | ||
| m_GameObject: {fileID: 167734} | ||
| m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} | ||
| m_LocalPosition: {x: 0, y: 0, z: 0} | ||
| m_LocalScale: {x: 1, y: 1, z: 1} | ||
| m_Children: [] | ||
| m_Father: {fileID: 22409990} | ||
| m_RootOrder: 0 | ||
| m_AnchorMin: {x: .5, y: 1} | ||
| m_AnchorMax: {x: .5, y: 1} | ||
| m_AnchoredPosition: {x: 0, y: 0} | ||
| m_SizeDelta: {x: 160, y: 30} | ||
| m_Pivot: {x: .5, y: 1} | ||
| --- !u!1001 &100100000 | ||
| Prefab: | ||
| m_ObjectHideFlags: 1 | ||
| serializedVersion: 2 | ||
| m_Modification: | ||
| m_TransformParent: {fileID: 0} | ||
| m_Modifications: [] | ||
| m_RemovedComponents: [] | ||
| m_ParentPrefab: {fileID: 0} | ||
| m_RootGameObject: {fileID: 100000} | ||
| m_IsPrefabParent: 1 |
| @@ -0,0 +1,38 @@ | ||
| using System; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class SimpleActivatorMenu : MonoBehaviour | ||
| { | ||
| // An incredibly simple menu which, when given references | ||
| // to gameobjects in the scene | ||
| public GUIText camSwitchButton; | ||
| public GameObject[] objects; | ||
|
|
||
|
|
||
| private int m_CurrentActiveObject; | ||
|
|
||
|
|
||
| private void OnEnable() | ||
| { | ||
| // active object starts from first in array | ||
| m_CurrentActiveObject = 0; | ||
| camSwitchButton.text = objects[m_CurrentActiveObject].name; | ||
| } | ||
|
|
||
|
|
||
| public void NextCamera() | ||
| { | ||
| int nextactiveobject = m_CurrentActiveObject + 1 >= objects.Length ? 0 : m_CurrentActiveObject + 1; | ||
|
|
||
| for (int i = 0; i < objects.Length; i++) | ||
| { | ||
| objects[i].SetActive(i == nextactiveobject); | ||
| } | ||
|
|
||
| m_CurrentActiveObject = nextactiveobject; | ||
| camSwitchButton.text = objects[m_CurrentActiveObject].name; | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,113 @@ | ||
| using System; | ||
| using UnityEngine; | ||
| using UnityStandardAssets.CrossPlatformInput; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class SimpleMouseRotator : MonoBehaviour | ||
| { | ||
| // A mouselook behaviour with constraints which operate relative to | ||
| // this gameobject's initial rotation. | ||
| // Only rotates around local X and Y. | ||
| // Works in local coordinates, so if this object is parented | ||
| // to another moving gameobject, its local constraints will | ||
| // operate correctly | ||
| // (Think: looking out the side window of a car, or a gun turret | ||
| // on a moving spaceship with a limited angular range) | ||
| // to have no constraints on an axis, set the rotationRange to 360 or greater. | ||
| public Vector2 rotationRange = new Vector3(70, 70); | ||
| public float rotationSpeed = 10; | ||
| public float dampingTime = 0.2f; | ||
| public bool autoZeroVerticalOnMobile = true; | ||
| public bool autoZeroHorizontalOnMobile = false; | ||
| public bool relative = true; | ||
|
|
||
|
|
||
| private Vector3 m_TargetAngles; | ||
| private Vector3 m_FollowAngles; | ||
| private Vector3 m_FollowVelocity; | ||
| private Quaternion m_OriginalRotation; | ||
|
|
||
|
|
||
| private void Start() | ||
| { | ||
| m_OriginalRotation = transform.localRotation; | ||
| } | ||
|
|
||
|
|
||
| private void Update() | ||
| { | ||
| // we make initial calculations from the original local rotation | ||
| transform.localRotation = m_OriginalRotation; | ||
|
|
||
| // read input from mouse or mobile controls | ||
| float inputH; | ||
| float inputV; | ||
| if (relative) | ||
| { | ||
| inputH = CrossPlatformInputManager.GetAxis("Mouse X"); | ||
| inputV = CrossPlatformInputManager.GetAxis("Mouse Y"); | ||
|
|
||
| // wrap values to avoid springing quickly the wrong way from positive to negative | ||
| if (m_TargetAngles.y > 180) | ||
| { | ||
| m_TargetAngles.y -= 360; | ||
| m_FollowAngles.y -= 360; | ||
| } | ||
| if (m_TargetAngles.x > 180) | ||
| { | ||
| m_TargetAngles.x -= 360; | ||
| m_FollowAngles.x -= 360; | ||
| } | ||
| if (m_TargetAngles.y < -180) | ||
| { | ||
| m_TargetAngles.y += 360; | ||
| m_FollowAngles.y += 360; | ||
| } | ||
| if (m_TargetAngles.x < -180) | ||
| { | ||
| m_TargetAngles.x += 360; | ||
| m_FollowAngles.x += 360; | ||
| } | ||
|
|
||
| #if MOBILE_INPUT | ||
| // on mobile, sometimes we want input mapped directly to tilt value, | ||
| // so it springs back automatically when the look input is released. | ||
| if (autoZeroHorizontalOnMobile) { | ||
| m_TargetAngles.y = Mathf.Lerp (-rotationRange.y * 0.5f, rotationRange.y * 0.5f, inputH * .5f + .5f); | ||
| } else { | ||
| m_TargetAngles.y += inputH * rotationSpeed; | ||
| } | ||
| if (autoZeroVerticalOnMobile) { | ||
| m_TargetAngles.x = Mathf.Lerp (-rotationRange.x * 0.5f, rotationRange.x * 0.5f, inputV * .5f + .5f); | ||
| } else { | ||
| m_TargetAngles.x += inputV * rotationSpeed; | ||
| } | ||
| #else | ||
| // with mouse input, we have direct control with no springback required. | ||
| m_TargetAngles.y += inputH*rotationSpeed; | ||
| m_TargetAngles.x += inputV*rotationSpeed; | ||
| #endif | ||
|
|
||
| // clamp values to allowed range | ||
| m_TargetAngles.y = Mathf.Clamp(m_TargetAngles.y, -rotationRange.y*0.5f, rotationRange.y*0.5f); | ||
| m_TargetAngles.x = Mathf.Clamp(m_TargetAngles.x, -rotationRange.x*0.5f, rotationRange.x*0.5f); | ||
| } | ||
| else | ||
| { | ||
| inputH = Input.mousePosition.x; | ||
| inputV = Input.mousePosition.y; | ||
|
|
||
| // set values to allowed range | ||
| m_TargetAngles.y = Mathf.Lerp(-rotationRange.y*0.5f, rotationRange.y*0.5f, inputH/Screen.width); | ||
| m_TargetAngles.x = Mathf.Lerp(-rotationRange.x*0.5f, rotationRange.x*0.5f, inputV/Screen.height); | ||
| } | ||
|
|
||
| // smoothly interpolate current values to target angles | ||
| m_FollowAngles = Vector3.SmoothDamp(m_FollowAngles, m_TargetAngles, ref m_FollowVelocity, dampingTime); | ||
|
|
||
| // update the actual gameobject's rotation | ||
| transform.localRotation = m_OriginalRotation*Quaternion.Euler(-m_FollowAngles.x, m_FollowAngles.y, 0); | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,61 @@ | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class SmoothFollow : MonoBehaviour | ||
| { | ||
|
|
||
| // The target we are following | ||
| [SerializeField] | ||
| private Transform target; | ||
| // The distance in the x-z plane to the target | ||
| [SerializeField] | ||
| private float distance = 10.0f; | ||
| // the height we want the camera to be above the target | ||
| [SerializeField] | ||
| private float height = 5.0f; | ||
|
|
||
| [SerializeField] | ||
| private float rotationDamping; | ||
| [SerializeField] | ||
| private float heightDamping; | ||
|
|
||
| // Use this for initialization | ||
| void Start() { } | ||
|
|
||
| // Update is called once per frame | ||
| void LateUpdate() | ||
| { | ||
| // Early out if we don't have a target | ||
| if (!target) | ||
| return; | ||
|
|
||
| // Calculate the current rotation angles | ||
| var wantedRotationAngle = target.eulerAngles.y; | ||
| var wantedHeight = target.position.y + height; | ||
|
|
||
| var currentRotationAngle = transform.eulerAngles.y; | ||
| var currentHeight = transform.position.y; | ||
|
|
||
| // Damp the rotation around the y-axis | ||
| currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotationDamping * Time.deltaTime); | ||
|
|
||
| // Damp the height | ||
| currentHeight = Mathf.Lerp(currentHeight, wantedHeight, heightDamping * Time.deltaTime); | ||
|
|
||
| // Convert the angle into a rotation | ||
| var currentRotation = Quaternion.Euler(0, currentRotationAngle, 0); | ||
|
|
||
| // Set the position of the camera on the x-z plane to: | ||
| // distance meters behind the target | ||
| transform.position = target.position; | ||
| transform.position -= currentRotation * Vector3.forward * distance; | ||
|
|
||
| // Set the height of the camera | ||
| transform.position = new Vector3(transform.position.x ,currentHeight , transform.position.z); | ||
|
|
||
| // Always look at the target | ||
| transform.LookAt(target); | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,216 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using UnityEngine; | ||
| using UnityEngine.SceneManagement; | ||
| #if UNITY_EDITOR | ||
| using UnityEditor; | ||
| #endif | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class TimedObjectActivator : MonoBehaviour | ||
| { | ||
| public enum Action | ||
| { | ||
| Activate, | ||
| Deactivate, | ||
| Destroy, | ||
| ReloadLevel, | ||
| Call, | ||
| } | ||
|
|
||
|
|
||
| [Serializable] | ||
| public class Entry | ||
| { | ||
| public GameObject target; | ||
| public Action action; | ||
| public float delay; | ||
| } | ||
|
|
||
|
|
||
| [Serializable] | ||
| public class Entries | ||
| { | ||
| public Entry[] entries; | ||
| } | ||
|
|
||
|
|
||
| public Entries entries = new Entries(); | ||
|
|
||
|
|
||
| private void Awake() | ||
| { | ||
| foreach (Entry entry in entries.entries) | ||
| { | ||
| switch (entry.action) | ||
| { | ||
| case Action.Activate: | ||
| StartCoroutine(Activate(entry)); | ||
| break; | ||
| case Action.Deactivate: | ||
| StartCoroutine(Deactivate(entry)); | ||
| break; | ||
| case Action.Destroy: | ||
| Destroy(entry.target, entry.delay); | ||
| break; | ||
|
|
||
| case Action.ReloadLevel: | ||
| StartCoroutine(ReloadLevel(entry)); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private IEnumerator Activate(Entry entry) | ||
| { | ||
| yield return new WaitForSeconds(entry.delay); | ||
| entry.target.SetActive(true); | ||
| } | ||
|
|
||
|
|
||
| private IEnumerator Deactivate(Entry entry) | ||
| { | ||
| yield return new WaitForSeconds(entry.delay); | ||
| entry.target.SetActive(false); | ||
| } | ||
|
|
||
|
|
||
| private IEnumerator ReloadLevel(Entry entry) | ||
| { | ||
| yield return new WaitForSeconds(entry.delay); | ||
| SceneManager.LoadScene(SceneManager.GetSceneAt(0).name); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| namespace UnityStandardAssets.Utility.Inspector | ||
| { | ||
| #if UNITY_EDITOR | ||
| [CustomPropertyDrawer(typeof (TimedObjectActivator.Entries))] | ||
| public class EntriesDrawer : PropertyDrawer | ||
| { | ||
| private const float k_LineHeight = 18; | ||
| private const float k_Spacing = 4; | ||
|
|
||
|
|
||
| public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) | ||
| { | ||
| EditorGUI.BeginProperty(position, label, property); | ||
|
|
||
| float x = position.x; | ||
| float y = position.y; | ||
| float width = position.width; | ||
|
|
||
| // Draw label | ||
| EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); | ||
|
|
||
| // Don't make child fields be indented | ||
| var indent = EditorGUI.indentLevel; | ||
| EditorGUI.indentLevel = 0; | ||
|
|
||
| var entries = property.FindPropertyRelative("entries"); | ||
|
|
||
| if (entries.arraySize > 0) | ||
| { | ||
| float actionWidth = .25f*width; | ||
| float targetWidth = .6f*width; | ||
| float delayWidth = .1f*width; | ||
| float buttonWidth = .05f*width; | ||
|
|
||
| for (int i = 0; i < entries.arraySize; ++i) | ||
| { | ||
| y += k_LineHeight + k_Spacing; | ||
|
|
||
| var entry = entries.GetArrayElementAtIndex(i); | ||
|
|
||
| float rowX = x; | ||
|
|
||
| // Calculate rects | ||
| Rect actionRect = new Rect(rowX, y, actionWidth, k_LineHeight); | ||
| rowX += actionWidth; | ||
|
|
||
| Rect targetRect = new Rect(rowX, y, targetWidth, k_LineHeight); | ||
| rowX += targetWidth; | ||
|
|
||
| Rect delayRect = new Rect(rowX, y, delayWidth, k_LineHeight); | ||
| rowX += delayWidth; | ||
|
|
||
| Rect buttonRect = new Rect(rowX, y, buttonWidth, k_LineHeight); | ||
| rowX += buttonWidth; | ||
|
|
||
| // Draw fields - passs GUIContent.none to each so they are drawn without labels | ||
|
|
||
| if (entry.FindPropertyRelative("action").enumValueIndex != | ||
| (int) TimedObjectActivator.Action.ReloadLevel) | ||
| { | ||
| EditorGUI.PropertyField(actionRect, entry.FindPropertyRelative("action"), GUIContent.none); | ||
| EditorGUI.PropertyField(targetRect, entry.FindPropertyRelative("target"), GUIContent.none); | ||
| } | ||
| else | ||
| { | ||
| actionRect.width = actionRect.width + targetRect.width; | ||
| EditorGUI.PropertyField(actionRect, entry.FindPropertyRelative("action"), GUIContent.none); | ||
| } | ||
|
|
||
| EditorGUI.PropertyField(delayRect, entry.FindPropertyRelative("delay"), GUIContent.none); | ||
| if (GUI.Button(buttonRect, "-")) | ||
| { | ||
| entries.DeleteArrayElementAtIndex(i); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // add & sort buttons | ||
| y += k_LineHeight + k_Spacing; | ||
|
|
||
| var addButtonRect = new Rect(position.x + position.width - 120, y, 60, k_LineHeight); | ||
| if (GUI.Button(addButtonRect, "Add")) | ||
| { | ||
| entries.InsertArrayElementAtIndex(entries.arraySize); | ||
| } | ||
|
|
||
| var sortButtonRect = new Rect(position.x + position.width - 60, y, 60, k_LineHeight); | ||
| if (GUI.Button(sortButtonRect, "Sort")) | ||
| { | ||
| bool changed = true; | ||
| while (entries.arraySize > 1 && changed) | ||
| { | ||
| changed = false; | ||
| for (int i = 0; i < entries.arraySize - 1; ++i) | ||
| { | ||
| var e1 = entries.GetArrayElementAtIndex(i); | ||
| var e2 = entries.GetArrayElementAtIndex(i + 1); | ||
|
|
||
| if (e1.FindPropertyRelative("delay").floatValue > e2.FindPropertyRelative("delay").floatValue) | ||
| { | ||
| entries.MoveArrayElement(i + 1, i); | ||
| changed = true; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| // Set indent back to what it was | ||
| EditorGUI.indentLevel = indent; | ||
| // | ||
|
|
||
|
|
||
| EditorGUI.EndProperty(); | ||
| } | ||
|
|
||
|
|
||
| public override float GetPropertyHeight(SerializedProperty property, GUIContent label) | ||
| { | ||
| SerializedProperty entries = property.FindPropertyRelative("entries"); | ||
| float lineAndSpace = k_LineHeight + k_Spacing; | ||
| return 40 + (entries.arraySize*lineAndSpace) + lineAndSpace; | ||
| } | ||
| } | ||
| #endif | ||
| } |
| @@ -0,0 +1,27 @@ | ||
| using System; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class TimedObjectDestructor : MonoBehaviour | ||
| { | ||
| [SerializeField] private float m_TimeOut = 1.0f; | ||
| [SerializeField] private bool m_DetachChildren = false; | ||
|
|
||
|
|
||
| private void Awake() | ||
| { | ||
| Invoke("DestroyNow", m_TimeOut); | ||
| } | ||
|
|
||
|
|
||
| private void DestroyNow() | ||
| { | ||
| if (m_DetachChildren) | ||
| { | ||
| transform.DetachChildren(); | ||
| } | ||
| DestroyObject(gameObject); | ||
| } | ||
| } | ||
| } |
| @@ -0,0 +1,384 @@ | ||
| using System; | ||
| using System.Collections; | ||
| using UnityEngine; | ||
| #if UNITY_EDITOR | ||
| using UnityEditor; | ||
|
|
||
| #endif | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class WaypointCircuit : MonoBehaviour | ||
| { | ||
| public WaypointList waypointList = new WaypointList(); | ||
| [SerializeField] private bool smoothRoute = true; | ||
| private int numPoints; | ||
| private Vector3[] points; | ||
| private float[] distances; | ||
|
|
||
| public float editorVisualisationSubsteps = 100; | ||
| public float Length { get; private set; } | ||
|
|
||
| public Transform[] Waypoints | ||
| { | ||
| get { return waypointList.items; } | ||
| } | ||
|
|
||
| //this being here will save GC allocs | ||
| private int p0n; | ||
| private int p1n; | ||
| private int p2n; | ||
| private int p3n; | ||
|
|
||
| private float i; | ||
| private Vector3 P0; | ||
| private Vector3 P1; | ||
| private Vector3 P2; | ||
| private Vector3 P3; | ||
|
|
||
| // Use this for initialization | ||
| private void Awake() | ||
| { | ||
| if (Waypoints.Length > 1) | ||
| { | ||
| CachePositionsAndDistances(); | ||
| } | ||
| numPoints = Waypoints.Length; | ||
| } | ||
|
|
||
|
|
||
| public RoutePoint GetRoutePoint(float dist) | ||
| { | ||
| // position and direction | ||
| Vector3 p1 = GetRoutePosition(dist); | ||
| Vector3 p2 = GetRoutePosition(dist + 0.1f); | ||
| Vector3 delta = p2 - p1; | ||
| return new RoutePoint(p1, delta.normalized); | ||
| } | ||
|
|
||
|
|
||
| public Vector3 GetRoutePosition(float dist) | ||
| { | ||
| int point = 0; | ||
|
|
||
| if (Length == 0) | ||
| { | ||
| Length = distances[distances.Length - 1]; | ||
| } | ||
|
|
||
| dist = Mathf.Repeat(dist, Length); | ||
|
|
||
| while (distances[point] < dist) | ||
| { | ||
| ++point; | ||
| } | ||
|
|
||
|
|
||
| // get nearest two points, ensuring points wrap-around start & end of circuit | ||
| p1n = ((point - 1) + numPoints)%numPoints; | ||
| p2n = point; | ||
|
|
||
| // found point numbers, now find interpolation value between the two middle points | ||
|
|
||
| i = Mathf.InverseLerp(distances[p1n], distances[p2n], dist); | ||
|
|
||
| if (smoothRoute) | ||
| { | ||
| // smooth catmull-rom calculation between the two relevant points | ||
|
|
||
|
|
||
| // get indices for the surrounding 2 points, because | ||
| // four points are required by the catmull-rom function | ||
| p0n = ((point - 2) + numPoints)%numPoints; | ||
| p3n = (point + 1)%numPoints; | ||
|
|
||
| // 2nd point may have been the 'last' point - a dupe of the first, | ||
| // (to give a value of max track distance instead of zero) | ||
| // but now it must be wrapped back to zero if that was the case. | ||
| p2n = p2n%numPoints; | ||
|
|
||
| P0 = points[p0n]; | ||
| P1 = points[p1n]; | ||
| P2 = points[p2n]; | ||
| P3 = points[p3n]; | ||
|
|
||
| return CatmullRom(P0, P1, P2, P3, i); | ||
| } | ||
| else | ||
| { | ||
| // simple linear lerp between the two points: | ||
|
|
||
| p1n = ((point - 1) + numPoints)%numPoints; | ||
| p2n = point; | ||
|
|
||
| return Vector3.Lerp(points[p1n], points[p2n], i); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float i) | ||
| { | ||
| // comments are no use here... it's the catmull-rom equation. | ||
| // Un-magic this, lord vector! | ||
| return 0.5f* | ||
| ((2*p1) + (-p0 + p2)*i + (2*p0 - 5*p1 + 4*p2 - p3)*i*i + | ||
| (-p0 + 3*p1 - 3*p2 + p3)*i*i*i); | ||
| } | ||
|
|
||
|
|
||
| private void CachePositionsAndDistances() | ||
| { | ||
| // transfer the position of each point and distances between points to arrays for | ||
| // speed of lookup at runtime | ||
| points = new Vector3[Waypoints.Length + 1]; | ||
| distances = new float[Waypoints.Length + 1]; | ||
|
|
||
| float accumulateDistance = 0; | ||
| for (int i = 0; i < points.Length; ++i) | ||
| { | ||
| var t1 = Waypoints[(i)%Waypoints.Length]; | ||
| var t2 = Waypoints[(i + 1)%Waypoints.Length]; | ||
| if (t1 != null && t2 != null) | ||
| { | ||
| Vector3 p1 = t1.position; | ||
| Vector3 p2 = t2.position; | ||
| points[i] = Waypoints[i%Waypoints.Length].position; | ||
| distances[i] = accumulateDistance; | ||
| accumulateDistance += (p1 - p2).magnitude; | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private void OnDrawGizmos() | ||
| { | ||
| DrawGizmos(false); | ||
| } | ||
|
|
||
|
|
||
| private void OnDrawGizmosSelected() | ||
| { | ||
| DrawGizmos(true); | ||
| } | ||
|
|
||
|
|
||
| private void DrawGizmos(bool selected) | ||
| { | ||
| waypointList.circuit = this; | ||
| if (Waypoints.Length > 1) | ||
| { | ||
| numPoints = Waypoints.Length; | ||
|
|
||
| CachePositionsAndDistances(); | ||
| Length = distances[distances.Length - 1]; | ||
|
|
||
| Gizmos.color = selected ? Color.yellow : new Color(1, 1, 0, 0.5f); | ||
| Vector3 prev = Waypoints[0].position; | ||
| if (smoothRoute) | ||
| { | ||
| for (float dist = 0; dist < Length; dist += Length/editorVisualisationSubsteps) | ||
| { | ||
| Vector3 next = GetRoutePosition(dist + 1); | ||
| Gizmos.DrawLine(prev, next); | ||
| prev = next; | ||
| } | ||
| Gizmos.DrawLine(prev, Waypoints[0].position); | ||
| } | ||
| else | ||
| { | ||
| for (int n = 0; n < Waypoints.Length; ++n) | ||
| { | ||
| Vector3 next = Waypoints[(n + 1)%Waypoints.Length].position; | ||
| Gizmos.DrawLine(prev, next); | ||
| prev = next; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
||
| [Serializable] | ||
| public class WaypointList | ||
| { | ||
| public WaypointCircuit circuit; | ||
| public Transform[] items = new Transform[0]; | ||
| } | ||
|
|
||
| public struct RoutePoint | ||
| { | ||
| public Vector3 position; | ||
| public Vector3 direction; | ||
|
|
||
|
|
||
| public RoutePoint(Vector3 position, Vector3 direction) | ||
| { | ||
| this.position = position; | ||
| this.direction = direction; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| namespace UnityStandardAssets.Utility.Inspector | ||
| { | ||
| #if UNITY_EDITOR | ||
| [CustomPropertyDrawer(typeof (WaypointCircuit.WaypointList))] | ||
| public class WaypointListDrawer : PropertyDrawer | ||
| { | ||
| private float lineHeight = 18; | ||
| private float spacing = 4; | ||
|
|
||
|
|
||
| public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) | ||
| { | ||
| EditorGUI.BeginProperty(position, label, property); | ||
|
|
||
| float x = position.x; | ||
| float y = position.y; | ||
| float inspectorWidth = position.width; | ||
|
|
||
| // Draw label | ||
|
|
||
|
|
||
| // Don't make child fields be indented | ||
| var indent = EditorGUI.indentLevel; | ||
| EditorGUI.indentLevel = 0; | ||
|
|
||
| var items = property.FindPropertyRelative("items"); | ||
| var titles = new string[] {"Transform", "", "", ""}; | ||
| var props = new string[] {"transform", "^", "v", "-"}; | ||
| var widths = new float[] {.7f, .1f, .1f, .1f}; | ||
| float lineHeight = 18; | ||
| bool changedLength = false; | ||
| if (items.arraySize > 0) | ||
| { | ||
| for (int i = -1; i < items.arraySize; ++i) | ||
| { | ||
| var item = items.GetArrayElementAtIndex(i); | ||
|
|
||
| float rowX = x; | ||
| for (int n = 0; n < props.Length; ++n) | ||
| { | ||
| float w = widths[n]*inspectorWidth; | ||
|
|
||
| // Calculate rects | ||
| Rect rect = new Rect(rowX, y, w, lineHeight); | ||
| rowX += w; | ||
|
|
||
| if (i == -1) | ||
| { | ||
| EditorGUI.LabelField(rect, titles[n]); | ||
| } | ||
| else | ||
| { | ||
| if (n == 0) | ||
| { | ||
| EditorGUI.ObjectField(rect, item.objectReferenceValue, typeof (Transform), true); | ||
| } | ||
| else | ||
| { | ||
| if (GUI.Button(rect, props[n])) | ||
| { | ||
| switch (props[n]) | ||
| { | ||
| case "-": | ||
| items.DeleteArrayElementAtIndex(i); | ||
| items.DeleteArrayElementAtIndex(i); | ||
| changedLength = true; | ||
| break; | ||
| case "v": | ||
| if (i > 0) | ||
| { | ||
| items.MoveArrayElement(i, i + 1); | ||
| } | ||
| break; | ||
| case "^": | ||
| if (i < items.arraySize - 1) | ||
| { | ||
| items.MoveArrayElement(i, i - 1); | ||
| } | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| y += lineHeight + spacing; | ||
| if (changedLength) | ||
| { | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| else | ||
| { | ||
| // add button | ||
| var addButtonRect = new Rect((x + position.width) - widths[widths.Length - 1]*inspectorWidth, y, | ||
| widths[widths.Length - 1]*inspectorWidth, lineHeight); | ||
| if (GUI.Button(addButtonRect, "+")) | ||
| { | ||
| items.InsertArrayElementAtIndex(items.arraySize); | ||
| } | ||
|
|
||
| y += lineHeight + spacing; | ||
| } | ||
|
|
||
| // add all button | ||
| var addAllButtonRect = new Rect(x, y, inspectorWidth, lineHeight); | ||
| if (GUI.Button(addAllButtonRect, "Assign using all child objects")) | ||
| { | ||
| var circuit = property.FindPropertyRelative("circuit").objectReferenceValue as WaypointCircuit; | ||
| var children = new Transform[circuit.transform.childCount]; | ||
| int n = 0; | ||
| foreach (Transform child in circuit.transform) | ||
| { | ||
| children[n++] = child; | ||
| } | ||
| Array.Sort(children, new TransformNameComparer()); | ||
| circuit.waypointList.items = new Transform[children.Length]; | ||
| for (n = 0; n < children.Length; ++n) | ||
| { | ||
| circuit.waypointList.items[n] = children[n]; | ||
| } | ||
| } | ||
| y += lineHeight + spacing; | ||
|
|
||
| // rename all button | ||
| var renameButtonRect = new Rect(x, y, inspectorWidth, lineHeight); | ||
| if (GUI.Button(renameButtonRect, "Auto Rename numerically from this order")) | ||
| { | ||
| var circuit = property.FindPropertyRelative("circuit").objectReferenceValue as WaypointCircuit; | ||
| int n = 0; | ||
| foreach (Transform child in circuit.waypointList.items) | ||
| { | ||
| child.name = "Waypoint " + (n++).ToString("000"); | ||
| } | ||
| } | ||
| y += lineHeight + spacing; | ||
|
|
||
| // Set indent back to what it was | ||
| EditorGUI.indentLevel = indent; | ||
| EditorGUI.EndProperty(); | ||
| } | ||
|
|
||
|
|
||
| public override float GetPropertyHeight(SerializedProperty property, GUIContent label) | ||
| { | ||
| SerializedProperty items = property.FindPropertyRelative("items"); | ||
| float lineAndSpace = lineHeight + spacing; | ||
| return 40 + (items.arraySize*lineAndSpace) + lineAndSpace; | ||
| } | ||
|
|
||
|
|
||
| // comparer for check distances in ray cast hits | ||
| public class TransformNameComparer : IComparer | ||
| { | ||
| public int Compare(object x, object y) | ||
| { | ||
| return ((Transform) x).name.CompareTo(((Transform) y).name); | ||
| } | ||
| } | ||
| } | ||
| #endif | ||
| } |
| @@ -0,0 +1,152 @@ | ||
| using System; | ||
| using UnityEngine; | ||
|
|
||
| namespace UnityStandardAssets.Utility | ||
| { | ||
| public class WaypointProgressTracker : MonoBehaviour | ||
| { | ||
| // This script can be used with any object that is supposed to follow a | ||
| // route marked out by waypoints. | ||
|
|
||
| // This script manages the amount to look ahead along the route, | ||
| // and keeps track of progress and laps. | ||
|
|
||
| [SerializeField] private WaypointCircuit circuit; // A reference to the waypoint-based route we should follow | ||
|
|
||
| [SerializeField] private float lookAheadForTargetOffset = 5; | ||
| // The offset ahead along the route that the we will aim for | ||
|
|
||
| [SerializeField] private float lookAheadForTargetFactor = .1f; | ||
| // A multiplier adding distance ahead along the route to aim for, based on current speed | ||
|
|
||
| [SerializeField] private float lookAheadForSpeedOffset = 10; | ||
| // The offset ahead only the route for speed adjustments (applied as the rotation of the waypoint target transform) | ||
|
|
||
| [SerializeField] private float lookAheadForSpeedFactor = .2f; | ||
| // A multiplier adding distance ahead along the route for speed adjustments | ||
|
|
||
| [SerializeField] private ProgressStyle progressStyle = ProgressStyle.SmoothAlongRoute; | ||
| // whether to update the position smoothly along the route (good for curved paths) or just when we reach each waypoint. | ||
|
|
||
| [SerializeField] private float pointToPointThreshold = 4; | ||
| // proximity to waypoint which must be reached to switch target to next waypoint : only used in PointToPoint mode. | ||
|
|
||
| public enum ProgressStyle | ||
| { | ||
| SmoothAlongRoute, | ||
| PointToPoint, | ||
| } | ||
|
|
||
| // these are public, readable by other objects - i.e. for an AI to know where to head! | ||
| public WaypointCircuit.RoutePoint targetPoint { get; private set; } | ||
| public WaypointCircuit.RoutePoint speedPoint { get; private set; } | ||
| public WaypointCircuit.RoutePoint progressPoint { get; private set; } | ||
|
|
||
| public Transform target; | ||
|
|
||
| private float progressDistance; // The progress round the route, used in smooth mode. | ||
| private int progressNum; // the current waypoint number, used in point-to-point mode. | ||
| private Vector3 lastPosition; // Used to calculate current speed (since we may not have a rigidbody component) | ||
| private float speed; // current speed of this object (calculated from delta since last frame) | ||
|
|
||
| // setup script properties | ||
| private void Start() | ||
| { | ||
| // we use a transform to represent the point to aim for, and the point which | ||
| // is considered for upcoming changes-of-speed. This allows this component | ||
| // to communicate this information to the AI without requiring further dependencies. | ||
|
|
||
| // You can manually create a transform and assign it to this component *and* the AI, | ||
| // then this component will update it, and the AI can read it. | ||
| if (target == null) | ||
| { | ||
| target = new GameObject(name + " Waypoint Target").transform; | ||
| } | ||
|
|
||
| Reset(); | ||
| } | ||
|
|
||
|
|
||
| // reset the object to sensible values | ||
| public void Reset() | ||
| { | ||
| progressDistance = 0; | ||
| progressNum = 0; | ||
| if (progressStyle == ProgressStyle.PointToPoint) | ||
| { | ||
| target.position = circuit.Waypoints[progressNum].position; | ||
| target.rotation = circuit.Waypoints[progressNum].rotation; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private void Update() | ||
| { | ||
| if (progressStyle == ProgressStyle.SmoothAlongRoute) | ||
| { | ||
| // determine the position we should currently be aiming for | ||
| // (this is different to the current progress position, it is a a certain amount ahead along the route) | ||
| // we use lerp as a simple way of smoothing out the speed over time. | ||
| if (Time.deltaTime > 0) | ||
| { | ||
| speed = Mathf.Lerp(speed, (lastPosition - transform.position).magnitude/Time.deltaTime, | ||
| Time.deltaTime); | ||
| } | ||
| target.position = | ||
| circuit.GetRoutePoint(progressDistance + lookAheadForTargetOffset + lookAheadForTargetFactor*speed) | ||
| .position; | ||
| target.rotation = | ||
| Quaternion.LookRotation( | ||
| circuit.GetRoutePoint(progressDistance + lookAheadForSpeedOffset + lookAheadForSpeedFactor*speed) | ||
| .direction); | ||
|
|
||
|
|
||
| // get our current progress along the route | ||
| progressPoint = circuit.GetRoutePoint(progressDistance); | ||
| Vector3 progressDelta = progressPoint.position - transform.position; | ||
| if (Vector3.Dot(progressDelta, progressPoint.direction) < 0) | ||
| { | ||
| progressDistance += progressDelta.magnitude*0.5f; | ||
| } | ||
|
|
||
| lastPosition = transform.position; | ||
| } | ||
| else | ||
| { | ||
| // point to point mode. Just increase the waypoint if we're close enough: | ||
|
|
||
| Vector3 targetDelta = target.position - transform.position; | ||
| if (targetDelta.magnitude < pointToPointThreshold) | ||
| { | ||
| progressNum = (progressNum + 1)%circuit.Waypoints.Length; | ||
| } | ||
|
|
||
|
|
||
| target.position = circuit.Waypoints[progressNum].position; | ||
| target.rotation = circuit.Waypoints[progressNum].rotation; | ||
|
|
||
| // get our current progress along the route | ||
| progressPoint = circuit.GetRoutePoint(progressDistance); | ||
| Vector3 progressDelta = progressPoint.position - transform.position; | ||
| if (Vector3.Dot(progressDelta, progressPoint.direction) < 0) | ||
| { | ||
| progressDistance += progressDelta.magnitude; | ||
| } | ||
| lastPosition = transform.position; | ||
| } | ||
| } | ||
|
|
||
|
|
||
| private void OnDrawGizmos() | ||
| { | ||
| if (Application.isPlaying) | ||
| { | ||
| Gizmos.color = Color.green; | ||
| Gizmos.DrawLine(transform.position, target.position); | ||
| Gizmos.DrawWireSphere(circuit.GetRoutePosition(progressDistance), 1); | ||
| Gizmos.color = Color.yellow; | ||
| Gizmos.DrawLine(target.position, target.position + target.forward); | ||
| } | ||
| } | ||
| } | ||
| } |