您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
244 行
7.0 KiB
244 行
7.0 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.Playables;
|
|
using UnityEngine.Animations;
|
|
using UnityEngine.Assertions;
|
|
|
|
[Serializable]
|
|
public struct BlendSpaceNode
|
|
{
|
|
public AnimationClip clip;
|
|
public Vector2 position;
|
|
public float speed;
|
|
[HideInInspector]
|
|
public float weight;
|
|
[HideInInspector]
|
|
public float clipLength;
|
|
}
|
|
|
|
public class BlendTree2dSimpleDirectional
|
|
{
|
|
public BlendTree2dSimpleDirectional(PlayableGraph graph, List<BlendSpaceNode> nodes)
|
|
{
|
|
m_Nodes = nodes;
|
|
var count = m_Nodes.Count;
|
|
m_Positions = new Vector2[count];
|
|
m_Clips = new AnimationClipPlayable[count];
|
|
m_Weights = new float[count];
|
|
|
|
m_Mixer = AnimationMixerPlayable.Create(graph, count);
|
|
m_Mixer.SetPropagateSetTime(true);
|
|
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var node = m_Nodes[i];
|
|
var clip = AnimationClipPlayable.Create(graph, node.clip);
|
|
node.clipLength = node.clip.length;
|
|
clip.Play();
|
|
m_Mixer.ConnectInput(i, clip, 0);
|
|
m_Clips[i] = clip;
|
|
m_Nodes[i] = node;
|
|
}
|
|
|
|
masterSpeed = 1f;
|
|
SetBlendPosition(new Vector2(0f, 0f));
|
|
}
|
|
|
|
public float SetBlendPosition(Vector2 position, bool updateGraph = true)
|
|
{
|
|
var count = m_Nodes.Count;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
m_Positions[i] = m_Nodes[i].position;
|
|
}
|
|
CalculateWeights(m_Positions, ref m_Weights, position);
|
|
|
|
// Store results and calculate blendedClipLength
|
|
m_BlendedClipLength = 0f;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var node = m_Nodes[i];
|
|
node.weight = m_Weights[i];
|
|
m_Nodes[i] = node;
|
|
m_BlendedClipLength += node.clipLength / m_Nodes[i].speed * node.weight;
|
|
}
|
|
|
|
GameDebug.Assert(m_BlendedClipLength > 0f, "blendedClipLength must be more that 0");
|
|
m_BlendedClipLength /= masterSpeed;
|
|
|
|
if (updateGraph)
|
|
{
|
|
UpdateGraph();
|
|
}
|
|
|
|
return m_BlendedClipLength;
|
|
}
|
|
|
|
public void UpdateGraph()
|
|
{
|
|
for (var i = 0; i < m_Clips.Length; i++)
|
|
{
|
|
m_Mixer.SetInputWeight(i, m_Weights[i]);
|
|
m_Clips[i].SetSpeed(m_Nodes[i].clipLength / m_BlendedClipLength);
|
|
m_Clips[i].SetApplyFootIK(footIk);
|
|
}
|
|
}
|
|
|
|
public void SetPhase(float phase)
|
|
{
|
|
for (var i = 0; i < m_Clips.Length; i++)
|
|
{
|
|
m_Clips[i].SetTime(phase * m_Clips[i].GetAnimationClip().length);
|
|
}
|
|
}
|
|
|
|
static void CalculateWeights(Vector2[] positionArray, ref float[] weightArray, Vector2 blendParam)
|
|
{
|
|
var count = positionArray.Length;
|
|
|
|
// Initialize all weights to 0
|
|
for (var i = 0; i < weightArray.Length; i++)
|
|
{
|
|
weightArray[i] = 0f;
|
|
}
|
|
|
|
// Handle fallback
|
|
if (count < 2)
|
|
{
|
|
if (count == 1)
|
|
weightArray[0] = 1;
|
|
return;
|
|
}
|
|
|
|
var blendPosition = new Vector2(blendParam.x, blendParam.y);
|
|
|
|
// Handle special case when sampled ecactly in the middle
|
|
if (blendPosition == new Vector2(0f, 0f))
|
|
{
|
|
// If we have a center motion, give that one all the weight
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
if (positionArray[i] == Vector2.zero)
|
|
{
|
|
weightArray[i] = 1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise divide weight evenly
|
|
float sharedWeight = 1.0f / count;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
weightArray[i] = sharedWeight;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
int indexA = -1;
|
|
int indexB = -1;
|
|
int indexCenter = -1;
|
|
float maxDotForNegCross = -100000.0f;
|
|
float maxDotForPosCross = -100000.0f;
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
if (positionArray[i] == Vector2.zero)
|
|
{
|
|
if (indexCenter >= 0)
|
|
return;
|
|
indexCenter = i;
|
|
continue;
|
|
}
|
|
|
|
// Vector2 posNormalized = Mathf.Normalize(positionArray[i]);
|
|
Vector2 posNormalized = positionArray[i].normalized;
|
|
var dot = Vector2.Dot(posNormalized, blendPosition);
|
|
var cross = posNormalized.x * blendPosition.y - posNormalized.y * blendPosition.x;
|
|
if (cross > 0f)
|
|
{
|
|
if (dot > maxDotForPosCross)
|
|
{
|
|
maxDotForPosCross = dot;
|
|
indexA = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dot > maxDotForNegCross)
|
|
{
|
|
maxDotForNegCross = dot;
|
|
indexB = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
float centerWeight = 0;
|
|
|
|
if (indexA < 0 || indexB < 0)
|
|
{
|
|
// Fallback if sampling point is not inside a triangle
|
|
centerWeight = 1;
|
|
}
|
|
else
|
|
{
|
|
var a = positionArray[indexA];
|
|
var b = positionArray[indexB];
|
|
|
|
// Calculate weights using barycentric coordinates
|
|
// (formulas from http://en.wikipedia.org/wiki/Barycentric_coordinate_system_%28mathematics%29 )
|
|
float det = b.y * a.x - b.x * a.y; // Simplified from: (b.y-0)*(a.x-0) + (0-b.x)*(a.y-0);
|
|
|
|
// TODO: Is x and y used correctly below??
|
|
float wA = (b.y * blendParam.x - b.x * blendParam.y) / det; // Simplified from: ((b.y-0)*(l.x-0) + (0-b.x)*(l.y-0)) / det;
|
|
float wB = (a.x * blendParam.y - a.y * blendParam.x) / det; // Simplified from: ((0-a.y)*(l.x-0) + (a.x-0)*(l.y-0)) / det;
|
|
centerWeight = 1 - wA - wB;
|
|
|
|
// Clamp to be inside triangle
|
|
if (centerWeight < 0)
|
|
{
|
|
centerWeight = 0;
|
|
float sum = wA + wB;
|
|
wA /= sum;
|
|
wB /= sum;
|
|
}
|
|
else if (centerWeight > 1)
|
|
{
|
|
centerWeight = 1;
|
|
wA = 0;
|
|
wB = 0;
|
|
}
|
|
|
|
// Give weight to the two vertices on the periphery that are closest
|
|
weightArray[indexA] = wA;
|
|
weightArray[indexB] = wB;
|
|
}
|
|
|
|
if (indexCenter >= 0)
|
|
{
|
|
weightArray[indexCenter] = centerWeight;
|
|
}
|
|
else
|
|
{
|
|
// Give weight to all children when input is in the center
|
|
float sharedWeight = 1.0f / count;
|
|
for (var i = 0; i < count; i++)
|
|
weightArray[i] += sharedWeight * centerWeight;
|
|
}
|
|
}
|
|
|
|
public Playable GetRootPlayable()
|
|
{
|
|
return m_Mixer;
|
|
}
|
|
|
|
AnimationClipPlayable[] m_Clips;
|
|
AnimationMixerPlayable m_Mixer;
|
|
List<BlendSpaceNode> m_Nodes;
|
|
Vector2[] m_Positions;
|
|
float[] m_Weights;
|
|
float m_BlendedClipLength;
|
|
|
|
public float masterSpeed;
|
|
public bool footIk;
|
|
}
|