avatar/Assets/VRCFury/Scripts/Editor/VF/Builder/VRCFuryAnimatorUtils.cs

379 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.Animations;
using UnityEngine;
using VF.Builder.Exceptions;
using VRC.SDK3.Avatars.Components;
using VRC.SDKBase;
namespace VF.Builder {
public class VFAController {
private readonly AnimatorController ctrl;
private readonly VRCAvatarDescriptor.AnimLayerType type;
public VFAController(AnimatorController ctrl, VRCAvatarDescriptor.AnimLayerType type) {
this.ctrl = ctrl;
this.type = type;
}
public VFALayer NewLayer(string name, int insertAt = -1) {
ctrl.AddLayer(name);
var layers = ctrl.layers;
var layer = layers.Last();
if (insertAt >= 0) {
for (var i = layers.Length-1; i > insertAt; i--) {
layers[i] = layers[i - 1];
}
layers[insertAt] = layer;
}
layer.defaultWeight = 1;
layer.stateMachine.anyStatePosition = VFAState.MovePos(layer.stateMachine.entryPosition, 0, 1);
ctrl.layers = layers;
return new VFALayer(layer.stateMachine, this);
}
public void RemoveLayer(int i) {
ctrl.RemoveLayer(i);
}
public VFABool NewTrigger(string name) {
return new VFABool(NewParam(name, AnimatorControllerParameterType.Trigger));
}
public VFABool NewBool(string name, bool def = false) {
return new VFABool(NewParam(name, AnimatorControllerParameterType.Bool, param => param.defaultBool = def));
}
public VFANumber NewFloat(string name, float def = 0) {
return new VFANumber(NewParam(name, AnimatorControllerParameterType.Float, param => param.defaultFloat = def));
}
public VFANumber NewInt(string name, int def = 0) {
return new VFANumber(NewParam(name, AnimatorControllerParameterType.Int, param => param.defaultInt = def));
}
private AnimatorControllerParameter NewParam(string name, AnimatorControllerParameterType type, Action<AnimatorControllerParameter> with = null) {
var exists = Array.Find(ctrl.parameters, other => other.name == name);
if (exists != null) return exists;
ctrl.AddParameter(name, type);
var parameters = ctrl.parameters;
var param = parameters[parameters.Length-1];
if (with != null) with(param);
ctrl.parameters = parameters;
return param;
}
}
public class VFALayer {
private readonly AnimatorStateMachine stateMachine;
private readonly VFAController ctrl;
public VFALayer(AnimatorStateMachine stateMachine, VFAController ctrl) {
this.stateMachine = stateMachine;
this.ctrl = ctrl;
}
public VFAState NewState(string name) {
var lastNode = GetLastNodeForPositioning();
stateMachine.AddState(name);
var node = GetLastNode().Value;
node.state.writeDefaultValues = true;
var state = new VFAState(node, stateMachine);
if (lastNode.HasValue) state.Move(lastNode.Value.position, 0, 1);
else state.Move(stateMachine.entryPosition, 1, 0);
return state;
}
private ChildAnimatorState? GetLastNodeForPositioning() {
var states = stateMachine.states;
var index = Array.FindLastIndex(states, state => !state.state.name.StartsWith("_"));
if (index < 0) return null;
return states[index];
}
private ChildAnimatorState? GetLastNode() {
var states = stateMachine.states;
if (states.Length == 0) return null;
return states[states.Length-1];
}
public AnimatorStateMachine GetRawStateMachine() {
return stateMachine;
}
}
public class VFAState {
private ChildAnimatorState node;
private readonly AnimatorStateMachine stateMachine;
private static readonly float X_OFFSET = 250;
private static readonly float Y_OFFSET = 80;
public VFAState(ChildAnimatorState node, AnimatorStateMachine stateMachine) {
this.node = node;
this.stateMachine = stateMachine;
}
public static Vector3 MovePos(Vector3 orig, float x, float y) {
var pos = orig;
pos.x += x * X_OFFSET;
pos.y += y * Y_OFFSET;
return pos;
}
public VFAState Move(Vector3 orig, float x, float y) {
node.position = MovePos(orig, x, y);
var states = stateMachine.states;
var index = Array.FindIndex(states, n => n.state == node.state);
if (index >= 0) {
states[index] = node;
stateMachine.states = states;
}
return this;
}
public VFAState Move(VFAState other, float x, float y) {
Move(other.node.position, x, y);
return this;
}
public VFAState Move(float x, float y) {
Move(this, x, y);
return this;
}
public VFAState WithAnimation(Motion motion) {
node.state.motion = motion;
return this;
}
public VFAState MotionTime(VFANumber param) {
node.state.timeParameterActive = true;
node.state.timeParameter = param.Name();
return this;
}
public VFAState Speed(float speed) {
node.state.speed = speed;
return this;
}
public VRCAvatarParameterDriver GetDriver(bool local = false) {
foreach (var b in node.state.behaviours) {
var d = b as VRCAvatarParameterDriver;
if (d && d.localOnly == local) return d;
}
var driver = VRCFAnimatorUtils.AddStateMachineBehaviour<VRCAvatarParameterDriver>(node.state);
driver.localOnly = local;
return driver;
}
private VRC_AvatarParameterDriver.Parameter Drives(string param, bool local = false) {
var driver = GetDriver(local);
var p = new VRC_AvatarParameterDriver.Parameter();
p.name = param;
p.type = VRC_AvatarParameterDriver.ChangeType.Set;
driver.parameters.Add(p);
return p;
}
public VFAState Drives(VFABool param, bool value, bool local = false) {
Drives(param.Name(), local).value = value ? 1 : 0;
return this;
}
public VFAState Drives(VFANumber param, float value, bool local = false) {
Drives(param.Name(), local).value = value;
return this;
}
public VFAState DrivesRandom(VFANumber param, float min, float max) {
var p = Drives(param.Name(), true);
p.type = VRC_AvatarParameterDriver.ChangeType.Random;
p.valueMin = min;
p.valueMax = max;
return this;
}
public VFAState DrivesDelta(VFANumber param, float delta) {
var p = Drives(param.Name(), true);
p.type = VRC_AvatarParameterDriver.ChangeType.Add;
p.value = delta;
return this;
}
public VFAState DrivesCopy(VFANumber param, VFANumber source) {
var driver = GetDriver(true);
var p = new VRC_AvatarParameterDriver.Parameter();
p.name = param.Name();
var sourceField = p.GetType().GetField("source");
if (sourceField == null) throw new VRCFBuilderException("VRCFury feature failed to build because VRCSDK is outdated");
sourceField.SetValue(p, source.Name());
// We cast rather than use Copy directly so it doesn't fail to compile on old VRCSDK
p.type = (VRC_AvatarParameterDriver.ChangeType)3; //VRC_AvatarParameterDriver.ChangeType.Copy;
driver.parameters.Add(p);
return this;
}
public VFAEntryTransition TransitionsFromEntry() {
return new VFAEntryTransition(() => stateMachine.AddEntryTransition(node.state));
}
public VFATransition TransitionsFromAny() {
return new VFATransition(() => stateMachine.AddAnyStateTransition(node.state));
}
public VFATransition TransitionsTo(VFAState other) {
return new VFATransition(() => node.state.AddTransition(other.node.state));
}
public VFATransition TransitionsToExit() {
return new VFATransition(() => node.state.AddExitTransition());
}
public AnimatorState GetRaw() {
return node.state;
}
public static void FakeAnyState(params (VFAState,VFACondition)[] states) {
VFACondition above = null;
foreach (var (state, when) in states) {
VFACondition myWhen;
if (state == states[states.Length - 1].Item1) {
if (above == null) throw new VRCFBuilderException("Cannot use FakeAnyState with 1 state.");
myWhen = above.Not();
} else if (above == null) {
above = myWhen = when;
} else {
myWhen = when.And(above.Not());
above = above.Or(when);
}
foreach (var (other,_) in states) {
if (other == state) continue;
other.TransitionsTo(state).When(myWhen);
}
}
}
}
public class VFAParam {
private readonly AnimatorControllerParameter param;
public VFAParam(AnimatorControllerParameter param) {
this.param = param;
}
public string Name() {
return param.name;
}
}
public class VFABool : VFAParam {
public VFABool(AnimatorControllerParameter param) : base(param) {}
public VFACondition IsTrue() {
return new VFACondition(new AnimatorCondition { mode = AnimatorConditionMode.If, parameter = Name(), threshold = 0 });
}
public VFACondition IsFalse() {
return new VFACondition(new AnimatorCondition { mode = AnimatorConditionMode.IfNot, parameter = Name(), threshold = 0 });
}
}
public class VFANumber : VFAParam {
public VFANumber(AnimatorControllerParameter param) : base(param) {}
public VFACondition IsGreaterThan(float num) {
return new VFACondition(new AnimatorCondition { mode = AnimatorConditionMode.Greater, parameter = Name(), threshold = num });
}
public VFACondition IsLessThan(float num) {
return new VFACondition(new AnimatorCondition { mode = AnimatorConditionMode.Less, parameter = Name(), threshold = num });
}
public VFACondition IsEqualTo(float num) {
return new VFACondition(new AnimatorCondition { mode = AnimatorConditionMode.Equals, parameter = Name(), threshold = num });
}
public VFACondition IsNotEqualTo(float num) {
return new VFACondition(new AnimatorCondition { mode = AnimatorConditionMode.NotEqual, parameter = Name(), threshold = num });
}
}
public class VFACondition {
internal IEnumerable<IEnumerable<AnimatorCondition>> transitions;
public VFACondition(AnimatorCondition cond) {
var transition = new List<AnimatorCondition> { cond };
transitions = new List<List<AnimatorCondition>> { transition };
}
public VFACondition(IEnumerable<IEnumerable<AnimatorCondition>> transitions) {
this.transitions = transitions;
}
public VFACondition And(VFACondition other) {
return new VFACondition(AnimatorConditionLogic.And(transitions, other.transitions));
}
public VFACondition Or(VFACondition other) {
return new VFACondition(AnimatorConditionLogic.Or(transitions, other.transitions));
}
public VFACondition Not() {
return new VFACondition(AnimatorConditionLogic.Not(transitions));
}
}
public class VFAEntryTransition {
private readonly Func<AnimatorTransition> transitionProvider;
public VFAEntryTransition(Func<AnimatorTransition> transitionProvider) {
this.transitionProvider = transitionProvider;
}
public VFAEntryTransition When(VFACondition cond) {
foreach (var t in cond.transitions) {
var transition = transitionProvider();
transition.conditions = t.ToArray();
}
return this;
}
}
public class VFATransition {
private readonly List<AnimatorStateTransition> createdTransitions = new List<AnimatorStateTransition>();
private Func<AnimatorStateTransition> transitionProvider;
public VFATransition(Func<AnimatorStateTransition> transitionProvider) {
this.transitionProvider = () => {
var trans = transitionProvider();
trans.duration = 0;
trans.canTransitionToSelf = false;
createdTransitions.Add(trans);
return trans;
};
}
public VFATransition When() {
var transition = transitionProvider();
return this;
}
public VFATransition When(VFACondition cond) {
foreach (var t in cond.transitions) {
var transition = transitionProvider();
transition.conditions = t.ToArray();
}
return this;
}
public VFATransition WithTransitionToSelf() {
foreach (var t in createdTransitions) {
t.canTransitionToSelf = true;
}
var oldProvider = transitionProvider;
transitionProvider = () => {
var trans = oldProvider();
trans.canTransitionToSelf = true;
return trans;
};
return this;
}
public VFATransition WithTransitionDurationSeconds(float time) {
foreach (var t in createdTransitions) {
t.duration = time;
}
var oldProvider = transitionProvider;
transitionProvider = () => {
var trans = oldProvider();
trans.duration = time;
return trans;
};
return this;
}
public VFATransition WithTransitionExitTime(float time) {
foreach (var t in createdTransitions) {
t.hasExitTime = true;
t.exitTime = time;
}
var oldProvider = transitionProvider;
transitionProvider = () => {
var trans = oldProvider();
trans.hasExitTime = true;
trans.exitTime = time;
return trans;
};
return this;
}
}
}