using System; using UnityEngine; using Framework.Services; using Framework.Support; namespace Framework.Cinematics { /// Contains movement instructions for one actor public class Track : ScriptComponent { /// Movement instructions that the actor should execute public Instruction[] Instructions; /// Guesses the position at which the cinematic track begins /// The position at which the cinematic track probably begins /// /// The start position is not fixed because animation tracks may simply pick up /// a character from an arbitrary position and let him/her walk towards the first /// goal. Thus use the position of the first goal at the origin. /// public Vector3 GuessInitialPosition() { #if false for(int index = 0; index < this.Instructions.Length; ++index) { if(isMovingInstruction(this.Instructions[index].Action)) { return transform.position + this.Instructions[index].Position; } } #endif return transform.position; } /// Called to visualize the movement instructions in the editor protected virtual void OnDrawGizmos() { Matrix4x4 oldMatrix = Gizmos.matrix; Color oldColor = Gizmos.color; try { if(this.Instructions != null) { drawInstructions(); } } finally { Gizmos.color = oldColor; Gizmos.matrix = oldMatrix; } } /// Draws the insturctions in the cinematic track private void drawInstructions() { Vector3 origin = GuessInitialPosition(); bool hasDrawnSpecialInstruction = false; for(int index = 0; index < this.Instructions.Length; ++index) { Vector3 destination; if(isMovingInstruction(this.Instructions[index].Action)) { destination = transform.position + this.Instructions[index].Position; } else { destination = origin; } switch(this.Instructions[index].Action) { case Action.Walk: { drawWalkInstruction(origin, destination); hasDrawnSpecialInstruction = false; break; } case Action.Jump: { drawJumpInstruction(origin, destination); hasDrawnSpecialInstruction = false; break; } case Action.Turn: { drawTurnInstruction(origin, transform.position + this.Instructions[index].Position); hasDrawnSpecialInstruction = false; break; } default: { if(!hasDrawnSpecialInstruction) { drawOtherInstruction(destination); hasDrawnSpecialInstruction = true; } break; } } origin = destination; } } /// Draws a walk instruction from one place to another /// Location the walk instruction begins at /// Location the walk instruction ends at private void drawWalkInstruction(Vector3 origin, Vector3 target) { if(origin == target) { origin.x -= 0.125f; target.x += 0.125f; } Vector3 direction = Vector3.Normalize(target - origin); Gizmos.matrix = Matrix4x4.TRS( origin, //(origin + target) / 2.0f, Quaternion.LookRotation(direction), Vector3.one ); // Using the transformation matrix from before, draw the line segment // as a cube (because this uses the depth buffer and makes ground placement // much easier to check) float length = (target - origin).magnitude; Gizmos.color = new Color(1.0f, 1.0f, 0.5f, 0.75f); Gizmos.DrawCube( new Vector3(0.0f, -0.05f, length / 2.0f), new Vector3(0.25f, 0.1f, length) ); } /// Draws a jump instruction into a scene /// Point from which the character will jump off /// Point at which the character will land private void drawJumpInstruction(Vector3 origin, Vector3 target) { CubicBezierCurve curve = new CubicBezierCurve(); curve.P0 = origin; curve.P1 = Vector3.Lerp(origin, target, 0.5f); curve.P2 = Vector3.Lerp(origin, target, 0.75f); curve.P3 = target; float apex = Math.Max(origin.y, target.y) + 0.5f; curve.P1.y = apex; curve.P2.y = apex; Gizmos.matrix = Matrix4x4.identity; Gizmos.color = new Color(1.0f, 1.0f, 0.0f, 1.0f); drawCubicBezierCurve(curve); } /// Draws an instruction to look at a target into the scene /// Location the turn instruction will be drawn at /// Target the turn instruction will look at private void drawTurnInstruction(Vector3 origin, Vector3 target) { Vector3 direction = Vector3.Normalize(target - origin); Gizmos.matrix = Matrix4x4.TRS( origin, Quaternion.LookRotation(direction), Vector3.one ); Gizmos.color = new Color(1.0f, 1.0f, 0.0f, 1.0f); Gizmos.DrawFrustum(Vector3.zero, 10.0f, 2.0f, 0.1f, 1.0f); //Gizmos.DrawSphere(target, 0.1f); } /// Draws a special (non-movement) instruction into the scene /// Location the special instruction will be drawn at private void drawOtherInstruction(Vector3 target) { Gizmos.matrix = Matrix4x4.identity; Gizmos.color = new Color(1.0f, 1.0f, 1.0f, 0.75f); Gizmos.DrawSphere(target, 0.1f); } /// Draws a cubic bezier curve using the provided supporting points /// Location from which the curve starts /// Point towards which the direction of the curve starts out /// Point from which towards p3 the direction of the curve ends /// Location at which the curve ends private static void drawCubicBezierCurve(CubicBezierCurve curve) { Vector3 previous; Vector3 current = curve.P0; for(int index = 1; index < 10; ++index) { previous = current; current = curve.GetPointAtTime((float)index / 10.0f); Gizmos.DrawLine(previous, current); } Gizmos.DrawLine(current, curve.P3); } /// /// Checks whether the specified instruction changes the agent's position /// /// Action that will be checked /// True if the action declares a movement instruction private bool isMovingInstruction(Action action) { return (action == Action.Walk) || (action == Action.Jump); } } } // namespace Framework.Cinematics