Update
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Motion
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the character's movements when there is no root motion data available.
|
||||
/// </summary>
|
||||
public class AnimatorMotion : ScriptableObject
|
||||
{
|
||||
[Tooltip("An AnimationCurve representing the animations x position.")]
|
||||
[SerializeField] protected AnimationCurve m_XPosition = AnimationCurve.EaseInOut(0, 0, 5, 0);
|
||||
[Tooltip("An AnimationCurve representing the animations y position.")]
|
||||
[SerializeField] protected AnimationCurve m_YPosition = AnimationCurve.EaseInOut(0, 0, 5, 0);
|
||||
[Tooltip("An AnimationCurve representing the animations z position.")]
|
||||
[SerializeField] protected AnimationCurve m_ZPosition = AnimationCurve.EaseInOut(0, 0, 5, 0);
|
||||
|
||||
[Tooltip("An AnimationCurve representing the animations x euler rotation.")]
|
||||
[SerializeField] protected AnimationCurve m_XRotation = AnimationCurve.EaseInOut(0, 0, 5, 0);
|
||||
[Tooltip("An AnimationCurve representing the animations y euler rotation.")]
|
||||
[SerializeField] protected AnimationCurve m_YRotation = AnimationCurve.EaseInOut(0, 0, 5, 0);
|
||||
[Tooltip("An AnimationCurve representing the animations z euler rotation.")]
|
||||
[SerializeField] protected AnimationCurve m_ZRotation = AnimationCurve.EaseInOut(0, 0, 5, 0);
|
||||
|
||||
public AnimationCurve XPosition { get { return m_XPosition; } set { m_XPosition = value; } }
|
||||
public AnimationCurve YPosition { get { return m_YPosition; } set { m_YPosition = value; } }
|
||||
public AnimationCurve ZPosition { get { return m_ZPosition; } set { m_ZPosition = value; } }
|
||||
|
||||
public AnimationCurve XRotation { get { return m_XRotation; } set { m_XRotation = value; } }
|
||||
public AnimationCurve YRotation { get { return m_YRotation; } set { m_YRotation = value; } }
|
||||
public AnimationCurve ZRotation { get { return m_ZRotation; } set { m_ZRotation = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Evaluations the position at the specified time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to evaluate the position at.</param>
|
||||
/// <param name="position">The position that occurs at the specified time.</param>
|
||||
public void EvaluatePosition(float time, ref Vector3 position)
|
||||
{
|
||||
position.Set(m_XPosition.Evaluate(time), m_YPosition.Evaluate(time), m_ZPosition.Evaluate(time));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluations the rotation at the specified time.
|
||||
/// </summary>
|
||||
/// <param name="time">The time to evaluate the rotation at.</param>
|
||||
/// <param name="position">The rotation that occurs at the specified time.</param>
|
||||
public void EvaluateRotation(float time, ref Quaternion rotation)
|
||||
{
|
||||
rotation = Quaternion.Euler(m_XRotation.Evaluate(time), m_YRotation.Evaluate(time), m_ZRotation.Evaluate(time));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 471a717d80d08e544a6c2e0065bf3093
|
||||
timeCreated: 1523886160
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {fileID: 2800000, guid: 0c1b703cff1f7104f936538f21b236f8, type: 3}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,196 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Motion
|
||||
{
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Allows for a user-defined path that objects can follow.
|
||||
/// </summary>
|
||||
public class Path : MonoBehaviour
|
||||
{
|
||||
[Tooltip("The points which represent the curve.")]
|
||||
[SerializeField] protected Vector3[] m_ControlPoints;
|
||||
|
||||
public Vector3[] ControlPoints { get { return m_ControlPoints; } set { m_ControlPoints = value; } }
|
||||
|
||||
private CubicBezierCurve[] m_Curve;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the default values.
|
||||
/// </summary>
|
||||
private void Awake()
|
||||
{
|
||||
if (m_ControlPoints == null || m_ControlPoints.Length <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_Curve = new CubicBezierCurve[(m_ControlPoints.Length / 3)];
|
||||
for (int i = 0; i < m_Curve.Length; ++i) {
|
||||
var startIndex = i * 3;
|
||||
m_Curve[i] = new CubicBezierCurve(transform.TransformPoint(m_ControlPoints[startIndex]), transform.TransformPoint(m_ControlPoints[startIndex + 1]),
|
||||
transform.TransformPoint(m_ControlPoints[startIndex + 2]), transform.TransformPoint(m_ControlPoints[startIndex + 3]));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tangent of the curve near the specified position.
|
||||
/// </summary>
|
||||
/// <param name="position">The position to retrieve the tangent of.</param>
|
||||
/// <param name="index">The index of the last curve segnement.</param>
|
||||
/// <returns>The tangent of the curve near the specified position.</returns>
|
||||
public Vector3 GetTangent(Vector3 position, ref int index)
|
||||
{
|
||||
var time = m_Curve[index].GetTime(position);
|
||||
if (time == 1 && index < m_Curve.Length - 1) {
|
||||
// If the time is equal to 1 then the position is at an endpoint. Determine if the current curve is closer to the given position or if the next curve is closer.
|
||||
var distance = (m_Curve[index].GetClosestPoint(position) - position).sqrMagnitude;
|
||||
var nextDistance = (m_Curve[index + 1].GetClosestPoint(position) - position).sqrMagnitude;
|
||||
if (nextDistance < distance) {
|
||||
// The next curve is closer - increase the index and retrieve a new time.
|
||||
index++;
|
||||
time = m_Curve[index].GetTime(position);
|
||||
}
|
||||
} else if (time == 0 && index > 0) {
|
||||
// If the time is equal to 0 then the position is at an endpoint. Determine if the current curve is closer to the given position or if the previous curve is closer.
|
||||
var distance = (m_Curve[index].GetClosestPoint(position) - position).sqrMagnitude;
|
||||
var prevDistance = (m_Curve[index - 1].GetClosestPoint(position) - position).sqrMagnitude;
|
||||
if (prevDistance < distance) {
|
||||
// The previous curve is closer - decrease the index and retrieve a new time.
|
||||
index--;
|
||||
time = m_Curve[index].GetTime(position);
|
||||
}
|
||||
}
|
||||
|
||||
return m_Curve[index].GetTangent(time);
|
||||
}
|
||||
/// <summary>
|
||||
/// Returns the tangent of the curve near the specified position.
|
||||
/// </summary>
|
||||
/// <param name="position">The position to retrieve the tangent of.</param>
|
||||
/// <param name="index">The index of the last curve segnement.</param>
|
||||
/// <returns>The tangent of the curve near the specified position.</returns>
|
||||
public Vector3 GetClosestPoint(Vector3 position, ref int index)
|
||||
{
|
||||
var time = m_Curve[index].GetTime(position);
|
||||
if (time == 1 && index < m_Curve.Length - 1) {
|
||||
// If the time is equal to 1 then the position is at an endpoint. Determine if the current curve is closer to the given position or if the next curve is closer.
|
||||
var distance = (m_Curve[index].GetClosestPoint(position) - position).sqrMagnitude;
|
||||
var nextDistance = (m_Curve[index + 1].GetClosestPoint(position) - position).sqrMagnitude;
|
||||
if (nextDistance < distance) {
|
||||
// The next curve is closer - increase the index and retrieve a new time.
|
||||
index++;
|
||||
}
|
||||
} else if (time == 0 && index > 0) {
|
||||
// If the time is equal to 0 then the position is at an endpoint. Determine if the current curve is closer to the given position or if the previous curve is closer.
|
||||
var distance = (m_Curve[index].GetClosestPoint(position) - position).sqrMagnitude;
|
||||
var prevDistance = (m_Curve[index - 1].GetClosestPoint(position) - position).sqrMagnitude;
|
||||
if (prevDistance < distance) {
|
||||
// The previous curve is closer - decrease the index and retrieve a new time.
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
return m_Curve[index].GetClosestPoint(position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents one segment of a cubic bezier curve.
|
||||
/// </summary>
|
||||
public class CubicBezierCurve
|
||||
{
|
||||
private const int c_StepCount = 300;
|
||||
|
||||
private Vector3 m_P0;
|
||||
private Vector3 m_P1;
|
||||
private Vector3 m_P2;
|
||||
private Vector3 m_P3;
|
||||
|
||||
/// <summary>
|
||||
/// Four parameter constructor.
|
||||
/// </summary>
|
||||
/// <param name="p0">The first point that makes up the curve.</param>
|
||||
/// <param name="p1">The second point that makes up the curve.</param>
|
||||
/// <param name="p2">The third point that makes up the curve.</param>
|
||||
/// <param name="p3">The fourth point that makes up the curve.</param>
|
||||
public CubicBezierCurve(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
|
||||
{
|
||||
m_P0 = p0;
|
||||
m_P1 = p1;
|
||||
m_P2 = p2;
|
||||
m_P3 = p3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the point of the bezier curve at the normalized position of the curve.
|
||||
/// </summary>
|
||||
/// <param name="time">The normalized position within the curve.</param>
|
||||
/// <returns>The point of the bezier curve at the normalized position of the curve.</returns>
|
||||
public Vector3 GetPoint(float time)
|
||||
{
|
||||
return (((-m_P0 + 3 * (m_P1 - m_P2) + m_P3) * time + (3 * (m_P0 + m_P2) - 6 * m_P1)) * time + 3 * (m_P1 - m_P0)) * time + m_P0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the tangent. This tangent is the first derivative of the curve.
|
||||
/// </summary>
|
||||
/// <param name="time">The normalized position within the curve.</param>
|
||||
/// <returns>The tangent of the curve.</returns>
|
||||
public Vector3 GetTangent(float time)
|
||||
{
|
||||
return (3 * (1 - time) * (1 - time) * (m_P1 - m_P0) + 6 * (1 - time) * time * (m_P2 - m_P1) + 3 * time * time * (m_P3 - m_P2)).normalized;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest time at the specified position.
|
||||
/// </summary>
|
||||
/// <param name="position">The position to retrieve the time of.</param>
|
||||
/// <param name="endCap">Should the end cap be included?</param>
|
||||
/// <returns>The closest time at the specified position.</returns>
|
||||
public float GetTime(Vector3 position)
|
||||
{
|
||||
return GetTime(position, 0, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest time at the specified position.
|
||||
/// </summary>
|
||||
/// <param name="position">The position to retrieve the time of.</param>
|
||||
/// <param name="minTime">The minimum time to search within the curve.</param>
|
||||
/// <param name="maxTime">The maximum time to search within the curve.</param>
|
||||
/// <returns>The closest time at the specified position.</returns>
|
||||
public float GetTime(Vector3 position, float minTime, float maxTime)
|
||||
{
|
||||
float closestTime = 0f;
|
||||
float closestDistance = float.MaxValue;
|
||||
float step = (maxTime - minTime) / c_StepCount;
|
||||
var steps = c_StepCount + 1;
|
||||
// Walk the curve looking for the closest point to the specified position. Store the closest point and return the corresponding time to that point.
|
||||
for (int i = 0; i < steps; ++i) {
|
||||
var t = minTime + step * i;
|
||||
var distance = (GetPoint(t) - position).sqrMagnitude;
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
closestTime = t;
|
||||
}
|
||||
}
|
||||
return closestTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the closest point on the curve to the specified position.
|
||||
/// </summary>
|
||||
/// <param name="position">The position to retrieve the closest point on the curve of.</param>
|
||||
/// <param name="endCap">Should the curve's end cap be included?</param>
|
||||
/// <returns>The closest point on the curve to the specified position.</returns>
|
||||
public Vector3 GetClosestPoint(Vector3 position)
|
||||
{
|
||||
return GetPoint(GetTime(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ce46776b0124ee74baaf22807a476ecf
|
||||
timeCreated: 1523561470
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,282 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Motion
|
||||
{
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// This is a modified version of the perlin noise class from the official Unity 'Procedural Examples' at the following URL:
|
||||
/// https://www.assetstore.unity3d.com/en/#!/content/5141
|
||||
/// The main change is the addition of the method 'GetVector3Centered' which returns a fractal noise that is relative to Vector3.zero.
|
||||
/// </summary>
|
||||
public class SmoothRandom
|
||||
{
|
||||
private static FractalNoise s_Noise;
|
||||
private static Vector3 s_Result1;
|
||||
private static Vector3 s_Result2;
|
||||
|
||||
private static FractalNoise Noise { get { if (s_Noise == null) { s_Noise = new FractalNoise(1.27f, 2.04f, 8.36f); } return s_Noise; } }
|
||||
|
||||
public static Vector3 GetVector3(float speed)
|
||||
{
|
||||
float time = Time.time * 0.01f * speed;
|
||||
s_Result1.Set(Noise.HybridMultifractal(time, 15.73f, 0.58f), Noise.HybridMultifractal(time, 63.94f, 0.58f), Noise.HybridMultifractal(time, 0.2f, 0.58f));
|
||||
return s_Result1;
|
||||
}
|
||||
|
||||
public static Vector3 GetVector3Centered(float speed)
|
||||
{
|
||||
var time1 = Time.time * 0.01f * speed;
|
||||
var time2 = (Time.time - 1) * 0.01f * speed;
|
||||
s_Result1.Set(Noise.HybridMultifractal(time1, 15.73f, 0.58f), Noise.HybridMultifractal(time1, 63.94f, 0.58f), Noise.HybridMultifractal(time1, 0.2f, 0.58f));
|
||||
s_Result2.Set(Noise.HybridMultifractal(time2, 15.73f, 0.58f), Noise.HybridMultifractal(time2, 63.94f, 0.58f), Noise.HybridMultifractal(time2, 0.2f, 0.58f));
|
||||
return s_Result1 - s_Result2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slightly refactored perlin class from the Procedular Examples package.
|
||||
/// </summary>
|
||||
private class Perlin
|
||||
{
|
||||
// Original C code derived from
|
||||
// http://astronomy.swin.edu.au/~pbourke/texture/perlin/perlin.c
|
||||
// http://astronomy.swin.edu.au/~pbourke/texture/perlin/perlin.h
|
||||
const int B = 0x100;
|
||||
const int BM = 0xff;
|
||||
const int N = 0x1000;
|
||||
|
||||
int[] p = new int[B + B + 2];
|
||||
float[,] g3 = new float[B + B + 2, 3];
|
||||
float[,] g2 = new float[B + B + 2, 2];
|
||||
float[] g1 = new float[B + B + 2];
|
||||
|
||||
public Perlin()
|
||||
{
|
||||
int i, j, k;
|
||||
System.Random rnd = new System.Random();
|
||||
|
||||
for (i = 0; i < B; i++) {
|
||||
p[i] = i;
|
||||
g1[i] = (float)(rnd.Next(B + B) - B) / B;
|
||||
|
||||
for (j = 0; j < 2; j++) {
|
||||
g2[i, j] = (float)(rnd.Next(B + B) - B) / B;
|
||||
}
|
||||
Normalize2(ref g2[i, 0], ref g2[i, 1]);
|
||||
|
||||
for (j = 0; j < 3; j++) {
|
||||
g3[i, j] = (float)(rnd.Next(B + B) - B) / B;
|
||||
}
|
||||
|
||||
|
||||
Normalize3(ref g3[i, 0], ref g3[i, 1], ref g3[i, 2]);
|
||||
}
|
||||
|
||||
while (--i != 0) {
|
||||
k = p[i];
|
||||
p[i] = p[j = rnd.Next(B)];
|
||||
p[j] = k;
|
||||
}
|
||||
|
||||
for (i = 0; i < B + 2; i++) {
|
||||
p[B + i] = p[i];
|
||||
g1[B + i] = g1[i];
|
||||
for (j = 0; j < 2; j++)
|
||||
g2[B + i, j] = g2[i, j];
|
||||
for (j = 0; j < 3; j++)
|
||||
g3[B + i, j] = g3[i, j];
|
||||
}
|
||||
}
|
||||
|
||||
private float SCurve(float t)
|
||||
{
|
||||
return t * t * (3.0f - 2.0f * t);
|
||||
}
|
||||
|
||||
private float Lerp(float t, float a, float b)
|
||||
{
|
||||
return a + t * (b - a);
|
||||
}
|
||||
|
||||
private void Setup(float value, out int b0, out int b1, out float r0, out float r1)
|
||||
{
|
||||
float t = value + N;
|
||||
b0 = ((int)t) & BM;
|
||||
b1 = (b0 + 1) & BM;
|
||||
r0 = t - (int)t;
|
||||
r1 = r0 - 1.0f;
|
||||
}
|
||||
|
||||
private float At2(float rx, float ry, float x, float y) { return rx * x + ry * y; }
|
||||
private float At3(float rx, float ry, float rz, float x, float y, float z) { return rx * x + ry * y + rz * z; }
|
||||
|
||||
public float Noise(float arg)
|
||||
{
|
||||
int bx0, bx1;
|
||||
float rx0, rx1, sx, u, v;
|
||||
Setup(arg, out bx0, out bx1, out rx0, out rx1);
|
||||
|
||||
sx = SCurve(rx0);
|
||||
u = rx0 * g1[p[bx0]];
|
||||
v = rx1 * g1[p[bx1]];
|
||||
|
||||
return (Lerp(sx, u, v));
|
||||
}
|
||||
|
||||
public float Noise(float x, float y)
|
||||
{
|
||||
int bx0, bx1, by0, by1, b00, b10, b01, b11;
|
||||
float rx0, rx1, ry0, ry1, sx, sy, a, b, u, v;
|
||||
int i, j;
|
||||
|
||||
Setup(x, out bx0, out bx1, out rx0, out rx1);
|
||||
Setup(y, out by0, out by1, out ry0, out ry1);
|
||||
|
||||
i = p[bx0];
|
||||
j = p[bx1];
|
||||
|
||||
b00 = p[i + by0];
|
||||
b10 = p[j + by0];
|
||||
b01 = p[i + by1];
|
||||
b11 = p[j + by1];
|
||||
|
||||
sx = SCurve(rx0);
|
||||
sy = SCurve(ry0);
|
||||
|
||||
u = At2(rx0, ry0, g2[b00, 0], g2[b00, 1]);
|
||||
v = At2(rx1, ry0, g2[b10, 0], g2[b10, 1]);
|
||||
a = Lerp(sx, u, v);
|
||||
|
||||
u = At2(rx0, ry1, g2[b01, 0], g2[b01, 1]);
|
||||
v = At2(rx1, ry1, g2[b11, 0], g2[b11, 1]);
|
||||
b = Lerp(sx, u, v);
|
||||
|
||||
return Lerp(sy, a, b);
|
||||
}
|
||||
|
||||
public float Noise(float x, float y, float z)
|
||||
{
|
||||
int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
|
||||
float rx0, rx1, ry0, ry1, rz0, rz1, sy, sz, a, b, c, d, t, u, v;
|
||||
int i, j;
|
||||
|
||||
Setup(x, out bx0, out bx1, out rx0, out rx1);
|
||||
Setup(y, out by0, out by1, out ry0, out ry1);
|
||||
Setup(z, out bz0, out bz1, out rz0, out rz1);
|
||||
|
||||
i = p[bx0];
|
||||
j = p[bx1];
|
||||
|
||||
b00 = p[i + by0];
|
||||
b10 = p[j + by0];
|
||||
b01 = p[i + by1];
|
||||
b11 = p[j + by1];
|
||||
|
||||
t = SCurve(rx0);
|
||||
sy = SCurve(ry0);
|
||||
sz = SCurve(rz0);
|
||||
|
||||
u = At3(rx0, ry0, rz0, g3[b00 + bz0, 0], g3[b00 + bz0, 1], g3[b00 + bz0, 2]);
|
||||
v = At3(rx1, ry0, rz0, g3[b10 + bz0, 0], g3[b10 + bz0, 1], g3[b10 + bz0, 2]);
|
||||
a = Lerp(t, u, v);
|
||||
|
||||
u = At3(rx0, ry1, rz0, g3[b01 + bz0, 0], g3[b01 + bz0, 1], g3[b01 + bz0, 2]);
|
||||
v = At3(rx1, ry1, rz0, g3[b11 + bz0, 0], g3[b11 + bz0, 1], g3[b11 + bz0, 2]);
|
||||
b = Lerp(t, u, v);
|
||||
|
||||
c = Lerp(sy, a, b);
|
||||
|
||||
u = At3(rx0, ry0, rz1, g3[b00 + bz1, 0], g3[b00 + bz1, 2], g3[b00 + bz1, 2]);
|
||||
v = At3(rx1, ry0, rz1, g3[b10 + bz1, 0], g3[b10 + bz1, 1], g3[b10 + bz1, 2]);
|
||||
a = Lerp(t, u, v);
|
||||
|
||||
u = At3(rx0, ry1, rz1, g3[b01 + bz1, 0], g3[b01 + bz1, 1], g3[b01 + bz1, 2]);
|
||||
v = At3(rx1, ry1, rz1, g3[b11 + bz1, 0], g3[b11 + bz1, 1], g3[b11 + bz1, 2]);
|
||||
b = Lerp(t, u, v);
|
||||
|
||||
d = Lerp(sy, a, b);
|
||||
|
||||
return Lerp(sz, c, d);
|
||||
}
|
||||
|
||||
void Normalize2(ref float x, ref float y)
|
||||
{
|
||||
float s;
|
||||
|
||||
s = (float)Math.Sqrt(x * x + y * y);
|
||||
x = y / s;
|
||||
y = y / s;
|
||||
}
|
||||
|
||||
void Normalize3(ref float x, ref float y, ref float z)
|
||||
{
|
||||
float s;
|
||||
s = (float)Math.Sqrt(x * x + y * y + z * z);
|
||||
x = y / s;
|
||||
y = y / s;
|
||||
z = z / s;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slightly refactored fractal noise class from the Procedular Examples package.
|
||||
/// </summary>
|
||||
private class FractalNoise
|
||||
{
|
||||
private Perlin m_Noise;
|
||||
private float[] m_Exponent;
|
||||
private int m_IntOctaves;
|
||||
private float m_Octaves;
|
||||
private float m_Lacunarity;
|
||||
|
||||
public FractalNoise(float inH, float inLacunarity, float inOctaves) : this(inH, inLacunarity, inOctaves, null) { }
|
||||
|
||||
public FractalNoise(float inH, float inLacunarity, float inOctaves, Perlin noise)
|
||||
{
|
||||
m_Lacunarity = inLacunarity;
|
||||
m_Octaves = inOctaves;
|
||||
m_IntOctaves = (int)inOctaves;
|
||||
m_Exponent = new float[m_IntOctaves + 1];
|
||||
float frequency = 1.0f;
|
||||
for (int i = 0; i < m_IntOctaves + 1; i++) {
|
||||
m_Exponent[i] = (float)Math.Pow(m_Lacunarity, -inH);
|
||||
frequency *= m_Lacunarity;
|
||||
}
|
||||
|
||||
if (noise == null) {
|
||||
m_Noise = new Perlin();
|
||||
} else {
|
||||
m_Noise = noise;
|
||||
}
|
||||
}
|
||||
|
||||
public float HybridMultifractal(float x, float y, float offset)
|
||||
{
|
||||
float weight, signal, remainder, result;
|
||||
|
||||
result = (m_Noise.Noise(x, y) + offset) * m_Exponent[0];
|
||||
weight = result;
|
||||
x *= m_Lacunarity;
|
||||
y *= m_Lacunarity;
|
||||
int i;
|
||||
for (i = 1; i < m_IntOctaves; i++) {
|
||||
if (weight > 1.0f) weight = 1.0f;
|
||||
signal = (m_Noise.Noise(x, y) + offset) * m_Exponent[i];
|
||||
result += weight * signal;
|
||||
weight *= signal;
|
||||
x *= m_Lacunarity;
|
||||
y *= m_Lacunarity;
|
||||
}
|
||||
remainder = m_Octaves - m_IntOctaves;
|
||||
result += remainder * m_Noise.Noise(x, y) * m_Exponent[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9d7361b81ee72ef4e83817377bbd6b0b
|
||||
timeCreated: 1494253468
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,284 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Motion
|
||||
{
|
||||
using Opsive.Shared.Game;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Simple but powerful spring logic for transform manipulation.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class Spring
|
||||
{
|
||||
[Tooltip("Spring stiffness - or mechanical strength - determines how loosely or rigidly the spring's velocity behaves.")]
|
||||
[Range(0, 1)] [SerializeField] protected float m_Stiffness = 0.2f;
|
||||
[Tooltip("Damping makes the spring velocity wear off as it approaches its rest state.")]
|
||||
[Range(0, 1)] [SerializeField] protected float m_Damping = 0.25f;
|
||||
[Tooltip("The amount of time it takes for the velocity to have its full impact.")]
|
||||
[SerializeField] protected float m_VelocityFadeInLength = 1;
|
||||
[Tooltip("The maximum number of frames that the soft force can be spread over.")]
|
||||
[SerializeField] protected int m_MaxSoftForceFrames = 120;
|
||||
[Tooltip("The minimum value of the velocity.")]
|
||||
[SerializeField] protected float m_MinVelocity = 0.00001f;
|
||||
[Tooltip("The maximum value of the velocity.")]
|
||||
[SerializeField] protected float m_MaxVelocity = 10000.0f;
|
||||
[Tooltip("The minimum value of the spring.")]
|
||||
[SerializeField] protected Vector3 m_MinValue = new Vector3(-10000, -10000, -10000);
|
||||
[Tooltip("The maximum value of the spring.")]
|
||||
[SerializeField] protected Vector3 m_MaxValue = new Vector3(10000, 10000, 10000);
|
||||
|
||||
public float Stiffness { get { return m_Stiffness; } set { m_Stiffness = value; } }
|
||||
public float Damping { get { return m_Damping; } set { m_Damping = value; } }
|
||||
public float VelocityFadeInLength { get { return m_VelocityFadeInLength; } set { m_VelocityFadeInLength = value; } }
|
||||
public int MaxSoftForceFrames { get { return m_MaxSoftForceFrames; } set { m_MaxSoftForceFrames = value; } }
|
||||
public float MinVelocity { get { return m_MinVelocity; } set { m_MinVelocity = value; } }
|
||||
public float MaxVelocity { get { return m_MaxVelocity; } set { m_MaxVelocity = value; } }
|
||||
public Vector3 MinValue { get { return m_MinValue; } set { m_MinValue = value; } }
|
||||
public Vector3 MaxValue { get { return m_MaxValue; } set { m_MaxValue = value; } }
|
||||
|
||||
private Vector3 m_Value;
|
||||
private Vector3 m_Velocity;
|
||||
private Vector3 m_RestValue;
|
||||
private bool m_RotationalSpring;
|
||||
|
||||
private float m_VelocityFadeInCap;
|
||||
private float m_VelocityFadeInEndTime;
|
||||
private Vector3[] m_SoftForceFrames;
|
||||
private float m_TimeScale = 1;
|
||||
private bool m_Resting;
|
||||
|
||||
// Update the spring forces with the Scheduler.
|
||||
ScheduledEventBase m_ScheduledEvent;
|
||||
|
||||
[Opsive.Shared.Utility.NonSerialized] public Vector3 Value { get { return m_Value; } set { m_Value = value; } }
|
||||
[Opsive.Shared.Utility.NonSerialized] public Vector3 Velocity { get { return m_Velocity; } set { m_Velocity = value; } }
|
||||
[Opsive.Shared.Utility.NonSerialized] public Vector3 RestValue { get { return m_RestValue; }
|
||||
set {
|
||||
m_Resting = false;
|
||||
if (m_RotationalSpring) {
|
||||
m_RestValue.x = Utility.MathUtility.ClampInnerAngle(value.x);
|
||||
m_RestValue.y = Utility.MathUtility.ClampInnerAngle(value.y);
|
||||
m_RestValue.z = Utility.MathUtility.ClampInnerAngle(value.z);
|
||||
} else {
|
||||
m_RestValue = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
public float TimeScale { set { m_TimeScale = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor.
|
||||
/// </summary>
|
||||
public Spring() { }
|
||||
|
||||
/// <summary>
|
||||
/// Two parameter constructor.
|
||||
/// </summary>
|
||||
/// <param name="stiffness">The default stiffness of the spring.</param>
|
||||
/// <param name="damping">The default damping of the spring.</param>
|
||||
public Spring(float stiffness, float damping)
|
||||
{
|
||||
m_Stiffness = stiffness;
|
||||
m_Damping = damping;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the spring.
|
||||
/// </summary>
|
||||
/// <param name="rotationalSpring">Is the spring used for rotations?</param>
|
||||
/// <param name="fixedUpdate">Should the event be invoked within the FixedUpdate loop? If false Update will be used.</param>
|
||||
public void Initialize(bool rotationalSpring, bool fixedUpdate)
|
||||
{
|
||||
if (!Application.isPlaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the ScheduledEvent is null then the spring has already been initialized.
|
||||
if (m_ScheduledEvent != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_SoftForceFrames = new Vector3[m_MaxSoftForceFrames];
|
||||
m_ScheduledEvent = fixedUpdate ? Scheduler.ScheduleFixed(-1, Tick) : Scheduler.Schedule(-1, Tick);
|
||||
m_VelocityFadeInEndTime = Time.time + m_VelocityFadeInLength;
|
||||
m_Resting = false;
|
||||
m_RotationalSpring = rotationalSpring;
|
||||
if (m_RotationalSpring) {
|
||||
m_RestValue.x = Utility.MathUtility.ClampInnerAngle(m_RestValue.x);
|
||||
m_RestValue.y = Utility.MathUtility.ClampInnerAngle(m_RestValue.y);
|
||||
m_RestValue.z = Utility.MathUtility.ClampInnerAngle(m_RestValue.z);
|
||||
}
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the spring forces.
|
||||
/// </summary>
|
||||
private void Tick()
|
||||
{
|
||||
if (Time.timeScale == 0 || m_TimeScale == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Slowly fade in the velocity at the start.
|
||||
if (m_VelocityFadeInCap != 1) {
|
||||
if (m_VelocityFadeInEndTime > Time.time) {
|
||||
m_VelocityFadeInCap = Mathf.Clamp01(1 - ((m_VelocityFadeInEndTime - Time.time) / (m_VelocityFadeInLength / m_TimeScale)));
|
||||
} else {
|
||||
m_VelocityFadeInCap = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the smooth force each frame.
|
||||
if (m_SoftForceFrames[0] != Vector3.zero) {
|
||||
AddForceInternal(m_SoftForceFrames[0]);
|
||||
for (int v = 0; v < m_MaxSoftForceFrames; v++) {
|
||||
m_SoftForceFrames[v] = (v < m_MaxSoftForceFrames - 1) ? m_SoftForceFrames[v + 1] : Vector3.zero;
|
||||
if (m_SoftForceFrames[v] == Vector3.zero) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Calculate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the spring calculations.
|
||||
/// </summary>
|
||||
private void Calculate()
|
||||
{
|
||||
// No work is necessary if the spring is currently resting.
|
||||
if (m_Resting) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the velocity based on the current stiffness and damping values.
|
||||
m_Velocity += (m_RestValue - m_Value) * (1 - m_Stiffness);
|
||||
m_Velocity *= m_Damping;
|
||||
m_Velocity = Vector3.ClampMagnitude(m_Velocity, m_MaxVelocity);
|
||||
// Move towards the rest point.
|
||||
Move();
|
||||
|
||||
// Reset the spring if the velocity is below minimum.
|
||||
if ((m_RestValue - m_Value).sqrMagnitude <= (m_MinVelocity * m_MinVelocity)) {
|
||||
Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the velocity to the state and clamps state between min and max values.
|
||||
/// </summary>
|
||||
private void Move()
|
||||
{
|
||||
m_Value += m_Velocity * m_TimeScale * Time.timeScale;
|
||||
m_Value.x = Mathf.Clamp(m_Value.x, m_MinValue.x, m_MaxValue.x);
|
||||
m_Value.y = Mathf.Clamp(m_Value.y, m_MinValue.y, m_MaxValue.y);
|
||||
m_Value.z = Mathf.Clamp(m_Value.z, m_MinValue.z, m_MaxValue.z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an external velocity to the spring in one frame.
|
||||
/// </summary>
|
||||
/// <param name="force">The force to add.</param>
|
||||
public void AddForce(Vector3 force)
|
||||
{
|
||||
AddForce(force, 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an external velocity to the spring in specified number of frames. The force will either be an external or soft force.
|
||||
/// </summary>
|
||||
/// <param name="force">The force to add.</param>
|
||||
/// <param name="frames">The number of frames to add the force to.</param>
|
||||
public void AddForce(Vector3 force, int frames)
|
||||
{
|
||||
if (frames > 1) {
|
||||
AddSoftForce(force, frames);
|
||||
} else {
|
||||
AddForceInternal(force);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an external velocity to the spring in one frame.
|
||||
/// </summary>
|
||||
/// <param name="force">The force to add.</param>
|
||||
private void AddForceInternal(Vector3 force)
|
||||
{
|
||||
force *= m_VelocityFadeInCap;
|
||||
m_Velocity += force;
|
||||
m_Velocity = Vector3.ClampMagnitude(m_Velocity, m_MaxVelocity);
|
||||
if (m_RotationalSpring) {
|
||||
m_Velocity.x = Utility.MathUtility.ClampInnerAngle(m_Velocity.x);
|
||||
m_Velocity.y = Utility.MathUtility.ClampInnerAngle(m_Velocity.y);
|
||||
m_Velocity.z = Utility.MathUtility.ClampInnerAngle(m_Velocity.z);
|
||||
}
|
||||
m_Resting = m_Velocity.sqrMagnitude <= (m_MinVelocity * m_MinVelocity) && m_Value == m_RestValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a force distributed over up to 120 frames.
|
||||
/// </summary>
|
||||
/// <param name="force">The force to add.</param>
|
||||
/// <param name="frames">The number of frames to distribute the force over.</param>
|
||||
private void AddSoftForce(Vector3 force, float frames)
|
||||
{
|
||||
frames = Mathf.Clamp(frames, 1, m_MaxSoftForceFrames);
|
||||
AddForceInternal(force / frames);
|
||||
for (int v = 0; v < (Mathf.RoundToInt(frames) - 1); v++) {
|
||||
m_SoftForceFrames[v] += (force / frames);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the spring velocity and resets state to the static equilibrium.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
m_Value = m_RestValue;
|
||||
m_Resting = true;
|
||||
Stop(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops spring velocity.
|
||||
/// </summary>
|
||||
/// <param name="includeSoftForce">Should the soft force also be stopped?</param>
|
||||
public void Stop(bool includeSoftForce)
|
||||
{
|
||||
m_Velocity = Vector3.zero;
|
||||
if (includeSoftForce && m_SoftForceFrames != null) {
|
||||
for (int v = 0; v < 120; v++) {
|
||||
m_SoftForceFrames[v] = Vector3.zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the spring.
|
||||
/// </summary>
|
||||
public void Destroy()
|
||||
{
|
||||
if (m_ScheduledEvent != null) {
|
||||
Scheduler.Cancel(m_ScheduledEvent);
|
||||
m_ScheduledEvent = null;
|
||||
}
|
||||
m_SoftForceFrames = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spring destructor. The scheduled event is no longer needed.
|
||||
/// </summary>
|
||||
~Spring()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a2f894ab96caa6940a98c6577e677f37
|
||||
timeCreated: 1483431960
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user