update
This commit is contained in:
@@ -0,0 +1,722 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Editor.Utility
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Animations;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code to create the states and transitions of a particular state machine.
|
||||
/// </summary>
|
||||
public static class AnimatorBuilder
|
||||
{
|
||||
private const string c_StartGeneratedCodeComment = "// ------------------------------------------- Start Generated Code -------------------------------------------";
|
||||
|
||||
/// <summary>
|
||||
/// Storage struct for specifying if a transition originates from the any or entry state transition.
|
||||
/// </summary>
|
||||
public struct AnimatorTransition
|
||||
{
|
||||
private AnimatorTransitionBase m_Transition;
|
||||
private bool m_AnyStateTransition;
|
||||
|
||||
public AnimatorTransitionBase Transition { get { return m_Transition; } }
|
||||
public bool AnyStateTransition { get { return m_AnyStateTransition; } }
|
||||
|
||||
public AnimatorTransition(AnimatorTransitionBase transition, bool anyStateTransition)
|
||||
{
|
||||
m_Transition = transition;
|
||||
m_AnyStateTransition = anyStateTransition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code necessary to recreate the states/transitions that are affected by the specified parameter name and value.
|
||||
/// </summary>
|
||||
/// <param name="animatorController">The animator controller to generate the states/transitions of.</param>
|
||||
/// <param name="parameterName">The name of the animator parameter which should have its states/transitions generated.</param>
|
||||
/// <param name="parameterValue">The value of the animator parameter which should have its states/transitions generated.</param>
|
||||
/// <param name="parentObject">The object which called GenerateAnimator.</param>
|
||||
/// <param name="baseDirectory">The directory that the scripts are located.</param>
|
||||
/// <returns>The file path of the generated code.</returns>
|
||||
public static string GenerateAnimatorCode(AnimatorController animatorController, AnimatorController firstPersonAnimatorController, string parameterName, float parameterValue, object parentObject, string baseDirectory)
|
||||
{
|
||||
var generatedCode = new StringBuilder();
|
||||
var generatedStateMachineCode = GenerateAnimatorCode(animatorController, "animatorController", parameterName, parameterValue, generatedCode);
|
||||
generatedStateMachineCode = GenerateAnimatorCode(firstPersonAnimatorController, "firstPersonAnimatorController", parameterName, parameterValue, generatedCode) || generatedStateMachineCode;
|
||||
|
||||
// The code for the animator controller has been generated. Add it to a new file.
|
||||
if (generatedStateMachineCode) {
|
||||
// Prepare the string for being appended to a file.
|
||||
var parentName = parentObject.GetType().Name;
|
||||
var path = EditorUtility.SaveFilePanel("Save Item", baseDirectory + "/Abilities", parentName + "InspectorDrawer.cs", "cs");
|
||||
if (path.Length != 0 && Application.dataPath.Length < path.Length) {
|
||||
var fileString = new StringBuilder();
|
||||
var fileName = Path.GetFileNameWithoutExtension(path);
|
||||
// If the file already exists then the generated code should be appended to the end.
|
||||
if (File.Exists(path)) {
|
||||
using (var sr = new StreamReader(path)) {
|
||||
var contents = sr.ReadToEnd();
|
||||
var startIndex = -1;
|
||||
if ((startIndex = contents.IndexOf(c_StartGeneratedCodeComment)) > -1) {
|
||||
// Remove the contents after the start generated code comment so the file can start fresh.
|
||||
contents = contents.Remove(startIndex);
|
||||
} else {
|
||||
// Remove the last two curly brackets if the code comment doesn't exist.
|
||||
var count = 0;
|
||||
while (count < 2) {
|
||||
contents = contents.TrimEnd(new char[] { ' ', '\n', '\r', '\t' });
|
||||
contents.Remove(contents.Length - 1);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
contents = contents.TrimEnd(new char[] { ' ', '\t' });
|
||||
fileString.Append(contents);
|
||||
}
|
||||
} else {
|
||||
// Start a new file.
|
||||
fileString.AppendLine();
|
||||
fileString.AppendLine("namespace Opsive.UltimateCharacterController.Editor.Inspectors.Character.Abilities");
|
||||
fileString.AppendLine("{");
|
||||
fileString.AppendLine("\tusing Opsive.UltimateCharacterController.Editor.Utility;");
|
||||
fileString.AppendLine("\tusing UnityEditor;");
|
||||
fileString.AppendLine("\tusing UnityEditor.Animations;");
|
||||
fileString.AppendLine("\tusing UnityEngine;");
|
||||
fileString.AppendLine();
|
||||
fileString.AppendLine("\t/// <summary>");
|
||||
fileString.AppendLine("\t/// Draws a custom inspector for the " + parentName + " Ability.");
|
||||
fileString.AppendLine("\t/// </summary>");
|
||||
fileString.AppendLine("\t[InspectorDrawer(typeof(" + parentObject.GetType().FullName + "))]");
|
||||
fileString.AppendLine("\tpublic class " + fileName + " : AbilityInspectorDrawer");
|
||||
fileString.AppendLine("\t{");
|
||||
}
|
||||
|
||||
// Add the generated code.
|
||||
fileString.AppendLine("\t\t" + c_StartGeneratedCodeComment);
|
||||
fileString.AppendLine("\t\t// ------- Do NOT make any changes below. Changes will be removed when the animator is generated again. -------");
|
||||
fileString.AppendLine("\t\t// ------------------------------------------------------------------------------------------------------------");
|
||||
fileString.AppendLine();
|
||||
fileString.AppendLine("\t\t/// <summary>");
|
||||
fileString.AppendLine("\t\t/// Returns true if the ability can build to the animator.");
|
||||
fileString.AppendLine("\t\t/// </summary>");
|
||||
fileString.AppendLine("\t\tpublic override bool CanBuildAnimator { get { return true; } }");
|
||||
fileString.AppendLine();
|
||||
fileString.AppendLine("\t\t/// <summary>");
|
||||
fileString.AppendLine("\t\t/// An editor only method which can add the abilities states/transitions to the animator.");
|
||||
fileString.AppendLine("\t\t/// </summary>");
|
||||
fileString.AppendLine("\t\t/// <param name=\"animatorController\">The Animator Controller to add the states to.</param>");
|
||||
fileString.AppendLine("\t\t/// <param name=\"firstPersonAnimatorController\">The first person Animator Controller to add the states to.</param>");
|
||||
fileString.AppendLine("\t\tpublic override void BuildAnimator(AnimatorController animatorController, AnimatorController firstPersonAnimatorController)");
|
||||
fileString.AppendLine("\t\t{");
|
||||
fileString.Append(generatedCode.ToString());
|
||||
fileString.AppendLine("\t\t}");
|
||||
fileString.AppendLine("\t}");
|
||||
fileString.AppendLine("}");
|
||||
|
||||
// Save the file.
|
||||
var file = new StreamWriter(path, false);
|
||||
file.Write(fileString.ToString());
|
||||
file.Close();
|
||||
AssetDatabase.Refresh();
|
||||
return path;
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code necessary to recreate the states/transitions that are affected by the specified parameter name and value.
|
||||
/// </summary>
|
||||
/// <param name="animatorController">The animator controller to generate the states/transitions of.</param>
|
||||
/// <param name="animatorVariableName">The name of the animator controller variable name. This name is used within the generated code.</param>
|
||||
/// <param name="parameterName">The name of the animator parameter which should have its states/transitions generated.</param>
|
||||
/// <param name="parameterValue">The value of the animator parameter which should have its states/transitions generated.</param>
|
||||
/// <param name="generatedCode">The final generated code.</param>
|
||||
/// <returns>Was the animator code generated?</returns>
|
||||
private static bool GenerateAnimatorCode(AnimatorController animatorController, string animatorVariableName, string parameterName, float parameterValue, StringBuilder generatedCode)
|
||||
{
|
||||
if (animatorController == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var generateAnimatorCode = false;
|
||||
var motionSet = new HashSet<UnityEngine.Motion>();
|
||||
for (int i = 0; i < animatorController.layers.Length; ++i) {
|
||||
var layer = animatorController.layers[i];
|
||||
var stateMachine = layer.stateMachine;
|
||||
|
||||
var transitions = new List<AnimatorTransition>();
|
||||
FindTransitions(stateMachine.anyStateTransitions, parameterName, parameterValue, transitions, true);
|
||||
FindTransitions(stateMachine.entryTransitions, parameterName, parameterValue, transitions, false);
|
||||
|
||||
// The list of transitions have been found which match the given parameters. The transition has a reference to the state
|
||||
// but not the state machine so another search needs to be done which finds the parent state machine. If the state machine
|
||||
// has the state then that state machine (and any child state machines) should be generated.
|
||||
var stateMachines = new List<ChildAnimatorStateMachine>();
|
||||
for (int j = 0; j < transitions.Count; ++j) {
|
||||
for (int k = 0; k < stateMachine.stateMachines.Length; ++k) {
|
||||
if (stateMachines.Contains(stateMachine.stateMachines[k])) {
|
||||
continue;
|
||||
}
|
||||
if (HasState(stateMachine.stateMachines[k].stateMachine, transitions[j].Transition.destinationState)) {
|
||||
stateMachines.Add(stateMachine.stateMachines[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create all of the states under the highest level child state machine.
|
||||
if (stateMachines.Count > 0) {
|
||||
generateAnimatorCode = true;
|
||||
if (generatedCode.Length > 0) {
|
||||
generatedCode.AppendLine();
|
||||
}
|
||||
|
||||
for (int j = 0; j < stateMachines.Count; ++j) {
|
||||
var baseStateMachine = stateMachines[j];
|
||||
// The state machine should start fresh.
|
||||
var baseStateMachineName = "baseStateMachine" + Mathf.Abs(baseStateMachine.GetHashCode());
|
||||
generatedCode.AppendLine("\t\t\tvar " + baseStateMachineName + " = " + animatorVariableName + ".layers[" + i + "].stateMachine;");
|
||||
generatedCode.AppendLine();
|
||||
generatedCode.AppendLine("\t\t\t// The state machine should start fresh.");
|
||||
generatedCode.AppendLine("\t\t\tfor (int i = 0; i < " + animatorVariableName + ".layers.Length; ++i) {");
|
||||
generatedCode.AppendLine("\t\t\t\tfor (int j = 0; j < " + baseStateMachineName + ".stateMachines.Length; ++j) {");
|
||||
generatedCode.AppendLine("\t\t\t\t\tif (" + baseStateMachineName + ".stateMachines[j].stateMachine.name == \"" + baseStateMachine.stateMachine.name + "\") {");
|
||||
generatedCode.AppendLine("\t\t\t\t\t\t" + baseStateMachineName + ".RemoveStateMachine(" + baseStateMachineName + ".stateMachines[j].stateMachine);");
|
||||
generatedCode.AppendLine("\t\t\t\t\t\tbreak;");
|
||||
generatedCode.AppendLine("\t\t\t\t\t}");
|
||||
generatedCode.AppendLine("\t\t\t\t}");
|
||||
generatedCode.AppendLine("\t\t\t}");
|
||||
generatedCode.AppendLine();
|
||||
|
||||
// Generate the AnimationClips first so they can be later referenced by the states.
|
||||
generatedCode.AppendLine("\t\t\t// AnimationClip references.");
|
||||
GenerateMotions(baseStateMachine, motionSet, generatedCode);
|
||||
generatedCode.AppendLine();
|
||||
|
||||
// Generate the states and transition within each substate machine.
|
||||
GenerateStateMachine(baseStateMachineName, baseStateMachine, generatedCode);
|
||||
}
|
||||
|
||||
for (int j = 0; j < stateMachines.Count; ++j) {
|
||||
var baseStateMachineName = "baseStateMachine" + Mathf.Abs(stateMachines[j].GetHashCode());
|
||||
|
||||
// Add the any state and entry transitions.
|
||||
generatedCode.AppendLine();
|
||||
generatedCode.AppendLine("\t\t\t// State Machine Transitions.");
|
||||
for (int k = 0; k < transitions.Count; ++k) {
|
||||
if (!HasState(stateMachines[j].stateMachine, transitions[k].Transition.destinationState)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GenerateTransition(baseStateMachineName, transitions[k].Transition, false, transitions[j].AnyStateTransition, generatedCode);
|
||||
if (k != transitions.Count - 1) {
|
||||
generatedCode.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return generateAnimatorCode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds any transitions that have a condition with the specified parameter name and value.
|
||||
/// </summary>
|
||||
/// <param name="transitions">The array of transitions to search.</param>
|
||||
/// <param name="parameterName">The name of the parameter to search for.</param>
|
||||
/// <param name="parameterValue">The value of the paramter to search for.</param>
|
||||
/// <param name="validTransitions">A list of transitions that have the matched parameters.</param>
|
||||
/// <param name="anyStateTransition">Does the transition originate from the any state?</param>
|
||||
public static void FindTransitions(AnimatorTransitionBase[] transitions, string parameterName, float parameterValue, List<AnimatorTransition> validTransitions, bool anyStateTransition)
|
||||
{
|
||||
if (transitions == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < transitions.Length; ++i) {
|
||||
if (transitions[i] == null) {
|
||||
continue;
|
||||
}
|
||||
var conditions = transitions[i].conditions;
|
||||
var validTransition = false;
|
||||
for (int j = 0; j < conditions.Length; ++j) {
|
||||
// The transition is valid if the parameter name and value matches.
|
||||
if (conditions[j].mode == AnimatorConditionMode.Equals && conditions[j].parameter == parameterName && conditions[j].threshold == parameterValue) {
|
||||
validTransition = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (validTransition) {
|
||||
validTransitions.Add(new AnimatorTransition(transitions[i], anyStateTransition));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the state machine (or any child state machines) contains the specified state.
|
||||
/// </summary>
|
||||
/// <param name="stateMachine">The state machine to determine if it has the specified state.</param>
|
||||
/// <param name="state">The state to check against.</param>
|
||||
/// <returns>True if the state machine contains the specified state.</returns>
|
||||
public static bool HasState(AnimatorStateMachine stateMachine, AnimatorState state)
|
||||
{
|
||||
if (stateMachine == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine if the current state machine contains the specified state.
|
||||
if (stateMachine.states != null) {
|
||||
for (int i = 0; i < stateMachine.states.Length; ++i) {
|
||||
if (stateMachine.states[i].state == state) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The state wasn't found within the current StateMachine. Search deeper.
|
||||
for (int i = 0; i < stateMachine.stateMachines.Length; ++i) {
|
||||
if (HasState(stateMachine.stateMachines[i].stateMachine, state)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The state machine and child state machines do not contain the state.
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code to recreate the motions within the specified state machine.
|
||||
/// </summary>
|
||||
/// <param name="childStateMachine">The state machine used to search for motions.</param>
|
||||
/// <param name="motionSet">The list of motions which have been already generated.</param>
|
||||
/// <param name="generatedCode">The final generated code.</param>
|
||||
private static void GenerateMotions(ChildAnimatorStateMachine childStateMachine, HashSet<UnityEngine.Motion> motionSet, StringBuilder generatedCode)
|
||||
{
|
||||
var stateMachine = childStateMachine.stateMachine;
|
||||
if (stateMachine.states.Length > 0) {
|
||||
for (int i = 0; i < stateMachine.states.Length; ++i) {
|
||||
var motion = stateMachine.states[i].state.motion;
|
||||
if (motion is BlendTree) {
|
||||
// Blend trees can contain other blend trees so recursion is necessary.
|
||||
GenerateBlendTreeMotions(motion as BlendTree, motionSet, generatedCode);
|
||||
} else {
|
||||
if (motion != null) {
|
||||
GenerateMotion(motion, motionSet, generatedCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Search deeper for any motions.
|
||||
for (int i = 0; i < stateMachine.stateMachines.Length; ++i) {
|
||||
GenerateMotions(stateMachine.stateMachines[i], motionSet, generatedCode);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code to recreate the motions within the specified blend tree.
|
||||
/// </summary>
|
||||
/// <param name="childStateMachine">The blend tree used to search for motions.</param>
|
||||
/// <param name="motionSet">The list of motions which have been already generated.</param>
|
||||
/// <param name="generatedCode">The final generated code.</param>
|
||||
private static void GenerateBlendTreeMotions(BlendTree blendTree, HashSet<UnityEngine.Motion> motionSet, StringBuilder generatedCode)
|
||||
{
|
||||
for (int j = 0; j < blendTree.children.Length; ++j) {
|
||||
var childMotion = blendTree.children[j];
|
||||
if (childMotion.motion != null) {
|
||||
if (childMotion.motion is BlendTree) {
|
||||
// The blend tree contains another blend tree - continue the search.
|
||||
GenerateBlendTreeMotions(childMotion.motion as BlendTree, motionSet, generatedCode);
|
||||
} else {
|
||||
GenerateMotion(childMotion.motion, motionSet, generatedCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code to recreate the specified motion.
|
||||
/// </summary>
|
||||
/// <param name="motion">The motion to generate the code of.</param>
|
||||
/// <param name="motionSet">The list of motions which have been already generated.</param>
|
||||
/// <param name="generatedCode">The final generated code.</param>
|
||||
private static void GenerateMotion(UnityEngine.Motion motion, HashSet<UnityEngine.Motion> motionSet, StringBuilder generatedCode)
|
||||
{
|
||||
if (motionSet.Contains(motion)) {
|
||||
return;
|
||||
}
|
||||
motionSet.Add(motion);
|
||||
|
||||
// Store the GUID so the path can change and not affect the generated animator controller.
|
||||
var uniqueMotionName = UniqueName(motion);
|
||||
var uniqueMotionNamePath = uniqueMotionName + "Path";
|
||||
generatedCode.AppendLine("\t\t\tvar " + uniqueMotionNamePath + " = AssetDatabase.GUIDToAssetPath(\"" +
|
||||
AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(motion as AnimationClip)) + "\"); ");
|
||||
generatedCode.AppendLine("\t\t\tvar " + uniqueMotionName + " = AnimatorBuilder.GetAnimationClip(" + uniqueMotionNamePath + ", \"" + motion.name + "\");");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code to recreate the specified state machine.
|
||||
/// </summary>
|
||||
/// <param name="parentStateMachineName">The name of the parent state machine variable.</param>
|
||||
/// <param name="childStateMachine">The state machine to generate the code of.</param>
|
||||
/// <param name="generatedCode">The final generated code.</param>
|
||||
private static void GenerateStateMachine(string parentStateMachineName, ChildAnimatorStateMachine childStateMachine, StringBuilder generatedCode)
|
||||
{
|
||||
var stateMachine = childStateMachine.stateMachine;
|
||||
generatedCode.AppendLine("\t\t\t// State Machine.");
|
||||
var uniqueStateMachineName = UniqueName(stateMachine);
|
||||
generatedCode.AppendLine("\t\t\tvar " + uniqueStateMachineName + " = " + parentStateMachineName + ".AddStateMachine(\"" +
|
||||
stateMachine.name + "\", " + Vector3String(childStateMachine.position) + ");");
|
||||
generatedCode.AppendLine();
|
||||
|
||||
// Add all the states.
|
||||
if (stateMachine.states.Length > 0) {
|
||||
generatedCode.AppendLine("\t\t\t// States.");
|
||||
for (int i = 0; i < stateMachine.states.Length; ++i) {
|
||||
var state = stateMachine.states[i].state;
|
||||
var uniqueStateName = UniqueName(state);
|
||||
generatedCode.AppendLine("\t\t\tvar " + uniqueStateName + " = " + uniqueStateMachineName + ".AddState(\"" + state.name + "\", " +
|
||||
Vector3String(stateMachine.states[i].position) + ");");
|
||||
if (state.motion is BlendTree) {
|
||||
var blendTree = state.motion as BlendTree;
|
||||
var blendTreeName = uniqueStateName + UniqueName(blendTree);
|
||||
GenerateBlendTree(blendTreeName, blendTree, generatedCode, 3);
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".motion = " + blendTreeName + ";");
|
||||
} else if (state.motion != null) {
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".motion = " + UniqueName(state.motion) + ";");
|
||||
}
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".cycleOffset = " + state.cycleOffset + "f;");
|
||||
if (!string.IsNullOrEmpty(state.cycleOffsetParameter)) {
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".cycleOffsetParameter = \"" + state.cycleOffsetParameter + "\";");
|
||||
}
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".cycleOffsetParameterActive = " + BoolString(state.cycleOffsetParameterActive) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".iKOnFeet = " + BoolString(state.iKOnFeet) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".mirror = " + BoolString(state.mirror) + ";");
|
||||
if (!string.IsNullOrEmpty(state.mirrorParameter)) {
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".mirrorParameter = \"" + state.mirrorParameter + "\";");
|
||||
}
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".mirrorParameterActive = " + BoolString(state.mirrorParameterActive) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".speed = " + state.speed + "f;");
|
||||
if (!string.IsNullOrEmpty(state.speedParameter)) {
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".speedParameter = \"" + state.speedParameter + "\";");
|
||||
}
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".speedParameterActive = " + BoolString(state.speedParameterActive) + ";");
|
||||
if (!string.IsNullOrEmpty(state.tag)) {
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".tag = \"" + state.tag + "\";");
|
||||
}
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateName + ".writeDefaultValues = " + BoolString(state.writeDefaultValues) + ";");
|
||||
generatedCode.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the any state transitions.
|
||||
if (stateMachine.anyStateTransitions.Length > 0) {
|
||||
generatedCode.AppendLine("\t\t\t// Any State Transitions.");
|
||||
for (int i = 0; i < stateMachine.anyStateTransitions.Length; ++i) {
|
||||
GenerateTransition(uniqueStateMachineName, stateMachine.anyStateTransitions[i], false, true, generatedCode);
|
||||
generatedCode.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the entry transitions.
|
||||
if (stateMachine.entryTransitions.Length > 0) {
|
||||
generatedCode.AppendLine("\t\t\t// Entry Transitions.");
|
||||
for (int i = 0; i < stateMachine.entryTransitions.Length; ++i) {
|
||||
GenerateTransition(uniqueStateMachineName, stateMachine.entryTransitions[i], false, false, generatedCode);
|
||||
generatedCode.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the state transitions.
|
||||
if (stateMachine.states.Length > 0) {
|
||||
generatedCode.AppendLine("\t\t\t// State Transitions.");
|
||||
for (int i = 0; i < stateMachine.states.Length; ++i) {
|
||||
var state = stateMachine.states[i].state;
|
||||
var uniqueStateName = UniqueName(stateMachine.states[i].state);
|
||||
for (int j = 0; j < state.transitions.Length; ++j) {
|
||||
GenerateTransition(uniqueStateName, state.transitions[j], true, false, generatedCode);
|
||||
generatedCode.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all of the state machines.
|
||||
for (int i = 0; i < stateMachine.stateMachines.Length; ++i) {
|
||||
GenerateStateMachine(uniqueStateMachineName, stateMachine.stateMachines[i], generatedCode);
|
||||
}
|
||||
|
||||
// Add the state machine defaults.
|
||||
generatedCode.AppendLine("\t\t\t// State Machine Defaults.");
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateMachineName + ".anyStatePosition = " + Vector3String(stateMachine.anyStatePosition) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateMachineName + ".defaultState = " + UniqueName(stateMachine.defaultState) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateMachineName + ".entryPosition = " + Vector3String(stateMachine.entryPosition) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateMachineName + ".exitPosition = " + Vector3String(stateMachine.exitPosition) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + uniqueStateMachineName + ".parentStateMachinePosition = " + Vector3String(stateMachine.parentStateMachinePosition) + ";");
|
||||
generatedCode.AppendLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code to recreate the specified blend tree.
|
||||
/// </summary>
|
||||
/// <param name="blendTreeName">The name of the blend tree variable.</param>
|
||||
/// <param name="blendTree">The blend tree to generate the code of.</param>
|
||||
/// <param name="generatedCode">The final generated code.</param>
|
||||
/// <param name="indentLevel">The number of indentations for the generated code.</param>
|
||||
private static void GenerateBlendTree(string blendTreeName, BlendTree blendTree, StringBuilder generatedCode, int indentLevel)
|
||||
{
|
||||
var indentation = "";
|
||||
for (int i = 0; i < indentLevel; ++i) {
|
||||
indentation += "\t";
|
||||
}
|
||||
generatedCode.AppendLine(indentation + "var " + blendTreeName + " = new BlendTree();");
|
||||
generatedCode.AppendLine(indentation + "AssetDatabase.AddObjectToAsset(" + blendTreeName + ", animatorController);");
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".hideFlags = HideFlags.HideInHierarchy;");
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".blendParameter = \"" + blendTree.blendParameter + "\";");
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".blendParameterY = \"" + blendTree.blendParameterY + "\";");
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".blendType = BlendTreeType." + blendTree.blendType + ";");
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".maxThreshold = " + blendTree.maxThreshold + "f;");
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".minThreshold = " + blendTree.minThreshold + "f;");
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".name = \"" + blendTree.name + "\";");
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".useAutomaticThresholds = " + BoolString(blendTree.useAutomaticThresholds) + ";");
|
||||
for (int j = 0; j < blendTree.children.Length; ++j) {
|
||||
var childName = blendTreeName + "Child" + j;
|
||||
if (blendTree.children[j].motion == null) {
|
||||
generatedCode.AppendLine(indentation + "var " + childName + " = new ChildMotion();");
|
||||
} else {
|
||||
var name = UniqueName(blendTree.children[j].motion);
|
||||
if (blendTree.children[j].motion is BlendTree) {
|
||||
name = blendTreeName + name;
|
||||
GenerateBlendTree(name, blendTree.children[j].motion as BlendTree, generatedCode, indentLevel);
|
||||
}
|
||||
generatedCode.AppendLine(indentation + "var " + childName + " = new ChildMotion();");
|
||||
generatedCode.AppendLine(indentation + childName + ".motion = " + name + ";");
|
||||
}
|
||||
generatedCode.AppendLine(indentation + childName + ".cycleOffset = " + blendTree.children[j].cycleOffset + "f;");
|
||||
generatedCode.AppendLine(indentation + childName + ".directBlendParameter = \"" + blendTree.children[j].directBlendParameter + "\";");
|
||||
generatedCode.AppendLine(indentation + childName + ".mirror = " + BoolString(blendTree.children[j].mirror) + ";");
|
||||
generatedCode.AppendLine(indentation + childName + ".position = " + Vector2String(blendTree.children[j].position) + ";");
|
||||
generatedCode.AppendLine(indentation + childName + ".threshold = " + blendTree.children[j].threshold + "f;");
|
||||
generatedCode.AppendLine(indentation + childName + ".timeScale = " + blendTree.children[j].timeScale + "f;");
|
||||
}
|
||||
// The children are only updated when assigned initially. Assign a new array so the children references will be correct.
|
||||
generatedCode.AppendLine(indentation + blendTreeName + ".children = new ChildMotion[] {");
|
||||
for (int j = 0; j < blendTree.children.Length; ++j) {
|
||||
var childName = blendTreeName + "Child" + j;
|
||||
generatedCode.AppendLine(indentation + "\t" + childName + (j < blendTree.children.Length - 1 ? "," : ""));
|
||||
}
|
||||
generatedCode.AppendLine(indentation + "};");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the code to recreate the specified transition.
|
||||
/// </summary>
|
||||
/// <param name="stateMachineName">The name of the state machine variable that the transition has been added to.</param>
|
||||
/// <param name="transition">The transition to generate the code of.</param>
|
||||
/// <param name="isStateTransition">Is the transition a state transition?</param>
|
||||
/// <param name="isAnyStateTransition">Is the transition an AnyState transition?</param>
|
||||
/// <param name="generatedCode">The final generated code.</param>
|
||||
private static void GenerateTransition(string stateMachineName, AnimatorTransitionBase transition, bool isStateTransition, bool isAnyStateTransition, StringBuilder generatedCode)
|
||||
{
|
||||
if (transition.destinationState == null && !transition.isExit) {
|
||||
return;
|
||||
}
|
||||
|
||||
var transitionName = UniqueName(transition);
|
||||
if (isStateTransition || isAnyStateTransition) {
|
||||
if (isAnyStateTransition) {
|
||||
generatedCode.AppendLine("\t\t\tvar " + transitionName + " = " + stateMachineName + ".AddAnyStateTransition(" + UniqueName(transition.destinationState) + ");");
|
||||
} else if (transition.isExit) {
|
||||
generatedCode.AppendLine("\t\t\tvar " + transitionName + " = " + stateMachineName + ".AddExitTransition();");
|
||||
} else {
|
||||
generatedCode.AppendLine("\t\t\tvar " + transitionName + " = " + stateMachineName + ".AddTransition(" + UniqueName(transition.destinationState) + ");");
|
||||
}
|
||||
var stateTransition = transition as AnimatorStateTransition;
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".canTransitionToSelf = " + BoolString(stateTransition.canTransitionToSelf) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".duration = " + stateTransition.duration + "f;");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".exitTime = " + stateTransition.exitTime + "f;");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".hasExitTime = " + BoolString(stateTransition.hasExitTime) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".hasFixedDuration = " + BoolString(stateTransition.hasFixedDuration) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".interruptionSource = TransitionInterruptionSource." + stateTransition.interruptionSource + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".offset = " + stateTransition.offset + "f;");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".orderedInterruption = " + BoolString(stateTransition.orderedInterruption) + ";");
|
||||
} else {
|
||||
generatedCode.AppendLine("\t\t\tvar " + transitionName + " = " + stateMachineName + ".AddEntryTransition(" + UniqueName(transition.destinationState) + ");");
|
||||
}
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".isExit = " + BoolString(transition.isExit) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".mute = " + BoolString(transition.mute) + ";");
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".solo = " + BoolString(transition.solo) + ";");
|
||||
for (int i = 0; i < transition.conditions.Length; ++i) {
|
||||
generatedCode.AppendLine("\t\t\t" + transitionName + ".AddCondition(AnimatorConditionMode." + transition.conditions[i].mode.ToString() + ", " +
|
||||
transition.conditions[i].threshold + "f, \"" + transition.conditions[i].parameter + "\");");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the Ultimate Character Controller parameters to the animator controller.
|
||||
/// </summary>
|
||||
/// <param name="animatorController">The animator controller to add the parameters to.</param>
|
||||
public static void AddParameters(AnimatorController animatorController)
|
||||
{
|
||||
if (!HasParameter(animatorController, "HorizontalMovement")) {
|
||||
animatorController.AddParameter("HorizontalMovement", AnimatorControllerParameterType.Float);
|
||||
}
|
||||
if (!HasParameter(animatorController, "ForwardMovement")) {
|
||||
animatorController.AddParameter("ForwardMovement", AnimatorControllerParameterType.Float);
|
||||
}
|
||||
if (!HasParameter(animatorController, "Pitch")) {
|
||||
animatorController.AddParameter("Pitch", AnimatorControllerParameterType.Float);
|
||||
}
|
||||
if (!HasParameter(animatorController, "Yaw")) {
|
||||
animatorController.AddParameter("Yaw", AnimatorControllerParameterType.Float);
|
||||
}
|
||||
if (!HasParameter(animatorController, "Speed")) {
|
||||
animatorController.AddParameter("Speed", AnimatorControllerParameterType.Float);
|
||||
}
|
||||
if (!HasParameter(animatorController, "Height")) {
|
||||
animatorController.AddParameter("Height", AnimatorControllerParameterType.Float);
|
||||
}
|
||||
if (!HasParameter(animatorController, "Moving")) {
|
||||
animatorController.AddParameter("Moving", AnimatorControllerParameterType.Bool);
|
||||
}
|
||||
if (!HasParameter(animatorController, "Aiming")) {
|
||||
animatorController.AddParameter("Aiming", AnimatorControllerParameterType.Bool);
|
||||
}
|
||||
if (!HasParameter(animatorController, "MovementSetID")) {
|
||||
animatorController.AddParameter("MovementSetID", AnimatorControllerParameterType.Int);
|
||||
}
|
||||
if (!HasParameter(animatorController, "AbilityIndex")) {
|
||||
animatorController.AddParameter("AbilityIndex", AnimatorControllerParameterType.Int);
|
||||
}
|
||||
if (!HasParameter(animatorController, "AbilityChange")) {
|
||||
animatorController.AddParameter("AbilityChange", AnimatorControllerParameterType.Trigger);
|
||||
}
|
||||
if (!HasParameter(animatorController, "AbilityIntData")) {
|
||||
animatorController.AddParameter("AbilityIntData", AnimatorControllerParameterType.Int);
|
||||
}
|
||||
if (!HasParameter(animatorController, "AbilityFloatData")) {
|
||||
animatorController.AddParameter("AbilityFloatData", AnimatorControllerParameterType.Float);
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
var parameterName = string.Format("Slot{0}ItemID", i);
|
||||
if (!HasParameter(animatorController, parameterName)) {
|
||||
animatorController.AddParameter(parameterName, AnimatorControllerParameterType.Int);
|
||||
}
|
||||
parameterName = string.Format("Slot{0}ItemStateIndex", i);
|
||||
if (!HasParameter(animatorController, parameterName)) {
|
||||
animatorController.AddParameter(parameterName, AnimatorControllerParameterType.Int);
|
||||
}
|
||||
parameterName = string.Format("Slot{0}ItemStateIndexChange", i);
|
||||
if (!HasParameter(animatorController, parameterName)) {
|
||||
animatorController.AddParameter(parameterName, AnimatorControllerParameterType.Trigger);
|
||||
}
|
||||
parameterName = string.Format("Slot{0}ItemSubstateIndex", i);
|
||||
if (!HasParameter(animatorController, parameterName)) {
|
||||
animatorController.AddParameter(parameterName, AnimatorControllerParameterType.Int);
|
||||
}
|
||||
}
|
||||
if (!HasParameter(animatorController, "LegIndex")) {
|
||||
animatorController.AddParameter("LegIndex", AnimatorControllerParameterType.Float);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the animator controller have the specified parameter?
|
||||
/// </summary>
|
||||
/// <param name="animatorController">The animator controller to determine if it has the specified parameter.</param>
|
||||
/// <param name="parameterName">The name of the parameter.</param>
|
||||
/// <returns>True if the animator controller has the specified parameter?</returns>
|
||||
private static bool HasParameter(AnimatorController animatorController, string parameterName)
|
||||
{
|
||||
for (int i = 0; i < animatorController.parameters.Length; ++i) {
|
||||
if (animatorController.parameters[i].name == parameterName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a unique name for the specified object.
|
||||
/// </summary>
|
||||
/// <param name="unityObject">The object to generate the unique name of.</param>
|
||||
/// <returns>The unique name for the specified object.</returns>
|
||||
private static string UniqueName(Object unityObject)
|
||||
{
|
||||
var name = AlphanumericString(unityObject.name + unityObject.GetType().Name + unityObject.GetHashCode());
|
||||
return char.ToLower(name[0]) + name.Substring(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string version of a new Vector2.
|
||||
/// </summary>
|
||||
/// <param name="value">The value of the Vector2.</param>
|
||||
/// <returns>The string version of a new Vector2.</returns>
|
||||
private static string Vector2String(Vector2 value)
|
||||
{
|
||||
return "new Vector2(" + value.x + "f, " + value.y + "f)";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string version of a new Vector3.
|
||||
/// </summary>
|
||||
/// <param name="value">The value of the Vector3.</param>
|
||||
/// <returns>The string version of a new Vector3.</returns>
|
||||
private static string Vector3String(Vector3 value)
|
||||
{
|
||||
return "new Vector3(" + value.x + "f, " + value.y + "f, " + value.z + "f)";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the string version of a new bool.
|
||||
/// </summary>
|
||||
/// <param name="value">The value of the bool.</param>
|
||||
/// <returns>The string version of a new bool.</returns>
|
||||
private static string BoolString(bool value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an alphanumeric string.
|
||||
/// </summary>
|
||||
/// <param name="value">The value of the string.</param>
|
||||
/// <returns>An alphanumeric string.</returns>
|
||||
private static string AlphanumericString(string value)
|
||||
{
|
||||
return Regex.Replace(value, "[^a-zA-Z0-9]+", "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the AnimationClip at the specified path with the specified name.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the AnimationClip.</param>
|
||||
/// <param name="name">The name of the AnimationClip.</param>
|
||||
/// <returns>The AnimationClip at the specified path with the specified name. Can be null.</returns>
|
||||
public static AnimationClip GetAnimationClip(string path, string name)
|
||||
{
|
||||
var animationClips = AssetDatabase.LoadAllAssetsAtPath(path);
|
||||
if (animationClips != null) {
|
||||
// The path may point to an asset with multiple AnimationClips within. Search through that asset for the AnimationClip with the specified name.
|
||||
for (int i = 0; i < animationClips.Length; i++) {
|
||||
if (animationClips[i] is AnimationClip && animationClips[i].name == name) {
|
||||
return animationClips[i] as AnimationClip;
|
||||
}
|
||||
}
|
||||
}
|
||||
// No animation clips with the specified name were found.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24869e8b6bc27e84a93b1c8155139c15
|
||||
timeCreated: 1513800736
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,192 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Editor.Utility
|
||||
{
|
||||
using Opsive.Shared.Utility;
|
||||
using UnityEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Editor script which will define or remove the Ultimate Character Controller compiler symbols so the components are aware of the asset import status.
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
public class DefineCompilerSymbols
|
||||
{
|
||||
private static string s_FirstPersonControllerSymbol = "FIRST_PERSON_CONTROLLER";
|
||||
private static string s_ThirdPersonControllerSymbol = "THIRD_PERSON_CONTROLLER";
|
||||
private static string s_ShooterSymbol = "ULTIMATE_CHARACTER_CONTROLLER_SHOOTER";
|
||||
private static string s_FirstPersonShooterSymbol = "FIRST_PERSON_SHOOTER";
|
||||
private static string s_MeleeSymbol = "ULTIMATE_CHARACTER_CONTROLLER_MELEE";
|
||||
private static string s_FirstPersonMeleeSymbol = "FIRST_PERSON_MELEE";
|
||||
private static string s_MultiplayerSymbol = "ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER";
|
||||
private static string s_VRSymbol = "ULTIMATE_CHARACTER_CONTROLLER_VR";
|
||||
#if UNITY_2019_2
|
||||
private static string s_LWRPSymbol = "ULTIMATE_CHARACTER_CONTROLLER_LWRP";
|
||||
#elif UNITY_2019_3_OR_NEWER
|
||||
private static string s_UniversalRPSymbol = "ULTIMATE_CHARACTER_CONTROLLER_UNIVERSALRP";
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// If the specified classes exist then the compiler symbol should be defined, otherwise the symbol should be removed.
|
||||
/// </summary>
|
||||
static DefineCompilerSymbols()
|
||||
{
|
||||
// The First Person Controller Combat MovementType will exist when the First Person Controller asset is imported.
|
||||
var firstPersonControllerExists = TypeUtility.GetType("Opsive.UltimateCharacterController.FirstPersonController.Character.MovementTypes.Combat") != null;
|
||||
#if FIRST_PERSON_CONTROLLER
|
||||
if (!firstPersonControllerExists) {
|
||||
RemoveSymbol(s_FirstPersonControllerSymbol);
|
||||
}
|
||||
#else
|
||||
if (firstPersonControllerExists) {
|
||||
AddSymbol(s_FirstPersonControllerSymbol);
|
||||
}
|
||||
#endif
|
||||
|
||||
// The Third Person Controller Combat MovementType will exist when the Third Person Controller asset is imported.
|
||||
var thirdPersonControllerExists = TypeUtility.GetType("Opsive.UltimateCharacterController.ThirdPersonController.Character.MovementTypes.Combat") != null;
|
||||
#if THIRD_PERSON_CONTROLLER
|
||||
if (!thirdPersonControllerExists) {
|
||||
RemoveSymbol(s_ThirdPersonControllerSymbol);
|
||||
}
|
||||
#else
|
||||
if (thirdPersonControllerExists) {
|
||||
AddSymbol(s_ThirdPersonControllerSymbol);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Shootable Weapon will exist if the shooter controller is imported.
|
||||
var shootableWeaponExists = TypeUtility.GetType("Opsive.UltimateCharacterController.Items.Actions.ShootableWeapon") != null;
|
||||
#if ULTIMATE_CHARACTER_CONTROLLER_SHOOTER
|
||||
if (!shootableWeaponExists) {
|
||||
RemoveSymbol(s_ShooterSymbol);
|
||||
}
|
||||
#else
|
||||
if (shootableWeaponExists) {
|
||||
AddSymbol(s_ShooterSymbol);
|
||||
}
|
||||
#endif
|
||||
|
||||
// First Person Shootable Weapon Properties will exist if the first person shootable controller is imported.
|
||||
var firstPersonShootableWeaponExists = TypeUtility.GetType("Opsive.UltimateCharacterController.FirstPersonController.Items.FirstPersonShootableWeaponProperties") != null;
|
||||
#if FIRST_PERSON_SHOOTER
|
||||
if (!firstPersonShootableWeaponExists) {
|
||||
RemoveSymbol(s_FirstPersonShooterSymbol);
|
||||
}
|
||||
#else
|
||||
if (firstPersonShootableWeaponExists) {
|
||||
AddSymbol(s_FirstPersonShooterSymbol);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Melee Weapon will exist if the melee controller is imported.
|
||||
var meleeWeaponExists = TypeUtility.GetType("Opsive.UltimateCharacterController.Items.Actions.MeleeWeapon") != null;
|
||||
#if ULTIMATE_CHARACTER_CONTROLLER_MELEE
|
||||
if (!meleeWeaponExists) {
|
||||
RemoveSymbol(s_MeleeSymbol);
|
||||
}
|
||||
#else
|
||||
if (meleeWeaponExists) {
|
||||
AddSymbol(s_MeleeSymbol);
|
||||
}
|
||||
#endif
|
||||
|
||||
// First Person Melee Weapon Properties will exist if the first person melee controller is imported.
|
||||
var firstPersonMeleeWeaponExists = TypeUtility.GetType("Opsive.UltimateCharacterController.FirstPersonController.Items.FirstPersonMeleeWeaponProperties") != null;
|
||||
#if FIRST_PERSON_MELEE
|
||||
if (!firstPersonMeleeWeaponExists) {
|
||||
RemoveSymbol(s_FirstPersonMeleeSymbol);
|
||||
}
|
||||
#else
|
||||
if (firstPersonMeleeWeaponExists) {
|
||||
AddSymbol(s_FirstPersonMeleeSymbol);
|
||||
}
|
||||
#endif
|
||||
|
||||
// INetworkCharacter will exist if the multiplayer add-on is imported.
|
||||
var multiplayerExists = TypeUtility.GetType("Opsive.UltimateCharacterController.AddOns.Multiplayer.Character.NetworkCharacterLocomotionHandler") != null;
|
||||
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
|
||||
if (!multiplayerExists) {
|
||||
RemoveSymbol(s_MultiplayerSymbol);
|
||||
}
|
||||
#else
|
||||
if (multiplayerExists) {
|
||||
AddSymbol(s_MultiplayerSymbol);
|
||||
}
|
||||
#endif
|
||||
|
||||
// VRViewType will exist if the VR add-on is imported.
|
||||
var vrExists = TypeUtility.GetType("Opsive.UltimateCharacterController.AddOns.VR.Editor.VRAddOnInspector") != null;
|
||||
#if ULTIMATE_CHARACTER_CONTROLLER_VR
|
||||
if (!vrExists) {
|
||||
RemoveSymbol(s_VRSymbol);
|
||||
}
|
||||
#else
|
||||
if (vrExists) {
|
||||
AddSymbol(s_VRSymbol);
|
||||
}
|
||||
#endif
|
||||
|
||||
// The LWRP/URP data will exists when the LWRP or URP is imported. This assembly definition must be added to the Opsive.UltimateCaracterController.Editor assembly definition.
|
||||
#if UNITY_2019_2
|
||||
var lwrpExists = TypeUtility.GetType("UnityEngine.Rendering.LWRP.ForwardRendererData") != null;
|
||||
#if ULTIMATE_CHARACTER_CONTROLLER_LWRP
|
||||
if (!lwrpExists) {
|
||||
RemoveSymbol(s_LWRPSymbol);
|
||||
}
|
||||
#else
|
||||
if (lwrpExists) {
|
||||
AddSymbol(s_LWRPSymbol);
|
||||
}
|
||||
#endif
|
||||
#elif UNITY_2019_3_OR_NEWER
|
||||
var universalrpExists = TypeUtility.GetType("UnityEngine.Rendering.Universal.ForwardRendererData") != null;
|
||||
#if ULTIMATE_CHARACTER_CONTROLLER_UNIVERSALRP
|
||||
if (!universalrpExists) {
|
||||
RemoveSymbol(s_UniversalRPSymbol);
|
||||
}
|
||||
#else
|
||||
if (universalrpExists) {
|
||||
AddSymbol(s_UniversalRPSymbol);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified symbol to the compiler definitions.
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol to add.</param>
|
||||
private static void AddSymbol(string symbol)
|
||||
{
|
||||
var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
if (symbols.Contains(symbol)) {
|
||||
return;
|
||||
}
|
||||
symbols += (";" + symbol);
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, symbols);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the specified symbol from the compiler definitions.
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol to remove.</param>
|
||||
private static void RemoveSymbol(string symbol)
|
||||
{
|
||||
var symbols = PlayerSettings.GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup);
|
||||
if (!symbols.Contains(symbol)) {
|
||||
return;
|
||||
}
|
||||
if (symbols.Contains(";" + symbol)) {
|
||||
symbols = symbols.Replace(";" + symbol, "");
|
||||
} else {
|
||||
symbols = symbols.Replace(symbol, "");
|
||||
}
|
||||
PlayerSettings.SetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup, symbols);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36ee0998725ed7d469b2f559311ad22c
|
||||
timeCreated: 1486343712
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,171 @@
|
||||
/// ---------------------------------------------
|
||||
/// Ultimate Character Controller
|
||||
/// Copyright (c) Opsive. All Rights Reserved.
|
||||
/// https://www.opsive.com
|
||||
/// ---------------------------------------------
|
||||
|
||||
|
||||
namespace Opsive.UltimateCharacterController.Editor.Utility
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the Unity input manager with the correct button bindings.
|
||||
/// </summary>
|
||||
public class UnityInputBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The elements axis type within the InputManager.
|
||||
/// </summary>
|
||||
public enum AxisType
|
||||
{
|
||||
KeyMouseButton, Mouse, Joystick
|
||||
}
|
||||
/// <summary>
|
||||
/// The element's axis number within the InputManager.
|
||||
/// </summary>
|
||||
public enum AxisNumber
|
||||
{
|
||||
X, Y, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Eleven, Twelve, Thirteen, Fourteen, Fifteen, Sixteen, Seventeen, Eighteen, Nineteen, Twenty
|
||||
}
|
||||
|
||||
private static Dictionary<string, int> s_FoundAxes;
|
||||
public static Dictionary<string, int> FoundAxes { get { return s_FoundAxes; } set { s_FoundAxes = value; } }
|
||||
|
||||
/// <summary>
|
||||
/// Update the Input Manager to add all of the correct controls.
|
||||
/// </summary>
|
||||
public static void UpdateInputManager()
|
||||
{
|
||||
var serializedObject = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/InputManager.asset")[0]);
|
||||
var axesProperty = serializedObject.FindProperty("m_Axes");
|
||||
|
||||
// Unity defined axis:
|
||||
AddInputAxis(axesProperty, "Horizontal", "left", "right", "a", "d", 1000, 0.001f, 3, true, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Vertical", "down", "up", "s", "w", 1000, 0.001f, 3, true, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Fire1", "", "left ctrl", "", "mouse 0", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Fire2", "", "", "", "mouse 1", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Fire3", "", "left shift", "", "mouse 2", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Jump", "", "space", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Mouse X", "", "", "", "", 0, 0, 0.1f, false, false, AxisType.Mouse, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Mouse Y", "", "", "", "", 0, 0, 0.1f, false, false, AxisType.Mouse, AxisNumber.Y);
|
||||
AddInputAxis(axesProperty, "Mouse ScrollWheel", "", "", "", "", 0, 0, 0.1f, false, false, AxisType.Mouse, AxisNumber.Three);
|
||||
AddInputAxis(axesProperty, "Horizontal", "", "", "", "", 1000, 0.19f, 1, false, false, AxisType.Joystick, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Vertical", "", "", "", "", 1000, 0.19f, 1, false, true, AxisType.Joystick, AxisNumber.Y);
|
||||
AddInputAxis(axesProperty, "Fire1", "", "", "", "", 1000, 0.001f, 1000, false, false, AxisType.Joystick, AxisNumber.Ten);
|
||||
AddInputAxis(axesProperty, "Fire2", "", "", "", "", 1000, 0.001f, 1000, false, false, AxisType.Joystick, AxisNumber.Nine);
|
||||
AddInputAxis(axesProperty, "Fire3", "", "joystick button 2", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Jump", "", "joystick button 0", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Mouse X", "", "", "", "", 0, 0.19f, 1, false, false, AxisType.Joystick, AxisNumber.Four);
|
||||
AddInputAxis(axesProperty, "Mouse Y", "", "", "", "", 0, 0.19f, 1, false, true, AxisType.Joystick, AxisNumber.Five);
|
||||
|
||||
// New axis:
|
||||
AddInputAxis(axesProperty, "Alt Horizontal", "q", "e", "", "", 1000, 0.19f, 3, true, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Change Speeds", "", "left shift", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Crouch", "", "c", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Crouch", "", "joystick button 9", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Toggle Perspective", "", "v", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Toggle Perspective", "", "joystick button 8", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Toggle Item Equip", "", "t", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Next Item", "", "e", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Next Item", "", "joystick button 3", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Previous Item", "", "q", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip First Item", "", "1", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Second Item", "", "2", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Third Item", "", "3", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Fourth Item", "", "4", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Fifth Item", "", "5", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Sixth Item", "", "6", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Seventh Item", "", "7", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Eighth Item", "", "8", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Ninth Item", "", "9", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Equip Tenth Item", "", "0", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Reload", "", "r", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Reload", "", "joystick button 2", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Drop", "", "y", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Grenade", "", "g", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Grenade", "", "joystick button 5", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Lean", "x", "z", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "SecondaryUse", "", "b", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "SecondaryUse", "", "joystick button 4", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Action", "", "f", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
AddInputAxis(axesProperty, "Action", "", "joystick button 1", "", "", 1000, 0.001f, 1000, false, false, AxisType.KeyMouseButton, AxisNumber.X);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new axis to the InputManager.
|
||||
/// </summary>
|
||||
/// <param name="axesProperty">The array of all of the axes.</param>
|
||||
/// <param name="name">The name of the new axis.</param>
|
||||
/// <param name="negativeButton">The name of the negative button of the new axis.</param>
|
||||
/// <param name="positiveButton">The name of the positive button of the new axis.</param>
|
||||
/// <param name="altNegativeButton">The name of the alternative negative button of the new axis.</param>
|
||||
/// <param name="altPositiveButton">The name of the alternative positive button of the new axis.</param>
|
||||
/// <param name="sensitivity">The sensitivity of the new axis.</param>
|
||||
/// <param name="gravity">The gravity of the new axis.</param>
|
||||
/// <param name="dead">The dead value of the new axis.</param>
|
||||
/// <param name="snap">Does the new axis snap?</param>
|
||||
/// <param name="axisType">The type of axis to add.</param>
|
||||
public static void AddInputAxis(SerializedProperty axesProperty, string name, string negativeButton, string positiveButton,
|
||||
string altNegativeButton, string altPositiveButton, float gravity, float dead, float sensitivity, bool snap, bool invert, AxisType axisType, AxisNumber axisNumber)
|
||||
{
|
||||
var property = FindAxisProperty(axesProperty, name);
|
||||
property.FindPropertyRelative("m_Name").stringValue = name;
|
||||
property.FindPropertyRelative("negativeButton").stringValue = negativeButton;
|
||||
property.FindPropertyRelative("positiveButton").stringValue = positiveButton;
|
||||
property.FindPropertyRelative("altNegativeButton").stringValue = altNegativeButton;
|
||||
property.FindPropertyRelative("altPositiveButton").stringValue = altPositiveButton;
|
||||
property.FindPropertyRelative("gravity").floatValue = gravity;
|
||||
property.FindPropertyRelative("dead").floatValue = dead;
|
||||
property.FindPropertyRelative("sensitivity").floatValue = sensitivity;
|
||||
property.FindPropertyRelative("snap").boolValue = snap;
|
||||
property.FindPropertyRelative("invert").boolValue = invert;
|
||||
property.FindPropertyRelative("type").intValue = (int)axisType;
|
||||
property.FindPropertyRelative("axis").intValue = (int)axisNumber;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for a property with the given name and axis type within the axes property array. If no property is found then a new one will be created.
|
||||
/// </summary>
|
||||
/// <param name="axesProperty">The array to search through.</param>
|
||||
/// <param name="name">The name of the property.</param>
|
||||
/// <returns></returns>
|
||||
private static SerializedProperty FindAxisProperty(SerializedProperty axesProperty, string name)
|
||||
{
|
||||
SerializedProperty foundProperty = null;
|
||||
// As new axes are being added make sure a previous axis is not overwritten because the name matches.
|
||||
var existingCount = 0;
|
||||
if (s_FoundAxes == null) {
|
||||
s_FoundAxes = new Dictionary<string, int>();
|
||||
}
|
||||
s_FoundAxes.TryGetValue(name, out existingCount);
|
||||
var localCount = 0;
|
||||
for (int i = 0; i < axesProperty.arraySize; ++i) {
|
||||
var property = axesProperty.GetArrayElementAtIndex(i);
|
||||
if (property.FindPropertyRelative("m_Name").stringValue.Equals(name)) {
|
||||
if (localCount == existingCount) {
|
||||
foundProperty = property;
|
||||
break;
|
||||
}
|
||||
localCount++;
|
||||
}
|
||||
}
|
||||
if (existingCount == 0) {
|
||||
s_FoundAxes.Add(name, 1);
|
||||
} else {
|
||||
s_FoundAxes[name] = existingCount + 1;
|
||||
}
|
||||
|
||||
// If no property was found then create a new one.
|
||||
if (foundProperty == null) {
|
||||
axesProperty.InsertArrayElementAtIndex(axesProperty.arraySize);
|
||||
foundProperty = axesProperty.GetArrayElementAtIndex(axesProperty.arraySize - 1);
|
||||
}
|
||||
|
||||
return foundProperty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c0a59ca677e813242811f6b3ee89b4bd
|
||||
timeCreated: 1507563065
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user