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