Added Feel plugin

This commit is contained in:
journaliciouz
2025-12-11 14:49:16 +01:00
parent 97dce4aaf6
commit 1942a531d4
2820 changed files with 257786 additions and 9 deletions

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 48e60c3a68661ec43a3003fe20b0282b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1671a5a55391bd24eb553baa8042d7fb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace MoreMountains.Tools
{
[CustomPropertyDrawer(typeof(AIAction))]
public class AIActionPropertyInspector : PropertyDrawer
{
const float LineHeight = 16f;
#if UNITY_EDITOR
/// <summary>
/// Draws
/// </summary>
/// <param name="rect"></param>
/// <param name="prop"></param>
/// <param name="label"></param>
public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label)
{
// determines the height of the Action property
var height = Mathf.Max(LineHeight, EditorGUI.GetPropertyHeight(prop));
Rect position = rect;
position.height = height;
// draws the dropdown
DrawSelectionDropdown(position, prop);
// draws the base field
position.y += height;
EditorGUI.PropertyField(position, prop);
}
#endif
/// <summary>
/// Draws a selector letting the user pick any action associated with the AIBrain this action is on
/// </summary>
/// <param name="position"></param>
/// <param name="prop"></param>
protected virtual void DrawSelectionDropdown(Rect position, SerializedProperty prop)
{
AIAction thisAction = prop.objectReferenceValue as AIAction;
AIAction[] actions = (prop.serializedObject.targetObject as AIBrain).GetAttachedActions();
int selected = 0;
int i = 1;
string[] options = new string[actions.Length + 1];
options[0] = "None";
foreach (AIAction action in actions)
{
string name = string.IsNullOrEmpty(action.Label) ? action.GetType().Name : action.Label;
options[i] = i.ToString() + " - " + name;
if (action == thisAction)
{
selected = i;
}
i++;
}
EditorGUI.BeginChangeCheck();
selected = EditorGUI.Popup(position, selected, options);
if (EditorGUI.EndChangeCheck())
{
prop.objectReferenceValue = (selected == 0) ? null : actions[selected - 1];
prop.serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(prop.serializedObject.targetObject);
}
}
/// <summary>
/// Returns the height of the full property
/// </summary>
/// <param name="property"></param>
/// <param name="label"></param>
/// <returns></returns>
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
var h = Mathf.Max(LineHeight, EditorGUI.GetPropertyHeight(property));
float height = h * 2; // 2 lines, one for the dropdown, one for the property field
return height;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 702bfb9f7cc2a2849a2c16f4087d1c55
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/Editor/MMAI/AIActionInspectorDrawer.cs
uploadId: 830868

View File

@@ -0,0 +1,74 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace MoreMountains.Tools
{
[CanEditMultipleObjects]
[CustomEditor(typeof(AIBrain), true)]
public class AIBrainEditor : Editor
{
protected MMReorderableList _list;
protected SerializedProperty _brainActive;
protected SerializedProperty _resetBrainOnEnable;
protected SerializedProperty _resetBrainOnStart;
protected SerializedProperty _timeInThisState;
protected SerializedProperty _target;
protected SerializedProperty _owner;
protected SerializedProperty _actionsFrequency;
protected SerializedProperty _decisionFrequency;
protected SerializedProperty _randomizeFrequencies;
protected SerializedProperty _randomActionFrequency;
protected SerializedProperty _randomDecisionFrequency;
protected virtual void OnEnable()
{
_list = new MMReorderableList(serializedObject.FindProperty("States"));
_list.elementNameProperty = "States";
_list.elementDisplayType = MMReorderableList.ElementDisplayType.Expandable;
_brainActive = serializedObject.FindProperty("BrainActive");
_resetBrainOnEnable = serializedObject.FindProperty("ResetBrainOnEnable");
_resetBrainOnStart = serializedObject.FindProperty("ResetBrainOnStart");
_timeInThisState = serializedObject.FindProperty("TimeInThisState");
_target = serializedObject.FindProperty("Target");
_owner = serializedObject.FindProperty("Owner");
_actionsFrequency = serializedObject.FindProperty("ActionsFrequency");
_decisionFrequency = serializedObject.FindProperty("DecisionFrequency");
_randomizeFrequencies = serializedObject.FindProperty("RandomizeFrequencies");
_randomActionFrequency = serializedObject.FindProperty("RandomActionFrequency");
_randomDecisionFrequency = serializedObject.FindProperty("RandomDecisionFrequency");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
_list.DoLayoutList();
EditorGUILayout.PropertyField(_timeInThisState);
EditorGUILayout.PropertyField(_owner);
EditorGUILayout.PropertyField(_target);
EditorGUILayout.PropertyField(_brainActive);
EditorGUILayout.PropertyField(_resetBrainOnEnable);
EditorGUILayout.PropertyField(_resetBrainOnStart);
EditorGUILayout.PropertyField(_actionsFrequency);
EditorGUILayout.PropertyField(_decisionFrequency);
EditorGUILayout.PropertyField(_randomizeFrequencies);
if (_randomizeFrequencies.boolValue)
{
EditorGUILayout.PropertyField(_randomActionFrequency);
EditorGUILayout.PropertyField(_randomDecisionFrequency);
}
serializedObject.ApplyModifiedProperties();
AIBrain brain = (AIBrain)target;
if (brain.CurrentState != null)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Current State", brain.CurrentState.StateName);
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7cd42026e6a9da7479e4c826b64a4df9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/Editor/MMAI/AIBrainEditor.cs
uploadId: 830868

View File

@@ -0,0 +1,126 @@
using UnityEngine;
using UnityEditor;
using System.Collections;
namespace MoreMountains.Tools
{
[CustomPropertyDrawer(typeof(AITransition))]
public class AITransitionPropertyInspector : PropertyDrawer
{
const float LineHeight = 16f;
#if UNITY_EDITOR
/// <summary>
/// Draws a Transition inspector, a transition is one or more action(s), one or more decision(s) and associated true/false states
/// </summary>
/// <param name="rect"></param>
/// <param name="prop"></param>
/// <param name="label"></param>
public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label)
{
Rect position = rect;
foreach (SerializedProperty a in prop)
{
var height = Mathf.Max(LineHeight, EditorGUI.GetPropertyHeight(a));
position.height = height;
if(a.name == "Decision")
{
// draw the decision dropdown
DrawSelectionDropdown(position, prop);
// draw the base decision field
position.y += height;
EditorGUI.PropertyField(position, a, new GUIContent(a.name));
position.y += height;
/*var @object = a.objectReferenceValue;
AIDecision @typedObject = @object as AIDecision;
if (@typedObject != null && !string.IsNullOrEmpty(@typedObject.Label))
{
EditorGUI.LabelField(position, "Label", @typedObject.Label);
position.y += height;
}
else
{
EditorGUIUtility.GetControlID(FocusType.Passive);
}*/
}
else
{
EditorGUI.PropertyField(position, a, new GUIContent(a.name));
position.y += height;
}
}
}
#endif
/// <summary>
/// Draws a selector letting the user pick any decision associated with the AIBrain this transition is on
/// </summary>
/// <param name="position"></param>
/// <param name="prop"></param>
protected virtual void DrawSelectionDropdown(Rect position, SerializedProperty prop)
{
AIDecision thisDecision = prop.objectReferenceValue as AIDecision;
AIDecision[] decisions = (prop.serializedObject.targetObject as AIBrain).GetAttachedDecisions();
int selected = 0;
int i = 1;
string[] options = new string[decisions.Length + 1];
options[0] = "None";
foreach (AIDecision decision in decisions)
{
string name = string.IsNullOrEmpty(decision.Label) ? decision.GetType().Name : decision.Label;
options[i] = i.ToString() + " - " + name;
if (decision == thisDecision)
{
selected = i;
}
i++;
}
EditorGUI.BeginChangeCheck();
selected = EditorGUI.Popup(position, selected, options);
if (EditorGUI.EndChangeCheck())
{
prop.objectReferenceValue = (selected == 0) ? null : decisions[selected - 1];
prop.serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(prop.serializedObject.targetObject);
}
}
/// <summary>
/// Determines the height of the transition property
/// </summary>
/// <param name="property"></param>
/// <param name="label"></param>
/// <returns></returns>
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
float height = 0;
foreach (SerializedProperty a in property)
{
var h = Mathf.Max(LineHeight, EditorGUI.GetPropertyHeight(a));
if(a.name == "Decision")
{
height += h * 2;
/*var @object = a.objectReferenceValue;
AIDecision @typedObject = @object as AIDecision;
if (@typedObject != null && !string.IsNullOrEmpty(@typedObject.Label))
{
height += h;
}*/
}
else
{
height += h;
}
}
return height;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 40eb84ddcba91054fa1432576815aa6b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/Editor/MMAI/AITransitionInspectorDrawer.cs
uploadId: 830868

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1917f0eaf05900748b6f700d0fd6c321
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,26 @@
using UnityEngine;
using UnityEditor;
namespace MoreMountains.Tools
{
[CustomEditor(typeof(MMAchievementList),true)]
/// <summary>
/// Custom inspector for the MMAchievementList scriptable object.
/// </summary>
public class MMAchievementListInspector : Editor
{
/// <summary>
/// When drawing the GUI, adds a "Reset Achievements" button, that does exactly what you think it does.
/// </summary>
public override void OnInspectorGUI()
{
DrawDefaultInspector ();
MMAchievementList achievementList = (MMAchievementList)target;
if(GUILayout.Button("Reset Achievements"))
{
achievementList.ResetAchievements();
}
EditorUtility.SetDirty (achievementList);
}
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 70d3db55e641fa4428b605f636e0015b
timeCreated: 1480263802
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/Editor/MMAchievements/MMAchievementListInspector.cs
uploadId: 830868

View File

@@ -0,0 +1,19 @@
using UnityEngine;
using System.Collections;
using MoreMountains.Tools;
using UnityEditor;
namespace MoreMountains.Tools
{
public static class MMAchievementMenu
{
[MenuItem("Tools/More Mountains/Reset all achievements", false,21)]
/// <summary>
/// Adds a menu item to enable help
/// </summary>
private static void EnableHelpInInspectors()
{
MMAchievementManager.ResetAllAchievements ();
}
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: a1d53c42aa1029048b98580478952105
timeCreated: 1482318762
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/Editor/MMAchievements/MMAchievementMenu.cs
uploadId: 830868

View File

@@ -0,0 +1,3 @@
{
"reference": "GUID:d9dbf313afb206f458581847ac758375"
}

View File

@@ -0,0 +1,14 @@
fileFormatVersion: 2
guid: e08d8cde70b2ab541adf3efc246ef16a
AssemblyDefinitionReferenceImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/Editor/MoreMountains.Tools.Foundation.Editor.asmref
uploadId: 830868

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b062a1793c8d8ec448d1cb2594114352
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// Actions are behaviours and describe what your character is doing. Examples include patrolling, shooting, jumping, etc.
/// </summary>
public abstract class AIAction : MonoBehaviour
{
public enum InitializationModes { EveryTime, OnlyOnce, }
/// whether initialization should happen only once, or every time the brain is reset
public InitializationModes InitializationMode;
protected bool _initialized;
/// a label you can set to organize your AI Actions, not used by anything else
[Tooltip("a label you can set to organize your AI Actions, not used by anything else")]
public string Label;
public abstract void PerformAction();
public virtual bool ActionInProgress { get; set; }
protected AIBrain _brain;
protected virtual bool ShouldInitialize
{
get
{
switch (InitializationMode)
{
case InitializationModes.EveryTime:
return true;
case InitializationModes.OnlyOnce:
return _initialized == false;
}
return true;
}
}
/// <summary>
/// On Awake we grab our AIBrain
/// </summary>
protected virtual void Awake()
{
_brain = this.gameObject.GetComponentInParent<AIBrain>();
}
/// <summary>
/// Initializes the action. Meant to be overridden
/// </summary>
public virtual void Initialization()
{
_initialized = true;
}
/// <summary>
/// Describes what happens when the brain enters the state this action is in. Meant to be overridden.
/// </summary>
public virtual void OnEnterState()
{
ActionInProgress = true;
}
/// <summary>
/// Describes what happens when the brain exits the state this action is in. Meant to be overridden.
/// </summary>
public virtual void OnExitState()
{
ActionInProgress = false;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 4d7020c7ee7492e40848350adbd515be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAI/AIAction.cs
uploadId: 830868

View File

@@ -0,0 +1,311 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
namespace MoreMountains.Tools
{
/// <summary>
/// the AI brain is responsible from going from one state to the other based on the defined transitions. It's basically just a collection of states, and it's where you'll link all the actions, decisions, states and transitions together.
/// </summary>
[AddComponentMenu("More Mountains/Tools/AI/AI Brain")]
public class AIBrain : MonoBehaviour
{
[Header("Debug")]
/// the owner of that AI Brain, usually the associated character
[MMReadOnly]
public GameObject Owner;
/// the collection of states
public List<AIState> States;
/// this brain's current state
public virtual AIState CurrentState { get; protected set; }
/// the time we've spent in the current state
[MMReadOnly]
public float TimeInThisState;
/// the current target
[MMReadOnly]
public Transform Target;
/// the last known world position of the target
[MMReadOnly]
public Vector3 _lastKnownTargetPosition = Vector3.zero;
[Header("State")]
/// whether or not this brain is active
public bool BrainActive = true;
public bool ResetBrainOnStart = true;
public bool ResetBrainOnEnable = false;
[Header("Frequencies")]
/// the frequency (in seconds) at which to perform actions (lower values : higher frequency, high values : lower frequency but better performance)
public float ActionsFrequency = 0f;
/// the frequency (in seconds) at which to evaluate decisions
public float DecisionFrequency = 0f;
/// whether or not to randomize the action and decision frequencies
public bool RandomizeFrequencies = false;
/// the min and max values between which to randomize the action frequency
[MMVector("min","max")]
public Vector2 RandomActionFrequency = new Vector2(0.5f, 1f);
/// the min and max values between which to randomize the decision frequency
[MMVector("min","max")]
public Vector2 RandomDecisionFrequency = new Vector2(0.5f, 1f);
protected AIDecision[] _decisions;
protected AIAction[] _actions;
protected float _lastActionsUpdate = 0f;
protected float _lastDecisionsUpdate = 0f;
protected AIState _initialState;
protected AIState _newState;
public virtual AIAction[] GetAttachedActions()
{
AIAction[] actions = this.gameObject.GetComponentsInChildren<AIAction>();
return actions;
}
public virtual AIDecision[] GetAttachedDecisions()
{
AIDecision[] decisions = this.gameObject.GetComponentsInChildren<AIDecision>();
return decisions;
}
protected virtual void OnEnable()
{
if (ResetBrainOnEnable)
{
ResetBrain();
}
}
/// <summary>
/// On awake we set our brain for all states
/// </summary>
protected virtual void Awake()
{
foreach (AIState state in States)
{
state.SetBrain(this);
}
_decisions = GetAttachedDecisions();
_actions = GetAttachedActions();
if (RandomizeFrequencies)
{
ActionsFrequency = Random.Range(RandomActionFrequency.x, RandomActionFrequency.y);
DecisionFrequency = Random.Range(RandomDecisionFrequency.x, RandomDecisionFrequency.y);
}
}
/// <summary>
/// On Start we set our first state
/// </summary>
protected virtual void Start()
{
if (ResetBrainOnStart)
{
ResetBrain();
}
}
/// <summary>
/// Every frame we update our current state
/// </summary>
protected virtual void Update()
{
if (!BrainActive || (CurrentState == null) || (Time.timeScale == 0f))
{
return;
}
if (Time.time - _lastActionsUpdate > ActionsFrequency)
{
CurrentState.PerformActions();
_lastActionsUpdate = Time.time;
}
if (!BrainActive)
{
return;
}
if (Time.time - _lastDecisionsUpdate > DecisionFrequency)
{
CurrentState.EvaluateTransitions();
_lastDecisionsUpdate = Time.time;
}
TimeInThisState += Time.deltaTime;
StoreLastKnownPosition();
}
/// <summary>
/// Transitions to the specified state, trigger exit and enter states events
/// </summary>
/// <param name="newStateName"></param>
public virtual void TransitionToState(string newStateName)
{
_newState = FindState(newStateName);
AIStateEvent.Trigger(this,CurrentState, _newState);
if (CurrentState == null)
{
CurrentState = _newState;
if (CurrentState != null)
{
CurrentState.EnterState();
}
return;
}
if (newStateName != CurrentState.StateName)
{
CurrentState.ExitState();
OnExitState();
CurrentState = _newState;
if (CurrentState != null)
{
CurrentState.EnterState();
}
}
}
/// <summary>
/// When exiting a state we reset our time counter
/// </summary>
protected virtual void OnExitState()
{
TimeInThisState = 0f;
}
/// <summary>
/// Initializes all decisions
/// </summary>
protected virtual void InitializeDecisions()
{
if (_decisions == null)
{
_decisions = GetAttachedDecisions();
}
foreach(AIDecision decision in _decisions)
{
decision.Initialization();
}
}
/// <summary>
/// Initializes all actions
/// </summary>
protected virtual void InitializeActions()
{
if (_actions == null)
{
_actions = GetAttachedActions();
}
foreach(AIAction action in _actions)
{
action.Initialization();
}
}
/// <summary>
/// Returns a state based on the specified state name
/// </summary>
/// <param name="stateName"></param>
/// <returns></returns>
protected AIState FindState(string stateName)
{
foreach (AIState state in States)
{
if (state.StateName == stateName)
{
return state;
}
}
if (stateName != "")
{
Debug.LogError("You're trying to transition to state '" + stateName + "' in " + this.gameObject.name + "'s AI Brain, but no state of this name exists. Make sure your states are named properly, and that your transitions states match existing states.");
}
return null;
}
/// <summary>
/// Stores the last known position of the target
/// </summary>
protected virtual void StoreLastKnownPosition()
{
if (Target != null)
{
_lastKnownTargetPosition = Target.transform.position;
}
}
/// <summary>
/// Resets the brain, forcing it to enter its first state
/// </summary>
public virtual void ResetBrain()
{
InitializeDecisions();
InitializeActions();
BrainActive = true;
this.enabled = true;
if (CurrentState != null)
{
CurrentState.ExitState();
OnExitState();
}
if (States.Count > 0)
{
_newState = States[0];
AIStateEvent.Trigger(this,CurrentState, _newState);
CurrentState = _newState;
CurrentState?.EnterState();
}
}
/// <summary>
/// Triggered via the context menu in its inspector (or if you call it directly), this will remove any unused actions and decisions from the brain
/// </summary>
[ContextMenu("Delete unused actions and decisions")]
public virtual void DeleteUnusedActionsAndDecisions()
{
AIAction[] actions = this.gameObject.GetComponentsInChildren<AIAction>();
AIDecision[] decisions = this.gameObject.GetComponentsInChildren<AIDecision>();
foreach (AIAction action in actions)
{
bool found = false;
foreach (AIState state in States)
{
if (state.Actions.Contains(action))
{
found = true;
}
}
if (!found)
{
DestroyImmediate(action);
}
}
foreach (AIDecision decision in decisions)
{
bool found = false;
foreach (AIState state in States)
{
foreach (AITransition transition in state.Transitions)
{
if (transition.Decision == decision)
{
found = true;
}
}
}
if (!found)
{
DestroyImmediate(decision);
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: eec89e4158bf96841b9bc830fc5385ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAI/AIBrain.cs
uploadId: 830868

View File

@@ -0,0 +1,54 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// Decisions are components that will be evaluated by transitions, every frame, and will return true or false. Examples include time spent in a state, distance to a target, or object detection within an area.
/// </summary>
public abstract class AIDecision : MonoBehaviour
{
/// Decide will be performed every frame while the Brain is in a state this Decision is in. Should return true or false, which will then determine the transition's outcome.
public abstract bool Decide();
/// a label you can set to organize your AI Decisions, not used by anything else
[Tooltip("a label you can set to organize your AI Decisions, not used by anything else")]
public string Label;
public virtual bool DecisionInProgress { get; set; }
protected AIBrain _brain;
/// <summary>
/// On Awake we grab our Brain
/// </summary>
protected virtual void Awake()
{
_brain = this.gameObject.GetComponentInParent<AIBrain>();
}
/// <summary>
/// Meant to be overridden, called when the game starts
/// </summary>
public virtual void Initialization()
{
}
/// <summary>
/// Meant to be overridden, called when the Brain enters a State this Decision is in
/// </summary>
public virtual void OnEnterState()
{
DecisionInProgress = true;
}
/// <summary>
/// Meant to be overridden, called when the Brain exits a State this Decision is in
/// </summary>
public virtual void OnExitState()
{
DecisionInProgress = false;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 86aa60c7eb6e3fe4a8c3624c6b3f1abc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAI/AIDecision.cs
uploadId: 830868

View File

@@ -0,0 +1,156 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
[System.Serializable]
public class AIActionsList : MMReorderableArray<AIAction>
{
}
[System.Serializable]
public class AITransitionsList : MMReorderableArray<AITransition>
{
}
public struct AIStateEvent
{
public AIBrain Brain;
public AIState ExitState;
public AIState EnterState;
public AIStateEvent(AIBrain brain, AIState exitState, AIState enterState)
{
Brain = brain;
ExitState = exitState;
EnterState = enterState;
}
static AIStateEvent e;
public static void Trigger(AIBrain brain, AIState exitState, AIState enterState)
{
e.Brain = brain;
e.ExitState = exitState;
e.EnterState = enterState;
MMEventManager.TriggerEvent(e);
}
}
/// <summary>
/// A State is a combination of one or more actions, and one or more transitions. An example of a state could be "_patrolling until an enemy gets in range_".
/// </summary>
[System.Serializable]
public class AIState
{
/// the name of the state (will be used as a reference in Transitions
public string StateName;
[MMReorderableAttribute(null, "Action", null)]
public AIActionsList Actions;
[MMReorderableAttribute(null, "Transition", null)]
public AITransitionsList Transitions;/*
/// a list of actions to perform in this state
public List<AIAction> Actions;
/// a list of transitions to evaluate to exit this state
public List<AITransition> Transitions;*/
protected AIBrain _brain;
/// <summary>
/// Sets this state's brain to the one specified in parameters
/// </summary>
/// <param name="brain"></param>
public virtual void SetBrain(AIBrain brain)
{
_brain = brain;
}
/// <summary>
/// On enter state we pass that info to our actions and decisions
/// </summary>
public virtual void EnterState()
{
foreach (AIAction action in Actions)
{
action.OnEnterState();
}
foreach (AITransition transition in Transitions)
{
if (transition.Decision != null)
{
transition.Decision.OnEnterState();
}
}
}
/// <summary>
/// On exit state we pass that info to our actions and decisions
/// </summary>
public virtual void ExitState()
{
foreach (AIAction action in Actions)
{
action.OnExitState();
}
foreach (AITransition transition in Transitions)
{
if (transition.Decision != null)
{
transition.Decision.OnExitState();
}
}
}
/// <summary>
/// Performs this state's actions
/// </summary>
public virtual void PerformActions()
{
if (Actions.Count == 0) { return; }
for (int i=0; i<Actions.Count; i++)
{
if (Actions[i] != null)
{
Actions[i].PerformAction();
}
else
{
Debug.LogError("An action in " + _brain.gameObject.name + " on state " + StateName + " is null.");
}
}
}
/// <summary>
/// Tests this state's transitions
/// </summary>
public virtual void EvaluateTransitions()
{
if (Transitions.Count == 0) { return; }
for (int i = 0; i < Transitions.Count; i++)
{
if (Transitions[i].Decision != null)
{
if (Transitions[i].Decision.Decide())
{
Transitions[i].LastDecisionEvaluation = true;
if (!string.IsNullOrEmpty(Transitions[i].TrueState))
{
_brain.TransitionToState(Transitions[i].TrueState);
break;
}
}
else
{
Transitions[i].LastDecisionEvaluation = false;
if (!string.IsNullOrEmpty(Transitions[i].FalseState))
{
_brain.TransitionToState(Transitions[i].FalseState);
break;
}
}
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6e1187ab043b6154493c3407ed149566
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAI/AIState.cs
uploadId: 830868

View File

@@ -0,0 +1,23 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// Transitions are a combination of one or more decisions and destination states whether or not these transitions are true or false. An example of a transition could be "_if an enemy gets in range, transition to the Shooting state_".
/// </summary>
[System.Serializable]
public class AITransition
{
/// this transition's decision
public AIDecision Decision;
/// the state to transition to if this Decision returns true
public string TrueState;
/// the state to transition to if this Decision returns false
public string FalseState;
/// the value of the last decision evaluation for this transition, for debug purposes
[MMReadOnly]
public bool LastDecisionEvaluation = false;
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 36bf680dabd04c14288ec99ad204ee89
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAI/AITransition.cs
uploadId: 830868

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 11c2284278bfce54a9d90b80bc88118a
folderAsset: yes
timeCreated: 1523893762
licenseType: Store
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 602325afa3a7e49e6a02b9bef136256b
folderAsset: yes
timeCreated: 1480001014
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,27 @@
fileFormatVersion: 2
guid: 0f03458534bea45e4bba9ab3411321b2
timeCreated: 1480001014
licenseType: Store
TrueTypeFontImporter:
serializedVersion: 4
fontSize: 16
forceTextureCase: -2
characterSpacing: 0
characterPadding: 1
includeFontData: 1
fontNames:
- Orange Kid
fallbackFontReferences: []
customCharacters:
fontRenderingMode: 0
ascentCalculationMode: 1
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Fonts/orange kid.ttf
uploadId: 830868

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: e9c2c059b6db042f9a6c5169883cfd91
folderAsset: yes
timeCreated: 1480084340
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,586 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &100100000
Prefab:
m_ObjectHideFlags: 1
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications: []
m_RemovedComponents: []
m_ParentPrefab: {fileID: 0}
m_RootGameObject: {fileID: 1000011764500980}
m_IsPrefabParent: 1
--- !u!1 &1000010625031310
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000013839664430}
- component: {fileID: 222000012145136048}
- component: {fileID: 114000012127359052}
m_Layer: 5
m_Name: AchievementDescription
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000010787308212
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000010446898764}
- component: {fileID: 222000010806795656}
- component: {fileID: 114000011025716940}
m_Layer: 5
m_Name: AchievementTitle
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000011017784956
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000010929275142}
- component: {fileID: 222000013828483476}
- component: {fileID: 114000014023583406}
m_Layer: 5
m_Name: AchievementBackgroundUnlocked
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!1 &1000011678039142
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000011865885252}
- component: {fileID: 222000014014719096}
- component: {fileID: 114000014234426190}
- component: {fileID: 114123889840356516}
m_Layer: 5
m_Name: ProgressBar
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000011764500980
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000014175055234}
- component: {fileID: 114000010642984398}
- component: {fileID: 225000012185432622}
m_Layer: 5
m_Name: AchievementDisplay
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000012137379068
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000011735803302}
- component: {fileID: 222000014068580754}
- component: {fileID: 114000012046079386}
m_Layer: 5
m_Name: AchievementIcon
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000012904130690
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000011949108480}
- component: {fileID: 222000011779272360}
- component: {fileID: 114000013121188064}
m_Layer: 5
m_Name: AchievementBackground
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000014260414582
GameObject:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000010576291022}
- component: {fileID: 222000010728672920}
- component: {fileID: 114000011002272268}
m_Layer: 5
m_Name: ProgressBarFront
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &114000010642984398
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011764500980}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e775494159ed5e4c997035d2df49776, type: 3}
m_Name:
m_EditorClassIdentifier:
BackgroundLocked: {fileID: 114000013121188064}
BackgroundUnlocked: {fileID: 114000014023583406}
Icon: {fileID: 114000012046079386}
Title: {fileID: 114000011025716940}
Description: {fileID: 114000012127359052}
ProgressBarDisplay: {fileID: 114123889840356516}
--- !u!114 &114000011002272268
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000014260414582}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114000011025716940
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010787308212}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_FontData:
m_Font: {fileID: 12800000, guid: 0f03458534bea45e4bba9ab3411321b2, type: 3}
m_FontSize: 30
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Achievement Title
--- !u!114 &114000012046079386
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012137379068}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 21300000, guid: 16a86a7f581fc4dfb953e0df8ad3ecac, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114000012127359052
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010625031310}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.578}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_FontData:
m_Font: {fileID: 12800000, guid: 0f03458534bea45e4bba9ab3411321b2, type: 3}
m_FontSize: 26
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 0.68
m_Text: The achievement's description goes here ipsum dolor lorem sit amet ipsum
dolor sit amet ipsum dolor sit amet
--- !u!114 &114000013121188064
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012904130690}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 21300000, guid: cecdbf60b0c9d47c9a26b99a41425de0, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114000014023583406
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011017784956}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 21300000, guid: c379ef0fb8ae144a5b0a0b1d0c02d125, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114000014234426190
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011678039142}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114123889840356516
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011678039142}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0a42a23ce4f9ed24abde69beacbef2f4, type: 3}
m_Name:
m_EditorClassIdentifier:
ForegroundBar: {fileID: 224000010576291022}
PlayerID:
--- !u!222 &222000010728672920
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000014260414582}
--- !u!222 &222000010806795656
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010787308212}
--- !u!222 &222000011779272360
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012904130690}
--- !u!222 &222000012145136048
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010625031310}
--- !u!222 &222000013828483476
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011017784956}
--- !u!222 &222000014014719096
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011678039142}
--- !u!222 &222000014068580754
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012137379068}
--- !u!224 &224000010446898764
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010787308212}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -201, y: 100}
m_SizeDelta: {x: 354.109, y: 50.075}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000010576291022
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000014260414582}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000011865885252}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 0, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 335, y: 5}
m_Pivot: {x: 0, y: 0.5}
--- !u!224 &224000010929275142
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011017784956}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 1}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -256, y: 64}
m_SizeDelta: {x: 512, y: 128}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000011735803302
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012137379068}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 0, y: 0.5}
m_AnchoredPosition: {x: -448, y: 64}
m_SizeDelta: {x: 128, y: 128}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000011865885252
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011678039142}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 224000010576291022}
m_Father: {fileID: 224000014175055234}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 0, y: 0.5}
m_AnchoredPosition: {x: -209.4, y: 13.9}
m_SizeDelta: {x: 335, y: 5}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000011949108480
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012904130690}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 1}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: -256, y: 64}
m_SizeDelta: {x: 512, y: 128}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000013839664430
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010625031310}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -201, y: 55}
m_SizeDelta: {x: 353.894, y: 79.908}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000014175055234
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011764500980}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 224000011949108480}
- {fileID: 224000010929275142}
- {fileID: 224000011735803302}
- {fileID: 224000010446898764}
- {fileID: 224000013839664430}
- {fileID: 224000011865885252}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: -6.1451902, y: 20.653046}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!225 &225000012185432622
CanvasGroup:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011764500980}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: bf6e65de4f70a4dcf99ceb68e999d287
timeCreated: 1480162379
licenseType: Store
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Prefabs/AchievementDisplay.prefab
uploadId: 830868

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4360e4e414a1faa4c9f4249158fb7f7a
folderAsset: yes
timeCreated: 1480084340
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,586 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1001 &100100000
Prefab:
m_ObjectHideFlags: 1
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications: []
m_RemovedComponents: []
m_ParentPrefab: {fileID: 0}
m_RootGameObject: {fileID: 1000011764500980}
m_IsPrefabParent: 1
--- !u!1 &1000010625031310
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000013839664430}
- component: {fileID: 222000012145136048}
- component: {fileID: 114000012127359052}
m_Layer: 5
m_Name: AchievementDescription
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000010787308212
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000010446898764}
- component: {fileID: 222000010806795656}
- component: {fileID: 114000011025716940}
m_Layer: 5
m_Name: AchievementTitle
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000011017784956
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000010929275142}
- component: {fileID: 222000013828483476}
- component: {fileID: 114000014023583406}
m_Layer: 5
m_Name: AchievementBackgroundUnlocked
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!1 &1000011678039142
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000011865885252}
- component: {fileID: 222000014014719096}
- component: {fileID: 114000014234426190}
- component: {fileID: 114000012929178306}
m_Layer: 5
m_Name: ProgressBar
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000011764500980
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000014175055234}
- component: {fileID: 114000010642984398}
- component: {fileID: 225000012185432622}
m_Layer: 5
m_Name: AchievementDisplay
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000012137379068
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000011735803302}
- component: {fileID: 222000014068580754}
- component: {fileID: 114000012046079386}
m_Layer: 5
m_Name: AchievementIcon
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000012904130690
GameObject:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000011949108480}
- component: {fileID: 222000011779272360}
- component: {fileID: 114000013121188064}
m_Layer: 5
m_Name: AchievementBackground
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!1 &1000014260414582
GameObject:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
serializedVersion: 5
m_Component:
- component: {fileID: 224000010576291022}
- component: {fileID: 222000010728672920}
- component: {fileID: 114000011002272268}
m_Layer: 5
m_Name: ProgressBarFront
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &114000010642984398
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011764500980}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e775494159ed5e4c997035d2df49776, type: 3}
m_Name:
m_EditorClassIdentifier:
BackgroundLocked: {fileID: 114000013121188064}
BackgroundUnlocked: {fileID: 114000014023583406}
Icon: {fileID: 114000012046079386}
Title: {fileID: 114000011025716940}
Description: {fileID: 114000012127359052}
ProgressBarDisplay: {fileID: 114000012929178306}
--- !u!114 &114000011002272268
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000014260414582}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114000011025716940
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010787308212}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_FontData:
m_Font: {fileID: 12800000, guid: 0f03458534bea45e4bba9ab3411321b2, type: 3}
m_FontSize: 30
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 1
m_Text: Achievement Title
--- !u!114 &114000012046079386
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012137379068}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 21300000, guid: 16a86a7f581fc4dfb953e0df8ad3ecac, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114000012127359052
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010625031310}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 708705254, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0.578}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_FontData:
m_Font: {fileID: 12800000, guid: 0f03458534bea45e4bba9ab3411321b2, type: 3}
m_FontSize: 26
m_FontStyle: 0
m_BestFit: 0
m_MinSize: 1
m_MaxSize: 40
m_Alignment: 0
m_AlignByGeometry: 0
m_RichText: 1
m_HorizontalOverflow: 0
m_VerticalOverflow: 0
m_LineSpacing: 0.68
m_Text: The achievement's description goes here ipsum dolor lorem sit amet ipsum
dolor sit amet ipsum dolor sit amet
--- !u!114 &114000012929178306
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011678039142}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0a42a23ce4f9ed24abde69beacbef2f4, type: 3}
m_Name:
m_EditorClassIdentifier:
ForegroundBar: {fileID: 224000010576291022}
PlayerID:
--- !u!114 &114000013121188064
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012904130690}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 21300000, guid: cecdbf60b0c9d47c9a26b99a41425de0, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114000014023583406
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011017784956}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 21300000, guid: c379ef0fb8ae144a5b0a0b1d0c02d125, type: 3}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!114 &114000014234426190
MonoBehaviour:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011678039142}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: -765806418, guid: f5f67c52d1564df4a8936ccd202a3bd8, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 1}
m_RaycastTarget: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
--- !u!222 &222000010728672920
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000014260414582}
--- !u!222 &222000010806795656
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010787308212}
--- !u!222 &222000011779272360
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012904130690}
--- !u!222 &222000012145136048
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010625031310}
--- !u!222 &222000013828483476
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011017784956}
--- !u!222 &222000014014719096
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011678039142}
--- !u!222 &222000014068580754
CanvasRenderer:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012137379068}
--- !u!224 &224000010446898764
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010787308212}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -201, y: 100}
m_SizeDelta: {x: 354.109, y: 50.075}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000010576291022
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000014260414582}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000011865885252}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 0, y: 0.5}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 335, y: 5}
m_Pivot: {x: 0, y: 0.5}
--- !u!224 &224000010929275142
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011017784956}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 1}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -256, y: 64}
m_SizeDelta: {x: 512, y: 128}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000011735803302
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012137379068}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 0, y: 0.5}
m_AnchoredPosition: {x: -448, y: 64}
m_SizeDelta: {x: 128, y: 128}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000011865885252
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011678039142}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 224000010576291022}
m_Father: {fileID: 224000014175055234}
m_RootOrder: 5
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0.5}
m_AnchorMax: {x: 0, y: 0.5}
m_AnchoredPosition: {x: -209.4, y: 13.9}
m_SizeDelta: {x: 335, y: 5}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000011949108480
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000012904130690}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 1}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 1, y: 0}
m_AnchorMax: {x: 1, y: 0}
m_AnchoredPosition: {x: -256, y: 64}
m_SizeDelta: {x: 512, y: 128}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000013839664430
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000010625031310}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 224000014175055234}
m_RootOrder: 4
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
m_AnchorMax: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: -201, y: 55}
m_SizeDelta: {x: 353.894, y: 79.908}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!224 &224000014175055234
RectTransform:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011764500980}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 224000011949108480}
- {fileID: 224000010929275142}
- {fileID: 224000011735803302}
- {fileID: 224000010446898764}
- {fileID: 224000013839664430}
- {fileID: 224000011865885252}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: -29.421097, y: -15.587612}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!225 &225000012185432622
CanvasGroup:
m_ObjectHideFlags: 1
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 100100000}
m_GameObject: {fileID: 1000011764500980}
m_Enabled: 1
m_Alpha: 1
m_Interactable: 1
m_BlocksRaycasts: 1
m_IgnoreParentGroups: 0

View File

@@ -0,0 +1,15 @@
fileFormatVersion: 2
guid: 445360f52aabb4446be71f24e6576a72
timeCreated: 1480162379
licenseType: Store
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Resources/AchievementDisplay.prefab
uploadId: 830868

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: af38923d3d51548fe95ccec5e05de8c8
folderAsset: yes
timeCreated: 1480084329
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,117 @@
using UnityEngine;
using System.Collections;
using System;
namespace MoreMountains.Tools
{
/// <summary>
/// This achievement system supports 2 types of achievements : simple (do something > get achievement), and progress based (jump X times, kill X enemies, etc).
/// </summary>
public enum AchievementTypes { Simple, Progress }
[Serializable]
public class MMAchievement
{
[Header("Identification")]
/// the unique identifier for this achievement
public string AchievementID;
/// is this achievement progress based or
public AchievementTypes AchievementType;
/// if this is true, the achievement won't be displayed in a list
public bool HiddenAchievement;
/// if this is true, the achievement has been unlocked. Otherwise, it's still up for grabs
public bool UnlockedStatus;
[Header("Description")]
/// the achievement's name/title
public string Title;
/// the achievement's description
public string Description;
/// the amount of points unlocking this achievement gets you
public int Points;
[Header("Image and Sounds")]
/// the image to display while this achievement is locked
public Sprite LockedImage;
/// the image to display when the achievement is unlocked
public Sprite UnlockedImage;
/// a sound to play when the achievement is unlocked
public AudioClip UnlockedSound;
[Header("Progress")]
/// the amount of progress needed to unlock this achievement.
public int ProgressTarget;
/// the current amount of progress made on this achievement
public int ProgressCurrent;
/// <summary>
/// Unlocks the achievement, asks for a save of the current achievements, and triggers an MMAchievementUnlockedEvent for this achievement.
/// This will usually then be caught by the MMAchievementDisplay class.
/// </summary>
public virtual void UnlockAchievement()
{
// if the achievement has already been unlocked, we do nothing and exit
if (UnlockedStatus)
{
return;
}
UnlockedStatus = true;
MMGameEvent.Trigger("Save");
MMAchievementUnlockedEvent.Trigger(this);
}
/// <summary>
/// Locks the achievement.
/// </summary>
public virtual void LockAchievement()
{
UnlockedStatus = false;
}
/// <summary>
/// Adds the specified value to the current progress.
/// </summary>
/// <param name="newProgress">New progress.</param>
public virtual void AddProgress(int newProgress)
{
ProgressCurrent += newProgress;
EvaluateProgress();
}
/// <summary>
/// Sets the progress to the value passed in parameter.
/// </summary>
/// <param name="newProgress">New progress.</param>
public virtual void SetProgress(int newProgress)
{
ProgressCurrent = newProgress;
EvaluateProgress();
}
/// <summary>
/// Evaluates the current progress of the achievement, and unlocks it if needed.
/// </summary>
protected virtual void EvaluateProgress()
{
MMAchievementChangedEvent.Trigger(this);
if (ProgressCurrent >= ProgressTarget)
{
ProgressCurrent = ProgressTarget;
UnlockAchievement();
}
}
/// <summary>
/// Copies this achievement (useful when loading from a scriptable object list)
/// </summary>
public virtual MMAchievement Copy()
{
MMAchievement clone = new MMAchievement ();
// we use Json utility to store a copy of our achievement, not a reference
clone = JsonUtility.FromJson<MMAchievement>(JsonUtility.ToJson(this));
return clone;
}
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 12a7d0ec5f1424c129527c01a444e964
timeCreated: 1480085861
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Scripts/MMAchievement.cs
uploadId: 830868

View File

@@ -0,0 +1,23 @@
#if MM_UI
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using MoreMountains.Tools;
namespace MoreMountains.Tools
{
/// <summary>
/// This class is used to display an achievement. Add it to a prefab containing all the required elements listed below.
/// </summary>
[AddComponentMenu("More Mountains/Tools/Achievements/MM Achievement Display Item")]
public class MMAchievementDisplayItem : MonoBehaviour
{
public Image BackgroundLocked;
public Image BackgroundUnlocked;
public Image Icon;
public Text Title;
public Text Description;
public MMProgressBar ProgressBarDisplay;
}
}
#endif

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 4e775494159ed5e4c997035d2df49776
timeCreated: 1480176460
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Scripts/MMAchievementDisplayItem.cs
uploadId: 830868

View File

@@ -0,0 +1,105 @@
using UnityEngine;
using System.Collections;
#if MM_UI
namespace MoreMountains.Tools
{
/// <summary>
/// A class used to display the achievements on screen.
/// The AchievementDisplayItems will be parented to it, so it's better if it has a LayoutGroup (Vertical or Horizontal) too.
/// </summary>
[AddComponentMenu("More Mountains/Tools/Achievements/MM Achievement Displayer")]
public class MMAchievementDisplayer : MonoBehaviour, MMEventListener<MMAchievementUnlockedEvent>
{
[Header("Achievements")]
/// the prefab to use to display achievements
public MMAchievementDisplayItem AchievementDisplayPrefab;
/// the duration the achievement will remain on screen for when unlocked
public float AchievementDisplayDuration = 5f;
/// the fade in/out speed
public float AchievementFadeDuration = 0.2f;
protected WaitForSeconds _achievementFadeOutWFS;
/// <summary>
/// Instantiates an achievement display prefab and shows it for the specified duration
/// </summary>
/// <returns>The achievement.</returns>
/// <param name="achievement">Achievement.</param>
public virtual IEnumerator DisplayAchievement(MMAchievement achievement)
{
if ((this.transform == null) || (AchievementDisplayPrefab == null))
{
yield break;
}
// we instantiate our achievement display prefab, and add it to the group that will automatically handle its position
GameObject instance = (GameObject)Instantiate(AchievementDisplayPrefab.gameObject);
instance.transform.SetParent(this.transform,false);
// we get the achievement displayer
MMAchievementDisplayItem achievementDisplay = instance.GetComponent<MMAchievementDisplayItem> ();
if (achievementDisplay == null)
{
yield break;
}
// we fill our achievement
achievementDisplay.Title.text = achievement.Title;
achievementDisplay.Description.text = achievement.Description;
achievementDisplay.Icon.sprite = achievement.UnlockedImage;
if (achievement.AchievementType == AchievementTypes.Progress)
{
achievementDisplay.ProgressBarDisplay.gameObject.SetActive(true);
}
else
{
achievementDisplay.ProgressBarDisplay.gameObject.SetActive(false);
}
// we play a sound if set
if (achievement.UnlockedSound != null)
{
MMSfxEvent.Trigger (achievement.UnlockedSound);
}
// we fade it in and out
CanvasGroup achievementCanvasGroup = instance.GetComponent<CanvasGroup> ();
if (achievementCanvasGroup != null)
{
achievementCanvasGroup.alpha = 0;
StartCoroutine(MMFade.FadeCanvasGroup(achievementCanvasGroup, AchievementFadeDuration, 1));
yield return _achievementFadeOutWFS;
StartCoroutine(MMFade.FadeCanvasGroup(achievementCanvasGroup, AchievementFadeDuration, 0));
}
}
/// <summary>
/// When an achievement is unlocked, we display it
/// </summary>
/// <param name="achievementUnlockedEvent">Achievement unlocked event.</param>
public virtual void OnMMEvent(MMAchievementUnlockedEvent achievementUnlockedEvent)
{
StartCoroutine(DisplayAchievement (achievementUnlockedEvent.Achievement));
}
/// <summary>
/// On enable, we start listening for unlocked achievements
/// </summary>
protected virtual void OnEnable()
{
this.MMEventStartListening<MMAchievementUnlockedEvent>();
_achievementFadeOutWFS = new WaitForSeconds (AchievementFadeDuration + AchievementDisplayDuration);
}
/// <summary>
/// On disable, we stop listening for unlocked achievements
/// </summary>
protected virtual void OnDisable()
{
this.MMEventStopListening<MMAchievementUnlockedEvent>();
}
}
}
#endif

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: f759f536b039f2b46881cfc8ea77c496
timeCreated: 1480176245
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Scripts/MMAchievementDisplayer.cs
uploadId: 830868

View File

@@ -0,0 +1,53 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace MoreMountains.Tools
{
/// <summary>
/// An event type used to broadcast the fact that an achievement has been unlocked
/// </summary>
public struct MMAchievementUnlockedEvent
{
/// the achievement that has been unlocked
public MMAchievement Achievement;
/// <summary>
/// Constructor
/// </summary>
/// <param name="newAchievement">New achievement.</param>
public MMAchievementUnlockedEvent(MMAchievement newAchievement)
{
Achievement = newAchievement;
}
static MMAchievementUnlockedEvent e;
public static void Trigger(MMAchievement newAchievement)
{
e.Achievement = newAchievement;
MMEventManager.TriggerEvent(e);
}
}
public struct MMAchievementChangedEvent
{
/// the achievement that has been unlocked
public MMAchievement Achievement;
/// <summary>
/// Constructor
/// </summary>
/// <param name="newAchievement">New achievement.</param>
public MMAchievementChangedEvent(MMAchievement newAchievement)
{
Achievement = newAchievement;
}
static MMAchievementChangedEvent e;
public static void Trigger(MMAchievement newAchievement)
{
e.Achievement = newAchievement;
MMEventManager.TriggerEvent(e);
}
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: d8ab561ba77c94d3fafd05ffde7d9f7c
timeCreated: 1480085871
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Scripts/MMAchievementEvent.cs
uploadId: 830868

View File

@@ -0,0 +1,34 @@
using UnityEngine;
using System.Collections;
using MoreMountains.Tools;
using System.Collections.Generic;
namespace MoreMountains.Tools
{
[CreateAssetMenu(fileName="AchievementList",menuName="MoreMountains/Achievement List")]
/// <summary>
/// A scriptable object containing a list of achievements. You need to create one and store it in a Resources folder for this to work.
/// </summary>
public class MMAchievementList : ScriptableObject
{
/// the unique ID of this achievement list. This is used to save/load data.
public string AchievementsListID = "AchievementsList";
/// the list of achievements
public List<MMAchievement> Achievements;
/// <summary>
/// Asks for a reset of all the achievements in this list (they'll all be locked again, their progress lost).
/// </summary>
public virtual void ResetAchievements()
{
Debug.LogFormat ("Reset Achievements");
MMAchievementManager.ResetAchievements (AchievementsListID);
}
private MMReferenceHolder<MMAchievementList> _instances;
protected virtual void OnEnable() { _instances.Reference(this); }
protected virtual void OnDisable() { _instances.Dispose(); }
public static MMAchievementList Any => MMReferenceHolder<MMAchievementList>.Any;
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 6f8f916bd4c5a444fa42e298d90f488d
timeCreated: 1480177569
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Scripts/MMAchievementList.cs
uploadId: 830868

View File

@@ -0,0 +1,225 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace MoreMountains.Tools
{
[ExecuteAlways]
/// <summary>
/// This static class is in charge of storing the current state of the achievements, unlocking/locking them, and saving them to data files
/// </summary>
public static class MMAchievementManager
{
public static List<MMAchievement> AchievementsList { get { return _achievements; }}
private static List<MMAchievement> _achievements;
private static MMAchievement _achievement = null;
public static string _defaultFileName = "Achievements";
public static string _saveFolderName = "MMAchievements/";
public static string _saveFileExtension = ".achievements";
public static string SaveFileName;
public static string ListID;
/// <summary>
/// You'll need to call this method to initialize the manager
/// </summary>
public static void LoadAchievementList(MMAchievementList achievementList)
{
_achievements = new List<MMAchievement> ();
if (achievementList == null)
{
return;
}
// we store the ID for save purposes
ListID = achievementList.AchievementsListID;
foreach (MMAchievement achievement in achievementList.Achievements)
{
_achievements.Add (achievement.Copy());
}
}
/// <summary>
/// Unlocks the specified achievement (if found).
/// </summary>
/// <param name="achievementID">Achievement I.</param>
public static void UnlockAchievement(string achievementID)
{
_achievement = AchievementManagerContains(achievementID);
if (_achievement != null)
{
_achievement.UnlockAchievement();
}
}
/// <summary>
/// Locks the specified achievement (if found).
/// </summary>
/// <param name="achievementID">Achievement ID.</param>
public static void LockAchievement(string achievementID)
{
_achievement = AchievementManagerContains(achievementID);
if (_achievement != null)
{
_achievement.LockAchievement();
}
}
/// <summary>
/// Adds progress to the specified achievement (if found).
/// </summary>
/// <param name="achievementID">Achievement ID.</param>
/// <param name="newProgress">New progress.</param>
public static void AddProgress(string achievementID, int newProgress)
{
_achievement = AchievementManagerContains(achievementID);
if (_achievement != null)
{
_achievement.AddProgress(newProgress);
}
}
/// <summary>
/// Sets the progress of the specified achievement (if found) to the specified progress.
/// </summary>
/// <param name="achievementID">Achievement ID.</param>
/// <param name="newProgress">New progress.</param>
public static void SetProgress(string achievementID, int newProgress)
{
_achievement = AchievementManagerContains(achievementID);
if (_achievement != null)
{
_achievement.SetProgress(newProgress);
}
}
/// <summary>
/// Determines if the achievement manager contains an achievement of the specified ID. Returns it if found, otherwise returns null
/// </summary>
/// <returns>The achievement corresponding to the searched ID if found, otherwise null.</returns>
/// <param name="searchedID">Searched I.</param>
private static MMAchievement AchievementManagerContains(string searchedID)
{
if (_achievements.Count == 0)
{
return null;
}
foreach(MMAchievement achievement in _achievements)
{
if (achievement.AchievementID == searchedID)
{
return achievement;
}
}
return null;
}
// SAVE ------------------------------------------------------------------------------------------------------------------------------------
/// <summary>
/// Removes saved data and resets all achievements from a list
/// </summary>
/// <param name="listID">The ID of the achievement list to reset.</param>
public static void ResetAchievements(string listID)
{
if (_achievements != null)
{
foreach(MMAchievement achievement in _achievements)
{
achievement.ProgressCurrent = 0;
achievement.UnlockedStatus = false;
}
}
DeterminePath (listID);
MMSaveLoadManager.DeleteSave(SaveFileName + _saveFileExtension, _saveFolderName);
Debug.LogFormat ("Achievements Reset");
}
public static void ResetAllAchievements()
{
ResetAchievements (ListID);
}
/// <summary>
/// Loads the saved achievements file and updates the array with its content.
/// </summary>
public static void LoadSavedAchievements()
{
DeterminePath ();
SerializedMMAchievementManager serializedMMAchievementManager = (SerializedMMAchievementManager)MMSaveLoadManager.Load(typeof(SerializedMMAchievementManager), SaveFileName+ _saveFileExtension, _saveFolderName);
ExtractSerializedMMAchievementManager(serializedMMAchievementManager);
}
/// <summary>
/// Saves the achievements current status to a file on disk
/// </summary>
public static void SaveAchievements()
{
DeterminePath ();
SerializedMMAchievementManager serializedMMAchievementManager = new SerializedMMAchievementManager();
FillSerializedMMAchievementManager(serializedMMAchievementManager);
MMSaveLoadManager.Save(serializedMMAchievementManager, SaveFileName+_saveFileExtension, _saveFolderName);
}
/// <summary>
/// Determines the path the achievements save file should be saved to.
/// </summary>
private static void DeterminePath(string specifiedFileName = "")
{
string tempFileName = (!string.IsNullOrEmpty(ListID)) ? ListID : _defaultFileName;
if (!string.IsNullOrEmpty(specifiedFileName))
{
tempFileName = specifiedFileName;
}
SaveFileName = tempFileName;
}
/// <summary>
/// Serializes the contents of the achievements array to a serialized, ready to save object
/// </summary>
/// <param name="serializedInventory">Serialized inventory.</param>
public static void FillSerializedMMAchievementManager(SerializedMMAchievementManager serializedAchievements)
{
serializedAchievements.Achievements = new SerializedMMAchievement[_achievements.Count];
for (int i = 0; i < _achievements.Count(); i++)
{
SerializedMMAchievement newAchievement = new SerializedMMAchievement (_achievements[i].AchievementID, _achievements[i].UnlockedStatus, _achievements[i].ProgressCurrent);
serializedAchievements.Achievements [i] = newAchievement;
}
}
/// <summary>
/// Extracts the serialized achievements into our achievements array if the achievements ID match.
/// </summary>
/// <param name="serializedAchievements">Serialized achievements.</param>
public static void ExtractSerializedMMAchievementManager(SerializedMMAchievementManager serializedAchievements)
{
if (serializedAchievements == null)
{
return;
}
for (int i = 0; i < _achievements.Count(); i++)
{
for (int j=0; j<serializedAchievements.Achievements.Length; j++)
{
if (_achievements[i].AchievementID == serializedAchievements.Achievements[j].AchievementID)
{
_achievements [i].UnlockedStatus = serializedAchievements.Achievements [j].UnlockedStatus;
_achievements [i].ProgressCurrent = serializedAchievements.Achievements [j].ProgressCurrent;
}
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 7190b0080c757413082e75f41313dee8
timeCreated: 1480085861
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Scripts/MMAchievementManager.cs
uploadId: 830868

View File

@@ -0,0 +1,81 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace MoreMountains.Tools
{
/// <summary>
/// That class is meant to be extended to implement the achievement rules specific to your game.
/// </summary>
public abstract class MMAchievementRules : MonoBehaviour, MMEventListener<MMGameEvent>
{
public MMAchievementList AchievementList;
[MMInspectorButton("PrintCurrentStatus")]
public bool PrintCurrentStatusBtn;
public virtual void PrintCurrentStatus()
{
foreach (MMAchievement achievement in MMAchievementManager.AchievementsList)
{
string status = achievement.UnlockedStatus ? "unlocked" : "locked";
MMDebug.DebugLogInfo("["+achievement.AchievementID + "] "+achievement.Title+", status : "+status+", progress : "+achievement.ProgressCurrent+"/"+achievement.ProgressTarget);
}
}
/// <summary>
/// On Awake, loads the achievement list and the saved file
/// </summary>
protected virtual void Awake()
{
// we load the list of achievements, stored in a ScriptableObject in our Resources folder.
MMAchievementManager.LoadAchievementList (AchievementList);
// we load our saved file, to update that list with the saved values.
MMAchievementManager.LoadSavedAchievements ();
}
/// <summary>
/// On enable, we start listening for MMGameEvents. You may want to extend that to listen to other types of events.
/// </summary>
protected virtual void OnEnable()
{
this.MMEventStartListening<MMGameEvent>();
}
/// <summary>
/// On disable, we stop listening for MMGameEvents. You may want to extend that to stop listening to other types of events.
/// </summary>
protected virtual void OnDisable()
{
this.MMEventStopListening<MMGameEvent>();
}
/// <summary>
/// When we catch an MMGameEvent, we do stuff based on its name
/// </summary>
/// <param name="gameEvent">Game event.</param>
public virtual void OnMMEvent(MMGameEvent gameEvent)
{
switch (gameEvent.EventName)
{
case "Save":
MMAchievementManager.SaveAchievements ();
break;
/*
// These are just examples of how you could catch a GameStart MMGameEvent and trigger the potential unlock of a corresponding achievement
case "GameStart":
MMAchievementManager.UnlockAchievement("theFirestarter");
break;
case "LifeLost":
MMAchievementManager.UnlockAchievement("theEndOfEverything");
break;
case "Pause":
MMAchievementManager.UnlockAchievement("timeStop");
break;
case "Jump":
MMAchievementManager.UnlockAchievement ("aSmallStepForMan");
MMAchievementManager.AddProgress ("toInfinityAndBeyond", 1);
break;*/
}
}
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 2c7c529377dc843a9a2316262550c4d6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- AchievementList: {fileID: 11400000, guid: 18225175314a9854581d8d6262302958, type: 2}
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Scripts/MMAchievementRules.cs
uploadId: 830868

View File

@@ -0,0 +1,39 @@
using UnityEngine;
using System.Collections;
using System;
namespace MoreMountains.Tools
{
[Serializable]
/// <summary>
/// A serializable class used to store an achievement into a save file
/// </summary>
public class SerializedMMAchievement
{
public string AchievementID;
public bool UnlockedStatus;
public int ProgressCurrent;
/// <summary>
/// Initializes a new instance of the <see cref="MoreMountains.Tools.SerializedMMAchievement"/> class.
/// </summary>
/// <param name="achievementID">Achievement I.</param>
/// <param name="unlockedStatus">If set to <c>true</c> unlocked status.</param>
/// <param name="progressCurrent">Progress current.</param>
public SerializedMMAchievement(string achievementID, bool unlockedStatus, int progressCurrent)
{
AchievementID = achievementID;
UnlockedStatus = unlockedStatus;
ProgressCurrent = progressCurrent;
}
}
[Serializable]
/// <summary>
/// Serializable MM achievement manager.
/// </summary>
public class SerializedMMAchievementManager
{
public SerializedMMAchievement[] Achievements;
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 24f964aa3d55241b591bc1da7903819e
timeCreated: 1480085861
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Scripts/SerializedMMAchievementManager.cs
uploadId: 830868

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 7fee60bb7a0474e9bad63c409ba9e02d
folderAsset: yes
timeCreated: 1479996221
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,147 @@
fileFormatVersion: 2
guid: cecdbf60b0c9d47c9a26b99a41425de0
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 16
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 32, y: 32, z: 32, w: 32}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 1
swizzle: 50462976
cookieLightType: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Sprites/AchievementBackground.png
uploadId: 830868

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,147 @@
fileFormatVersion: 2
guid: c379ef0fb8ae144a5b0a0b1d0c02d125
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 1
swizzle: 50462976
cookieLightType: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Sprites/AchievementBackgroundUnlocked.png
uploadId: 830868

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,147 @@
fileFormatVersion: 2
guid: 16a86a7f581fc4dfb953e0df8ad3ecac
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 12
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 1
swizzle: 50462976
cookieLightType: 1
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: WebGL
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Android
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAchievements/Sprites/AchievementIcon.png
uploadId: 830868

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bcac673537fad024f8be0aea6ced689b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,57 @@
using UnityEngine;
using System.Collections;
namespace MoreMountains.Tools
{
/// <summary>
/// Add this script to an animation in Mecanim and you'll be able to control its start position and speed
/// </summary>
[AddComponentMenu("More Mountains/Tools/Animation/MM Animation Modifier")]
public class MMAnimationModifier : StateMachineBehaviour
{
[MMVectorAttribute("Min", "Max")]
/// the min and max values for the start position of the animation (between 0 and 1)
public Vector2 StartPosition = new Vector2(0, 0);
[MMVectorAttribute("Min", "Max")]
/// the min and max values for the animation speed (1 is normal)
public Vector2 AnimationSpeed = new Vector2(1, 1);
protected bool _enteredState = false;
protected float _initialSpeed;
/// <summary>
/// On state enter, we modify our speed and start position
/// </summary>
/// <param name="animator"></param>
/// <param name="stateInfo"></param>
/// <param name="layerIndex"></param>
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
base.OnStateEnter(animator, stateInfo, layerIndex);
// handle speed
_initialSpeed = animator.speed;
animator.speed = Random.Range(AnimationSpeed.x, AnimationSpeed.y);
// handle start position
if (!_enteredState)
{
animator.Play(stateInfo.fullPathHash, layerIndex, Random.Range(StartPosition.x, StartPosition.y));
}
_enteredState = !_enteredState;
}
/// <summary>
/// On state exit, we restore our speed
/// </summary>
/// <param name="animator"></param>
/// <param name="stateInfo"></param>
/// <param name="layerIndex"></param>
public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
{
base.OnStateExit(animator, stateInfo, layerIndex);
animator.speed = _initialSpeed;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6ba4bd496d6656d4da1541ce7071ed10
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAnimation/MMAnimationModifier.cs
uploadId: 830868

View File

@@ -0,0 +1,68 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// A helper class that will hash a animation parameter and update it on demand
/// </summary>
[AddComponentMenu("More Mountains/Tools/Animation/MM Animation Parameter")]
public class MMAnimationParameter : MonoBehaviour
{
/// the name of the animation parameter to hash
public string ParameterName;
/// the animator to update
public Animator TargetAnimator;
protected int _parameter;
/// <summary>
/// On awake we initialize our class
/// </summary>
protected virtual void Awake()
{
Initialization();
}
/// <summary>
/// Hashes the parameter name into an int
/// </summary>
protected virtual void Initialization()
{
_parameter = Animator.StringToHash(ParameterName);
}
/// <summary>
/// Sets the trigger of the specified name
/// </summary>
public virtual void SetTrigger()
{
TargetAnimator.SetTrigger(_parameter);
}
/// <summary>
/// Sets the int of the specified name to the specified value
/// </summary>
public virtual void SetInt(int value)
{
TargetAnimator.SetInteger(_parameter, value);
}
/// <summary>
/// Sets the float of the specified name to the specified value
/// </summary>
public virtual void SetFloat(float value)
{
TargetAnimator.SetFloat(_parameter, value);
}
/// <summary>
/// Sets the bool of the specified name to the specified value
/// </summary>
public virtual void SetBool(bool value)
{
TargetAnimator.SetBool(_parameter, value);
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5451e999c8f9dfb41aa3b2894ffc3d60
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAnimation/MMAnimationParameter.cs
uploadId: 830868

View File

@@ -0,0 +1,133 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// This class will let you mirror the behaviour of an Animator's parameters on a Source Animator onto the ones of a Target Animator.
/// Target will mirror Source.
/// Only the parameters existing on both Target and Source will be considered, you'll need to have the same on both before entering runtime.
/// </summary>
public class MMAnimatorMirror : MonoBehaviour
{
/// a struct used to store bindings
public struct MMAnimatorMirrorBind
{
public int ParameterHash;
public AnimatorControllerParameterType ParameterType;
}
[Header("Bindings")]
/// the animator to mirror
public Animator SourceAnimator;
/// the animator to mirror to
public Animator TargetAnimator;
protected AnimatorControllerParameter[] _sourceParameters;
protected AnimatorControllerParameter[] _targetParameters;
protected List<MMAnimatorMirrorBind> _updateParameters;
/// <summary>
/// On Awake we initialize
/// </summary>
protected virtual void Awake()
{
Initialization();
}
/// <summary>
/// Stores animation parameters hashes
/// </summary>
public virtual void Initialization()
{
if (TargetAnimator == null)
{
TargetAnimator = this.gameObject.GetComponent<Animator>();
}
if ((TargetAnimator == null) || (SourceAnimator == null))
{
return;
}
// we store our source parameters
int numberOfParameters = SourceAnimator.parameterCount;
_sourceParameters = new AnimatorControllerParameter[numberOfParameters];
for (int i = 0; i < numberOfParameters; i++)
{
_sourceParameters[i] = SourceAnimator.GetParameter(i);
}
// we store our target parameters
numberOfParameters = TargetAnimator.parameterCount;
_targetParameters = new AnimatorControllerParameter[numberOfParameters];
for (int i = 0; i < numberOfParameters; i++)
{
_targetParameters[i] = TargetAnimator.GetParameter(i);
}
// we store our matching parameters
_updateParameters = new List<MMAnimatorMirrorBind>();
foreach (AnimatorControllerParameter sourceParam in _sourceParameters)
{
foreach (AnimatorControllerParameter targetParam in _targetParameters)
{
if (sourceParam.name == targetParam.name)
{
MMAnimatorMirrorBind bind = new MMAnimatorMirrorBind();
bind.ParameterHash = sourceParam.nameHash;
bind.ParameterType = sourceParam.type;
_updateParameters.Add(bind);
}
}
}
}
/// <summary>
/// On Update we mirror our behaviours
/// </summary>
protected virtual void Update()
{
Mirror();
}
/// <summary>
/// Copies animation parameter states from one animator to the other
/// </summary>
protected virtual void Mirror()
{
if ((TargetAnimator == null) || (SourceAnimator == null))
{
return;
}
foreach (MMAnimatorMirrorBind bind in _updateParameters)
{
switch (bind.ParameterType)
{
case AnimatorControllerParameterType.Bool:
TargetAnimator.SetBool(bind.ParameterHash, SourceAnimator.GetBool(bind.ParameterHash));
break;
case AnimatorControllerParameterType.Float:
TargetAnimator.SetFloat(bind.ParameterHash, SourceAnimator.GetFloat(bind.ParameterHash));
break;
case AnimatorControllerParameterType.Int:
TargetAnimator.SetInteger(bind.ParameterHash, SourceAnimator.GetInteger(bind.ParameterHash));
break;
case AnimatorControllerParameterType.Trigger:
if (SourceAnimator.GetBool(bind.ParameterHash))
{
TargetAnimator.SetTrigger(bind.ParameterHash);
}
else
{
TargetAnimator.ResetTrigger(bind.ParameterHash);
}
break;
}
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: e5d1374627ebc7d4491c7eb256713f69
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAnimation/MMAnimatorMirror.cs
uploadId: 830868

View File

@@ -0,0 +1,74 @@
using UnityEngine;
using System.Collections;
namespace MoreMountains.Tools
{
/// <summary>
/// Use this class to offset an animation by a random range
/// </summary>
[RequireComponent(typeof(Animator))]
[AddComponentMenu("More Mountains/Tools/Animation/MM Offset Animation")]
public class MMOffsetAnimation : MonoBehaviour
{
/// the minimum amount (in seconds) by which to offset the animation
public float MinimumRandomRange = 0f;
/// the maximum amount (in seconds) by which to offset the animation
public float MaximumRandomRange = 1f;
/// the layer to affect
public int AnimationLayerID = 0;
/// whether or not to apply that offset on Start
public bool OffsetOnStart = true;
/// whether or not to offset animation on enable
public bool OffsetOnEnable = false;
/// whether or not to self disable after offsetting
public bool DisableAfterOffset = true;
protected Animator _animator;
protected AnimatorStateInfo _stateInfo;
/// <summary>
/// On awake we store our animator
/// </summary>
protected virtual void Awake()
{
_animator = this.gameObject.GetComponent<Animator>();
}
/// <summary>
/// On Start we offset our animation
/// </summary>
protected virtual void Start()
{
if (!OffsetOnStart)
{
return;
}
OffsetCurrentAnimation();
}
/// <summary>
/// On Enable we offset our animation if needed
/// </summary>
protected virtual void OnEnable()
{
if (!OffsetOnEnable)
{
return;
}
OffsetCurrentAnimation();
}
/// <summary>
/// offsets the target animation
/// </summary>
public virtual void OffsetCurrentAnimation()
{
_stateInfo = _animator.GetCurrentAnimatorStateInfo(AnimationLayerID);
_animator.Play(_stateInfo.fullPathHash, -1, Random.Range(MinimumRandomRange, MaximumRandomRange));
if (DisableAfterOffset)
{
this.enabled = false;
}
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9c77c6d59898ea34989b338214db7de1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAnimation/MMOffsetAnimation.cs
uploadId: 830868

View File

@@ -0,0 +1,326 @@
using UnityEngine;
using System.Collections.Generic;
namespace MoreMountains.Tools
{
/// <summary>
/// A class used to store ragdoll body parts informations
/// </summary>
public class RagdollBodyPart
{
public Transform BodyPartTransform;
public Vector3 StoredPosition;
public Quaternion StoredRotation;
}
/// <summary>
/// Use this class to pilot a ragdoll on a character that is usually driven by an animator and have it fall elegantly
/// If you have parts of your ragdoll that you don't want to be affected by this script (a weapon for example), just add a MMRagdollerIgnore component to them
/// </summary>
[AddComponentMenu("More Mountains/Tools/Animation/MM Ragdoller")]
public class MMRagdoller : MonoBehaviour
{
/// <summary>
/// The possible states of the ragdoll :
/// - animated : driven by an animator controller, rigidbodies asleep
/// - ragdolling : full ragdoll mode, purely physics driven
/// - blending : transitioning between ragdolling and animated
/// </summary>
public enum RagdollStates
{
Animated,
Ragdolling,
Blending
}
[Header("Ragdoll")]
/// the current state of the ragdoll
public RagdollStates CurrentState = RagdollStates.Animated;
/// the duration in seconds it takes to blend from Ragdolling to Animated
public float RagdollToMecanimBlendDuration = 0.5f;
[Header("Rigidbodies")]
/// The rigidbody attached to the main body part of the ragdoll (usually the Pelvis)
public Rigidbody MainRigidbody;
/// if this is true, all rigidbodies will be forced to sleep every frame
public bool ForceSleep = true;
/// whether or not blending will occur when going from ragdolling to animated
public bool AllowBlending = true;
protected float _mecanimToGetUpTransitionTime = 0.05f;
protected float _ragdollingEndTimestamp = -float.MaxValue;
protected Vector3 _ragdolledHipPosition;
protected Vector3 _ragdolledHeadPosition;
protected Vector3 _ragdolledFeetPosition;
protected List<RagdollBodyPart> _bodyparts = new List<RagdollBodyPart>();
protected Animator _animator;
protected List<Component> _rigidbodiesTempList;
protected Component[] _rigidbodies;
protected HashSet<int> _animatorParameters;
protected const string _getUpFromBackAnimationParameterName = "GetUpFromBack";
protected int _getUpFromBackAnimationParameter;
protected const string _getUpFromBellyAnimationParameterName = "GetUpFromBelly";
protected int _getUpFromBellyAnimationParameter;
protected bool _initialized = false;
/// <summary>
/// Use this to get the current state of the ragdoll or to set a new one
/// </summary>
public bool Ragdolling
{
get
{
// if we're not animated, we're ragdolling
return CurrentState != RagdollStates.Animated;
}
set
{
if (value == true)
{
// if we're
if (CurrentState == RagdollStates.Animated)
{
SetIsKinematic(false);
_animator.enabled = false;
CurrentState = RagdollStates.Ragdolling;
MMAnimatorExtensions.UpdateAnimatorBool(_animator, _getUpFromBackAnimationParameter, false, _animatorParameters);
MMAnimatorExtensions.UpdateAnimatorBool(_animator, _getUpFromBellyAnimationParameter, false, _animatorParameters);
}
}
else
{
if (CurrentState == RagdollStates.Ragdolling)
{
SetIsKinematic(true);
_ragdollingEndTimestamp = Time.time;
_animator.enabled = true;
CurrentState = AllowBlending ? RagdollStates.Blending: RagdollStates.Animated;
foreach (RagdollBodyPart bodypart in _bodyparts)
{
bodypart.StoredRotation = bodypart.BodyPartTransform.rotation;
bodypart.StoredPosition = bodypart.BodyPartTransform.position;
}
_ragdolledFeetPosition = 0.5f * (_animator.GetBoneTransform(HumanBodyBones.LeftToes).position + _animator.GetBoneTransform(HumanBodyBones.RightToes).position);
_ragdolledHeadPosition = _animator.GetBoneTransform(HumanBodyBones.Head).position;
_ragdolledHipPosition = _animator.GetBoneTransform(HumanBodyBones.Hips).position;
if (_animator.GetBoneTransform(HumanBodyBones.Hips).forward.y > 0)
{
MMAnimatorExtensions.UpdateAnimatorBool(_animator, _getUpFromBackAnimationParameter, true, _animatorParameters);
}
else
{
MMAnimatorExtensions.UpdateAnimatorBool(_animator, _getUpFromBellyAnimationParameter, true, _animatorParameters);
}
}
}
}
}
/// <summary>
/// On start we initialize our ragdoller
/// </summary>
protected virtual void Start()
{
Initialization();
}
/// <summary>
/// Grabs rigidbodies, adds body parts and stores the animator
/// </summary>
protected virtual void Initialization()
{
// we grab all rigidbodies and set them to kinematic
_rigidbodies = GetComponentsInChildren(typeof(Rigidbody));
_rigidbodiesTempList = new List<Component>();
foreach (Component rigidbody in _rigidbodies)
{
if (rigidbody.gameObject.MMGetComponentNoAlloc<MMRagdollerIgnore>() == null)
{
_rigidbodiesTempList.Add(rigidbody);
}
}
_rigidbodies = null;
_rigidbodies = _rigidbodiesTempList.ToArray();
if (CurrentState == RagdollStates.Animated)
{
SetIsKinematic(true);
}
else
{
SetIsKinematic(false);
}
// we grab all transforms and add a RagdollBodyPart to them
Component[] transforms = GetComponentsInChildren(typeof(Transform));
foreach (Component component in transforms)
{
if (component.transform != this.transform)
{
RagdollBodyPart bodyPart = new RagdollBodyPart { BodyPartTransform = component as Transform };
_bodyparts.Add(bodyPart);
}
}
// we store our animator
_animator = this.gameObject.GetComponent<Animator>();
RegisterAnimatorParameters();
_initialized = true;
}
/// <summary>
/// Registers our animation parameters
/// </summary>
protected virtual void RegisterAnimatorParameters()
{
_animatorParameters = new HashSet<int>();
_getUpFromBackAnimationParameter = Animator.StringToHash(_getUpFromBackAnimationParameterName);
_getUpFromBellyAnimationParameter = Animator.StringToHash(_getUpFromBellyAnimationParameterName);
if (_animator == null)
{
return;
}
if (_animator.MMHasParameterOfType(_getUpFromBackAnimationParameterName, AnimatorControllerParameterType.Bool))
{
_animatorParameters.Add(_getUpFromBackAnimationParameter);
}
if (_animator.MMHasParameterOfType(_getUpFromBellyAnimationParameterName, AnimatorControllerParameterType.Bool))
{
_animatorParameters.Add(_getUpFromBellyAnimationParameter);
}
}
/// <summary>
/// Sets all rigidbodies in the ragdoll to kinematic and stops them from detecting collisions (or the other way around)
/// </summary>
/// <param name="isKinematic"></param>
protected virtual void SetIsKinematic(bool isKinematic)
{
foreach (Component rigidbody in _rigidbodies)
{
if (rigidbody.transform != this.transform)
{
(rigidbody as Rigidbody).detectCollisions = !isKinematic;
(rigidbody as Rigidbody).isKinematic = isKinematic;
}
}
}
/// <summary>
/// Forces all rigidbodies in the ragdoll to sleep
/// </summary>
public virtual void ForceRigidbodiesToSleep()
{
foreach (Component rigidbody in _rigidbodies)
{
if (rigidbody.transform != this.transform)
{
(rigidbody as Rigidbody).Sleep();
}
}
}
/// <summary>
/// On late update, we force our ragdoll elements to sleep and handle blending
/// </summary>
protected virtual void LateUpdate()
{
if ((CurrentState == RagdollStates.Animated) && ForceSleep)
{
ForceRigidbodiesToSleep();
}
HandleBlending();
}
/// <summary>
/// Blends between ragdolling and animated and switches to Animated at the end
/// </summary>
protected virtual void HandleBlending()
{
if (CurrentState == RagdollStates.Blending)
{
if (Time.time <= _ragdollingEndTimestamp + _mecanimToGetUpTransitionTime)
{
transform.position = GetRootPosition();
Vector3 ragdollingDirection = _ragdolledHeadPosition - _ragdolledFeetPosition;
ragdollingDirection.y = 0;
Vector3 meanFeetPosition = 0.5f * (_animator.GetBoneTransform(HumanBodyBones.LeftFoot).position + _animator.GetBoneTransform(HumanBodyBones.RightFoot).position);
Vector3 animatedDirection = _animator.GetBoneTransform(HumanBodyBones.Head).position - meanFeetPosition;
animatedDirection.y = 0;
transform.rotation *= Quaternion.FromToRotation(animatedDirection.normalized, ragdollingDirection.normalized);
}
float ragdollBlendAmount = 1.0f - (Time.time - _ragdollingEndTimestamp - _mecanimToGetUpTransitionTime) / RagdollToMecanimBlendDuration;
ragdollBlendAmount = Mathf.Clamp01(ragdollBlendAmount);
foreach (RagdollBodyPart bodypart in _bodyparts)
{
if (bodypart.BodyPartTransform != transform)
{
if (bodypart.BodyPartTransform == _animator.GetBoneTransform(HumanBodyBones.Hips))
{
bodypart.BodyPartTransform.position = Vector3.Lerp(bodypart.BodyPartTransform.position, bodypart.StoredPosition, ragdollBlendAmount);
}
bodypart.BodyPartTransform.rotation = Quaternion.Slerp(bodypart.BodyPartTransform.rotation, bodypart.StoredRotation, ragdollBlendAmount);
}
}
if (ragdollBlendAmount == 0)
{
CurrentState = RagdollStates.Animated;
return;
}
}
}
/// <summary>
/// Returns the current position of the ragdoll (technically the hips position)
/// </summary>
/// <returns></returns>
public Vector3 GetPosition()
{
if (!_initialized)
{
Initialization();
}
Vector3 newPosition = (_animator.GetBoneTransform(HumanBodyBones.Hips) == null) ? MainRigidbody.position : _animator.GetBoneTransform(HumanBodyBones.Hips).position;
return newPosition;
}
/// <summary>
/// Returns the offset root position
/// </summary>
/// <returns></returns>
protected Vector3 GetRootPosition()
{
Vector3 ragdollPosition = (_animator.GetBoneTransform(HumanBodyBones.Hips) == null) ? MainRigidbody.position : _animator.GetBoneTransform(HumanBodyBones.Hips).position;
Vector3 animatedToRagdolling = _ragdolledHipPosition - ragdollPosition;
Vector3 newRootPosition = transform.position + animatedToRagdolling;
RaycastHit[] hits = Physics.RaycastAll(new Ray(newRootPosition, Vector3.down));
newRootPosition.y = 0;
foreach (RaycastHit hit in hits)
{
if (!hit.transform.IsChildOf(transform))
{
newRootPosition.y = Mathf.Max(newRootPosition.y, hit.point.y);
}
}
return newRootPosition;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 9687a46a55ccbca4f92cdff319b70844
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAnimation/MMRagdoller.cs
uploadId: 830868

View File

@@ -0,0 +1,15 @@
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
namespace MoreMountains.Tools
{
/// <summary>
/// Add this class to objects you'd like to be unaffected by the MMRagdoller (like weapons for example)
/// </summary>
[AddComponentMenu("More Mountains/Tools/Animation/MM Ragdoller Ignore")]
public class MMRagdollerIgnore : MonoBehaviour
{
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 38f5f99a403324842b72d7bb5d781405
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAnimation/MMRagdollerIgnore.cs
uploadId: 830868

View File

@@ -0,0 +1,94 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
[AddComponentMenu("More Mountains/Tools/Animation/MM Stop Motion Animation")]
public class MMStopMotionAnimation : MonoBehaviour
{
public enum FramerateModes { Manual, Automatic }
[Header("General Settings")]
public bool StopMotionEnabled = true;
public int AnimationLayerID = 0;
[Header("Framerate")]
public FramerateModes FramerateMode = FramerateModes.Automatic;
[MMEnumCondition("FramerateMode", (int)FramerateModes.Automatic)]
public float FramesPerSecond = 4f;
[MMEnumCondition("FramerateMode", (int)FramerateModes.Automatic)]
public float PollFrequency = 1f;
[MMEnumCondition("FramerateMode", (int)FramerateModes.Manual)]
public float ManualTimeBetweenFrames = 0.125f;
[MMEnumCondition("FramerateMode", (int)FramerateModes.Manual)]
public float ManualAnimatorSpeed = 2;
public float timet = 0;
protected float _currentClipFPS = 0;
protected float _currentClipLength = 0f;
protected float _lastPollAt = -10f;
protected Animator _animator;
protected AnimationClip _currentClip;
protected virtual void Awake()
{
_animator = this.gameObject.GetComponent<Animator>();
}
protected virtual void Update()
{
StopMotion();
if (Time.time - _lastPollAt > PollFrequency)
{
Poll();
}
}
protected virtual void StopMotion()
{
if (!StopMotionEnabled)
{
return;
}
float timeBetweenFrames = 0f;
float animatorSpeed = 0f;
switch(FramerateMode)
{
case FramerateModes.Manual:
timeBetweenFrames = ManualTimeBetweenFrames;
animatorSpeed = ManualAnimatorSpeed;
break;
case FramerateModes.Automatic:
timeBetweenFrames = (1 / FramesPerSecond);
animatorSpeed = (1 / (FramesPerSecond - 1)) * 2f * _currentClipFPS;
break;
}
timet += Time.deltaTime;
if (timet > timeBetweenFrames)
{
timet -= timeBetweenFrames;
_animator.speed = animatorSpeed;
}
else
{
_animator.speed = 0;
}
}
protected virtual void Poll()
{
_currentClip = _animator.GetCurrentAnimatorClipInfo(AnimationLayerID)[0].clip;
_currentClipLength = _currentClip.length;
_currentClipFPS = _currentClip.frameRate;
_lastPollAt = Time.time;
}
}
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 409ecbe35fc2afd4b82ade0142839256
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMAnimation/MMStopMotionAnimation.cs
uploadId: 830868

View File

@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 92a846d385f0b4102b3984953a3501e2
folderAsset: yes
timeCreated: 1462366972
licenseType: Store
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,79 @@
using UnityEngine;
using System.Collections;
using MoreMountains.Tools;
#if MM_UI
namespace MoreMountains.Tools
{
/// <summary>
/// This persistent singleton handles the inputs and sends commands to the player in the MMControls demo, its sole purpose is to output debug logs of the various interactions with the demo's inputs
/// </summary>
public class MMControlsTestInputManager : MonoBehaviour, MMEventListener<MMSwipeEvent>
{
// on start, we force a high target frame rate for a more fluid experience on mobile devices
protected virtual void Start()
{
Application.targetFrameRate = 300;
}
public virtual void LeftJoystickMovement(Vector2 movement) { MMDebug.DebugOnScreen("left joystick",movement); }
public virtual void RightJoystickMovement(Vector2 movement) { MMDebug.DebugOnScreen("right joystick", movement); }
public virtual void RepositionableJoystickMovement(Vector2 movement) { MMDebug.DebugOnScreen("Repositionable joystick", movement); }
public virtual void FollowerJoystickMovement(Vector2 movement) { MMDebug.DebugOnScreen("Follower joystick", movement); }
public virtual void APressed() { MMDebug.DebugOnScreen("Button A Pressed"); }
public virtual void BPressed() { MMDebug.DebugOnScreen("Button B Pressed"); }
public virtual void XPressed() { MMDebug.DebugOnScreen("Button X Pressed"); }
public virtual void YPressed() { MMDebug.DebugOnScreen("Button Y Pressed"); }
public virtual void RTPressed() { MMDebug.DebugOnScreen("Button RT Pressed"); }
public virtual void APressedFirstTime() { Debug.LogFormat("Button A Pressed for the first time"); }
public virtual void BPressedFirstTime() { Debug.LogFormat("Button B Pressed for the first time"); }
public virtual void XPressedFirstTime() { Debug.LogFormat("Button X Pressed for the first time"); }
public virtual void YPressedFirstTime() { Debug.LogFormat("Button Y Pressed for the first time"); }
public virtual void RTPressedFirstTime() { Debug.LogFormat("Button RT Pressed for the first time"); }
public virtual void AReleased() { Debug.LogFormat("Button A Released"); }
public virtual void BReleased() { Debug.LogFormat("Button B Released"); }
public virtual void XReleased() { Debug.LogFormat("Button X Released"); }
public virtual void YReleased() { Debug.LogFormat("Button Y Released"); }
public virtual void RTReleased() { Debug.LogFormat("Button RT Released"); }
public virtual void HorizontalAxisPressed(float value) { MMDebug.DebugOnScreen("horizontal movement",value); }
public virtual void VerticalAxisPressed(float value) { MMDebug.DebugOnScreen("vertical movement",value); }
public virtual void LeftPressedFirstTime() { Debug.LogFormat("Button Left Pressed for the first time"); }
public virtual void UpPressedFirstTime() { Debug.LogFormat("Button Up Pressed for the first time"); }
public virtual void DownPressedFirstTime() { Debug.LogFormat("Button Down Pressed for the first time"); }
public virtual void RightPressedFirstTime() { Debug.LogFormat("Button Right Pressed for the first time"); }
public virtual void LeftReleased() { Debug.LogFormat("Button Left Released"); }
public virtual void UpReleased() { Debug.LogFormat("Button Up Released"); }
public virtual void DownReleased() { Debug.LogFormat("Button Down Released"); }
public virtual void RightReleased() { Debug.LogFormat("Button Right Released"); }
public virtual void StickDragged() { Debug.LogFormat("On drag"); }
public virtual void StickPointerUp() { Debug.LogFormat("On pointer up"); }
public virtual void StickPointerDown() { Debug.LogFormat("On pointer down"); }
public virtual void OnMMEvent(MMSwipeEvent swipeEvent)
{
Debug.LogFormat("Swipe at a "+ swipeEvent.SwipeAngle+"° angle, and a length of "+ swipeEvent.SwipeLength+" length. Cardinal direction : "+ swipeEvent.SwipeDirection);
Debug.LogFormat("Swipe origin : " + swipeEvent.SwipeOrigin+ ", swipe end : " + swipeEvent.SwipeDestination);
Debug.LogFormat("Swipe duration : "+swipeEvent.SwipeDuration);
}
protected virtual void OnEnable()
{
this.MMEventStartListening<MMSwipeEvent>();
}
protected virtual void OnDisable()
{
this.MMEventStopListening<MMSwipeEvent>();
}
}
}
#endif

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 9640016db57d1c646a90fa18111eebf0
timeCreated: 1462436657
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMControls/MMControlsTestInputManager.cs
uploadId: 830868

View File

@@ -0,0 +1,225 @@
#if MM_UI
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
using UnityEngine.InputSystem;
#endif
namespace MoreMountains.Tools
{
/// <summary>
/// The possible directions a swipe can have
/// </summary>
public enum MMPossibleSwipeDirections { Up, Down, Left, Right }
[System.Serializable]
public class SwipeEvent : UnityEvent<MMSwipeEvent> {}
/// <summary>
/// An event usually triggered when a swipe happens. It contains the swipe "base" direction, and detailed information if needed (angle, length, origin and destination
/// </summary>
public struct MMSwipeEvent
{
public MMPossibleSwipeDirections SwipeDirection;
public float SwipeAngle;
public float SwipeLength;
public Vector2 SwipeOrigin;
public Vector2 SwipeDestination;
public float SwipeDuration;
/// <summary>
/// Initializes a new instance of the <see cref="MoreMountains.Tools.MMSwipeEvent"/> struct.
/// </summary>
/// <param name="direction">Direction.</param>
/// <param name="angle">Angle.</param>
/// <param name="length">Length.</param>
/// <param name="origin">Origin.</param>
/// <param name="destination">Destination.</param>
public MMSwipeEvent(MMPossibleSwipeDirections direction, float angle, float length, Vector2 origin, Vector2 destination, float swipeDuration)
{
SwipeDirection = direction;
SwipeAngle = angle;
SwipeLength = length;
SwipeOrigin = origin;
SwipeDestination = destination;
SwipeDuration = swipeDuration;
}
static MMSwipeEvent e;
public static void Trigger(MMPossibleSwipeDirections direction, float angle, float length, Vector2 origin, Vector2 destination, float swipeDuration)
{
e.SwipeDirection = direction;
e.SwipeAngle = angle;
e.SwipeLength = length;
e.SwipeOrigin = origin;
e.SwipeDestination = destination;
e.SwipeDuration = swipeDuration;
MMEventManager.TriggerEvent(e);
}
}
/// <summary>
/// Add a swipe manager to your scene, and it'll trigger MMSwipeEvents everytime a swipe happens. From its inspector you can determine the minimal length of a swipe. Shorter swipes will be ignored
/// </summary>
[RequireComponent(typeof(RectTransform))]
[AddComponentMenu("More Mountains/Tools/Controls/MM Swipe Zone")]
public class MMSwipeZone : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IPointerEnterHandler
{
/// the minimal length of a swipe
[Tooltip("the minimal length of a swipe")]
public float MinimalSwipeLength = 50f;
/// the maximum press length of a swipe
[Tooltip("the maximum press length of a swipe")]
public float MaximumPressLength = 10f;
/// The method(s) to call when the zone is swiped
[Tooltip("The method(s) to call when the zone is swiped")]
public SwipeEvent ZoneSwiped;
/// The method(s) to call while the zone is being pressed
[Tooltip("The method(s) to call while the zone is being pressed")]
public UnityEvent ZonePressed;
[Header("Mouse Mode")]
[MMInformation("If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input).", MMInformationAttribute.InformationType.Info,false)]
/// If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input).
[Tooltip("If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input).")]
public bool MouseMode = false;
protected Vector2 _firstTouchPosition;
protected float _angle;
protected float _length;
protected Vector2 _destination;
protected Vector2 _deltaSwipe;
protected MMPossibleSwipeDirections _swipeDirection;
protected float _lastPointerUpAt = 0f;
protected float _swipeStartedAt = 0f;
protected float _swipeEndedAt = 0f;
/// <summary>
/// Invokes a swipe event with the correct properties
/// </summary>
protected virtual void Swipe()
{
float duration = _swipeEndedAt - _swipeStartedAt;
MMSwipeEvent swipeEvent = new MMSwipeEvent (_swipeDirection, _angle, _length, _firstTouchPosition, _destination, duration);
MMEventManager.TriggerEvent(swipeEvent);
if (ZoneSwiped != null)
{
ZoneSwiped.Invoke (swipeEvent);
}
}
/// <summary>
/// Raises the press event
/// </summary>
protected virtual void Press()
{
if (ZonePressed != null)
{
ZonePressed.Invoke ();
}
}
/// <summary>
/// Triggers the bound pointer down action
/// </summary>
public virtual void OnPointerDown(PointerEventData data)
{
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
_firstTouchPosition = Mouse.current.position.ReadValue();
#else
_firstTouchPosition = Input.mousePosition;
#endif
_swipeStartedAt = Time.unscaledTime;
}
/// <summary>
/// Triggers the bound pointer up action
/// </summary>
public virtual void OnPointerUp(PointerEventData data)
{
if (Time.frameCount == _lastPointerUpAt)
{
return;
}
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
_destination = Mouse.current.position.ReadValue();
#else
_destination = Input.mousePosition;
#endif
_deltaSwipe = _destination - _firstTouchPosition;
_length = _deltaSwipe.magnitude;
// if the swipe has been long enough
if (_length > MinimalSwipeLength)
{
_angle = MMMaths.AngleBetween (_deltaSwipe, Vector2.right);
_swipeDirection = AngleToSwipeDirection (_angle);
_swipeEndedAt = Time.unscaledTime;
Swipe ();
}
// if it's just a press
if (_deltaSwipe.magnitude < MaximumPressLength)
{
Press ();
}
_lastPointerUpAt = Time.frameCount;
}
/// <summary>
/// Triggers the bound pointer enter action when touch enters zone
/// </summary>
public virtual void OnPointerEnter(PointerEventData data)
{
if (!MouseMode)
{
OnPointerDown (data);
}
}
/// <summary>
/// Triggers the bound pointer exit action when touch is out of zone
/// </summary>
public virtual void OnPointerExit(PointerEventData data)
{
if (!MouseMode)
{
OnPointerUp(data);
}
}
/// <summary>
/// Determines a MMPossibleSwipeDirection out of an angle in degrees.
/// </summary>
/// <returns>The to swipe direction.</returns>
/// <param name="angle">Angle in degrees.</param>
protected virtual MMPossibleSwipeDirections AngleToSwipeDirection(float angle)
{
if ((angle < 45) || (angle >= 315))
{
return MMPossibleSwipeDirections.Right;
}
if ((angle >= 45) && (angle < 135))
{
return MMPossibleSwipeDirections.Up;
}
if ((angle >= 135) && (angle < 225))
{
return MMPossibleSwipeDirections.Left;
}
if ((angle >= 225) && (angle < 315))
{
return MMPossibleSwipeDirections.Down;
}
return MMPossibleSwipeDirections.Right;
}
}
}
#endif

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 5a68e615c17d2bf49a1a082e34fd9b6e
timeCreated: 1494336562
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMControls/MMSwipeZone.cs
uploadId: 830868

View File

@@ -0,0 +1,181 @@
#if MM_UI
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace MoreMountains.Tools
{
[System.Serializable]
public class AxisEvent : UnityEvent<float> {}
/// <summary>
/// Add this component to a GUI Image to have it act as an axis.
/// Bind pressed down, pressed continually and released actions to it from the inspector
/// Handles mouse and multi touch
/// </summary>
[RequireComponent(typeof(Rect))]
[RequireComponent(typeof(CanvasGroup))]
[AddComponentMenu("More Mountains/Tools/Controls/MM Touch Axis")]
public class MMTouchAxis : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IPointerEnterHandler
{
public enum ButtonStates { Off, ButtonDown, ButtonPressed, ButtonUp }
[Header("Binding")]
/// The method(s) to call when the axis gets pressed down
[Tooltip("The method(s) to call when the axis gets pressed down")]
public UnityEvent AxisPressedFirstTime;
/// The method(s) to call when the axis gets released
[Tooltip("The method(s) to call when the axis gets released")]
public UnityEvent AxisReleased;
/// The method(s) to call while the axis is being pressed
[Tooltip("The method(s) to call while the axis is being pressed")]
public AxisEvent AxisPressed;
[Header("Pressed Behaviour")]
[MMInformation("Here you can set the opacity of the button when it's pressed. Useful for visual feedback.",MMInformationAttribute.InformationType.Info,false)]
/// the new opacity to apply to the canvas group when the axis is pressed
[Tooltip("the new opacity to apply to the canvas group when the axis is pressed")]
public float PressedOpacity = 0.5f;
/// the value to send the bound method when the axis is pressed
[Tooltip("the value to send the bound method when the axis is pressed")]
public float AxisValue;
[Header("Mouse Mode")]
[MMInformation("If you set this to true, you'll need to actually press the axis for it to be triggered, otherwise a simple hover will trigger it (better for touch input).", MMInformationAttribute.InformationType.Info,false)]
/// If you set this to true, you'll need to actually press the axis for it to be triggered, otherwise a simple hover will trigger it (better for touch input).
[Tooltip("If you set this to true, you'll need to actually press the axis for it to be triggered, otherwise a simple hover will trigger it (better for touch input).")]
public bool MouseMode = false;
public virtual ButtonStates CurrentState { get; protected set; }
protected CanvasGroup _canvasGroup;
protected float _initialOpacity;
/// <summary>
/// On Start, we get our canvasgroup and set our initial alpha
/// </summary>
protected virtual void Awake()
{
_canvasGroup = GetComponent<CanvasGroup>();
if (_canvasGroup!=null)
{
_initialOpacity = _canvasGroup.alpha;
}
ResetButton();
}
/// <summary>
/// Every frame, if the touch zone is pressed, we trigger the bound method if it exists
/// </summary>
protected virtual void Update()
{
if (AxisPressed != null)
{
if (CurrentState == ButtonStates.ButtonPressed)
{
AxisPressed.Invoke(AxisValue);
}
}
}
/// <summary>
/// At the end of every frame, we change our button's state if needed
/// </summary>
protected virtual void LateUpdate()
{
if (CurrentState == ButtonStates.ButtonUp)
{
CurrentState = ButtonStates.Off;
}
if (CurrentState == ButtonStates.ButtonDown)
{
CurrentState = ButtonStates.ButtonPressed;
}
}
/// <summary>
/// Triggers the bound pointer down action
/// </summary>
public virtual void OnPointerDown(PointerEventData data)
{
if (CurrentState != ButtonStates.Off)
{
return;
}
CurrentState = ButtonStates.ButtonDown;
if (_canvasGroup!=null)
{
_canvasGroup.alpha=PressedOpacity;
}
if (AxisPressedFirstTime!=null)
{
AxisPressedFirstTime.Invoke();
}
}
/// <summary>
/// Triggers the bound pointer up action
/// </summary>
public virtual void OnPointerUp(PointerEventData data)
{
if (CurrentState != ButtonStates.ButtonPressed && CurrentState != ButtonStates.ButtonDown)
{
return;
}
CurrentState = ButtonStates.ButtonUp;
if (_canvasGroup!=null)
{
_canvasGroup.alpha=_initialOpacity;
}
if (AxisReleased != null)
{
AxisReleased.Invoke();
}
AxisPressed.Invoke(0);
}
/// <summary>
/// OnEnable, we reset our button state
/// </summary>
protected virtual void OnEnable()
{
ResetButton();
}
/// <summary>
/// Resets the button's state and opacity
/// </summary>
protected virtual void ResetButton()
{
CurrentState = ButtonStates.Off;
_canvasGroup.alpha = _initialOpacity;
CurrentState = ButtonStates.Off;
}
/// <summary>
/// Triggers the bound pointer enter action when touch enters zone
/// </summary>
public virtual void OnPointerEnter(PointerEventData data)
{
if (!MouseMode)
{
OnPointerDown (data);
}
}
/// <summary>
/// Triggers the bound pointer exit action when touch is out of zone
/// </summary>
public virtual void OnPointerExit(PointerEventData data)
{
if (!MouseMode)
{
OnPointerUp(data);
}
}
}
}
#endif

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: fc509a62d8e6cfc45a8431d8d586b9f6
timeCreated: 1462615497
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMControls/MMTouchAxis.cs
uploadId: 830868

View File

@@ -0,0 +1,553 @@
#if MM_UI
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;
namespace MoreMountains.Tools
{
/// <summary>
/// Add this component to a GUI Image to have it act as a button.
/// Bind pressed down, pressed continually and released actions to it from the inspector
/// Handles mouse and multi touch
/// </summary>
[RequireComponent(typeof(Rect))]
[RequireComponent(typeof(CanvasGroup))]
[AddComponentMenu("More Mountains/Tools/Controls/MM Touch Button")]
public class MMTouchButton : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IPointerExitHandler, IPointerEnterHandler, ISubmitHandler
{
[Header("Interaction")]
/// whether or not this button can be interacted with
public bool Interactable = true;
/// The different possible states for the button :
/// Off (default idle state), ButtonDown (button pressed for the first time), ButtonPressed (button being pressed), ButtonUp (button being released), Disabled (unclickable but still present on screen)
/// ButtonDown and ButtonUp will only last one frame, the others will last however long you press them / disable them / do nothing
public enum ButtonStates { Off, ButtonDown, ButtonPressed, ButtonUp, Disabled }
[Header("Binding")]
/// The method(s) to call when the button gets pressed down
[Tooltip("The method(s) to call when the button gets pressed down")]
public UnityEvent ButtonPressedFirstTime;
/// The method(s) to call when the button gets released
[Tooltip("The method(s) to call when the button gets released")]
public UnityEvent ButtonReleased;
/// The method(s) to call while the button is being pressed
[Tooltip("The method(s) to call while the button is being pressed")]
public UnityEvent ButtonPressed;
[Header("Sprite Swap")]
[MMInformation("Here you can define, for disabled and pressed states, if you want a different sprite, and a different color.", MMInformationAttribute.InformationType.Info,false)]
/// the sprite to use on the button when it's in the disabled state
[Tooltip("the sprite to use on the button when it's in the disabled state")]
public Sprite DisabledSprite;
/// whether or not to change color when the button is disabled
[Tooltip("whether or not to change color when the button is disabled")]
public bool DisabledChangeColor = false;
/// the color to use when the button is disabled
[Tooltip("the color to use when the button is disabled")]
[MMCondition("DisabledChangeColor", true)]
public Color DisabledColor = Color.white;
/// the sprite to use on the button when it's in the pressed state
[Tooltip("the sprite to use on the button when it's in the pressed state")]
public Sprite PressedSprite;
/// whether or not to change the button color on press
[Tooltip("whether or not to change the button color on press")]
public bool PressedChangeColor = false;
/// the color to use when the button is pressed
[Tooltip("the color to use when the button is pressed")]
[MMCondition("PressedChangeColor", true)]
public Color PressedColor= Color.white;
/// the sprite to use on the button when it's in the highlighted state
[Tooltip("the sprite to use on the button when it's in the highlighted state")]
public Sprite HighlightedSprite;
/// whether or not to change color when highlighting the button
[Tooltip("whether or not to change color when highlighting the button")]
public bool HighlightedChangeColor = false;
/// the color to use when the button is highlighted
[Tooltip("the color to use when the button is highlighted")]
[MMCondition("HighlightedChangeColor", true)]
public Color HighlightedColor = Color.white;
[Header("Opacity")]
[MMInformation("Here you can set different opacities for the button when it's pressed, idle, or disabled. Useful for visual feedback.",MMInformationAttribute.InformationType.Info,false)]
/// the new opacity to apply to the canvas group when the button is pressed
[Tooltip("the opacity to apply to the canvas group when the button is pressed")]
public float PressedOpacity = 1f;
/// the new opacity to apply to the canvas group when the button is idle
[Tooltip("the new opacity to apply to the canvas group when the button is idle")]
public float IdleOpacity = 1f;
/// the new opacity to apply to the canvas group when the button is disabled
[Tooltip("the new opacity to apply to the canvas group when the button is disabled")]
public float DisabledOpacity = 1f;
[Header("Delays")]
[MMInformation("Specify here the delays to apply when the button is pressed initially, and when it gets released. Usually you'll keep them at 0.",MMInformationAttribute.InformationType.Info,false)]
/// the delay to apply to events when the button gets pressed for the first time
[Tooltip("the delay to apply to events when the button gets pressed for the first time")]
public float PressedFirstTimeDelay = 0f;
/// the delay to apply to events when the button gets released
[Tooltip("the delay to apply to events when the button gets released")]
public float ReleasedDelay = 0f;
[Header("Buffer")]
/// the duration (in seconds) after a press during which the button can't be pressed again
[Tooltip("the duration (in seconds) after a press during which the button can't be pressed again")]
public float BufferDuration = 0f;
[Header("Animation")]
[MMInformation("Here you can bind an animator, and specify animation parameter names for the various states.",MMInformationAttribute.InformationType.Info,false)]
/// an animator you can bind to this button to have its states updated to reflect the button's states
[Tooltip("an animator you can bind to this button to have its states updated to reflect the button's states")]
public Animator Animator;
/// the name of the animation parameter to turn true when the button is idle
[Tooltip("the name of the animation parameter to turn true when the button is idle")]
public string IdleAnimationParameterName = "Idle";
/// the name of the animation parameter to turn true when the button is disabled
[Tooltip("the name of the animation parameter to turn true when the button is disabled")]
public string DisabledAnimationParameterName = "Disabled";
/// the name of the animation parameter to turn true when the button is pressed
[Tooltip("the name of the animation parameter to turn true when the button is pressed")]
public string PressedAnimationParameterName = "Pressed";
[Header("Mouse Mode")]
[MMInformation("If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better to leave it unchecked if you're going for touch input).", MMInformationAttribute.InformationType.Info,false)]
/// If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input).
[Tooltip("If you set this to true, you'll need to actually press the button for it to be triggered, otherwise a simple hover will trigger it (better for touch input).")]
public bool MouseMode = false;
public bool PreventLeftClick = false;
public bool PreventMiddleClick = true;
public bool PreventRightClick = true;
public virtual bool ReturnToInitialSpriteAutomatically { get; set; }
/// the current state of the button (off, down, pressed or up)
public virtual ButtonStates CurrentState { get; protected set; }
public event System.Action<PointerEventData.FramePressState, PointerEventData> ButtonStateChange;
protected bool _zonePressed = false;
protected CanvasGroup _canvasGroup;
protected float _initialOpacity;
protected Animator _animator;
protected Image _image;
protected Sprite _initialSprite;
protected Color _initialColor;
protected float _lastClickTimestamp = 0f;
protected Selectable _selectable;
/// <summary>
/// On Start, we get our canvasgroup and set our initial alpha
/// </summary>
protected virtual void Awake()
{
Initialization ();
}
/// <summary>
/// On init we grab our Image, Animator and CanvasGroup and set them up
/// </summary>
protected virtual void Initialization()
{
ReturnToInitialSpriteAutomatically = true;
_selectable = GetComponent<Selectable> ();
_image = GetComponent<Image> ();
if (_image != null)
{
_initialColor = _image.color;
_initialSprite = _image.sprite;
}
_animator = GetComponent<Animator> ();
if (Animator != null)
{
_animator = Animator;
}
_canvasGroup = GetComponent<CanvasGroup>();
if (_canvasGroup!=null)
{
_initialOpacity = IdleOpacity;
_canvasGroup.alpha = _initialOpacity;
_initialOpacity = _canvasGroup.alpha;
}
ResetButton();
}
/// <summary>
/// Every frame, if the touch zone is pressed, we trigger the OnPointerPressed method, to detect continuous press
/// </summary>
protected virtual void Update()
{
switch (CurrentState)
{
case ButtonStates.Off:
SetOpacity (IdleOpacity);
if ((_image != null) && (ReturnToInitialSpriteAutomatically))
{
_image.color = _initialColor;
_image.sprite = _initialSprite;
}
if (_selectable != null)
{
_selectable.interactable = true;
if (EventSystem.current.currentSelectedGameObject == this.gameObject)
{
if ((_image != null) && HighlightedChangeColor)
{
_image.color = HighlightedColor;
}
if (HighlightedSprite != null)
{
_image.sprite = HighlightedSprite;
}
}
}
break;
case ButtonStates.Disabled:
SetOpacity (DisabledOpacity);
if (_image != null)
{
if (DisabledSprite != null)
{
_image.sprite = DisabledSprite;
}
if (DisabledChangeColor)
{
_image.color = DisabledColor;
}
}
if (_selectable != null)
{
_selectable.interactable = false;
}
break;
case ButtonStates.ButtonDown:
break;
case ButtonStates.ButtonPressed:
SetOpacity (PressedOpacity);
OnPointerPressed();
if (_image != null)
{
if (PressedSprite != null)
{
_image.sprite = PressedSprite;
}
if (PressedChangeColor)
{
_image.color = PressedColor;
}
}
break;
case ButtonStates.ButtonUp:
break;
}
UpdateAnimatorStates ();
}
/// <summary>
/// At the end of every frame, we change our button's state if needed
/// </summary>
protected virtual void LateUpdate()
{
if (CurrentState == ButtonStates.ButtonUp)
{
CurrentState = ButtonStates.Off;
}
if (CurrentState == ButtonStates.ButtonDown)
{
CurrentState = ButtonStates.ButtonPressed;
}
}
/// <summary>
/// Triggers the ButtonStateChange event for the specified state
/// </summary>
/// <param name="newState"></param>
/// <param name="data"></param>
public virtual void InvokeButtonStateChange(PointerEventData.FramePressState newState, PointerEventData data)
{
ButtonStateChange?.Invoke(newState, data);
}
/// <summary>
/// Checks whether or not the specified click is allowed, if in mouse mode
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
protected virtual bool AllowedClick(PointerEventData data)
{
if (!MouseMode)
{
return true;
}
if (PreventLeftClick && data.button == PointerEventData.InputButton.Left)
{
return false;
}
if (PreventMiddleClick && data.button == PointerEventData.InputButton.Middle)
{
return false;
}
if (PreventRightClick && data.button == PointerEventData.InputButton.Right)
{
return false;
}
return true;
}
/// <summary>
/// Triggers the bound pointer down action
/// </summary>
public virtual void OnPointerDown(PointerEventData data)
{
if (!Interactable)
{
return;
}
if (!AllowedClick(data))
{
return;
}
if (Time.unscaledTime - _lastClickTimestamp < BufferDuration)
{
return;
}
if (CurrentState != ButtonStates.Off)
{
return;
}
CurrentState = ButtonStates.ButtonDown;
_lastClickTimestamp = Time.unscaledTime;
InvokeButtonStateChange(PointerEventData.FramePressState.Pressed, data);
if ((Time.timeScale != 0) && (PressedFirstTimeDelay > 0))
{
Invoke ("InvokePressedFirstTime", PressedFirstTimeDelay);
}
else
{
ButtonPressedFirstTime.Invoke();
}
}
/// <summary>
/// Raises the ButtonPressedFirstTime event
/// </summary>
protected virtual void InvokePressedFirstTime()
{
if (ButtonPressedFirstTime!=null)
{
ButtonPressedFirstTime.Invoke();
}
}
/// <summary>
/// Triggers the bound pointer up action
/// </summary>
public virtual void OnPointerUp(PointerEventData data)
{
if (!Interactable)
{
return;
}
if (!AllowedClick(data))
{
return;
}
if (CurrentState != ButtonStates.ButtonPressed && CurrentState != ButtonStates.ButtonDown)
{
return;
}
CurrentState = ButtonStates.ButtonUp;
InvokeButtonStateChange(PointerEventData.FramePressState.Released, data);
if ((Time.timeScale != 0) && (ReleasedDelay > 0))
{
Invoke ("InvokeReleased", ReleasedDelay);
}
else
{
ButtonReleased.Invoke();
}
}
/// <summary>
/// Invokes the ButtonReleased event
/// </summary>
protected virtual void InvokeReleased()
{
if (ButtonReleased != null)
{
ButtonReleased.Invoke();
}
}
/// <summary>
/// Triggers the bound pointer pressed action
/// </summary>
public virtual void OnPointerPressed()
{
if (!Interactable)
{
return;
}
CurrentState = ButtonStates.ButtonPressed;
if (ButtonPressed != null)
{
ButtonPressed.Invoke();
}
}
/// <summary>
/// Resets the button's state and opacity
/// </summary>
protected virtual void ResetButton()
{
SetOpacity(_initialOpacity);
CurrentState = ButtonStates.Off;
}
/// <summary>
/// Triggers the bound pointer enter action when touch enters zone
/// </summary>
public virtual void OnPointerEnter(PointerEventData data)
{
if (!Interactable)
{
return;
}
if (!AllowedClick(data))
{
return;
}
if (!MouseMode)
{
OnPointerDown (data);
}
}
/// <summary>
/// Triggers the bound pointer exit action when touch is out of zone
/// </summary>
public virtual void OnPointerExit(PointerEventData data)
{
if (!Interactable)
{
return;
}
if (!AllowedClick(data))
{
return;
}
if (!MouseMode)
{
OnPointerUp(data);
}
}
/// <summary>
/// OnEnable, we reset our button state
/// </summary>
protected virtual void OnEnable()
{
ResetButton();
}
/// <summary>
/// On disable we reset our flags and disable the button
/// </summary>
private void OnDisable()
{
bool wasActive = CurrentState != ButtonStates.Off && CurrentState != ButtonStates.Disabled && CurrentState != ButtonStates.ButtonUp;
DisableButton();
CurrentState = ButtonStates.Off;
if (wasActive)
{
InvokeButtonStateChange(PointerEventData.FramePressState.Released, null);
ButtonReleased?.Invoke();
}
}
/// <summary>
/// Prevents the button from receiving touches
/// </summary>
public virtual void DisableButton()
{
CurrentState = ButtonStates.Disabled;
}
/// <summary>
/// Allows the button to receive touches
/// </summary>
public virtual void EnableButton()
{
if (CurrentState == ButtonStates.Disabled)
{
CurrentState = ButtonStates.Off;
}
}
/// <summary>
/// Sets the canvas group's opacity to the requested value
/// </summary>
/// <param name="newOpacity"></param>
protected virtual void SetOpacity(float newOpacity)
{
if (_canvasGroup!=null)
{
_canvasGroup.alpha = newOpacity;
}
}
/// <summary>
/// Updates animator states based on the current state of the button
/// </summary>
protected virtual void UpdateAnimatorStates ()
{
if (_animator == null)
{
return;
}
if (DisabledAnimationParameterName != null)
{
_animator.SetBool (DisabledAnimationParameterName, (CurrentState == ButtonStates.Disabled));
}
if (PressedAnimationParameterName != null)
{
_animator.SetBool (PressedAnimationParameterName, (CurrentState == ButtonStates.ButtonPressed));
}
if (IdleAnimationParameterName != null)
{
_animator.SetBool (IdleAnimationParameterName, (CurrentState == ButtonStates.Off));
}
}
/// <summary>
/// On submit, raises the appropriate events
/// </summary>
/// <param name="eventData"></param>
public virtual void OnSubmit(BaseEventData eventData)
{
if (ButtonPressedFirstTime!=null)
{
ButtonPressedFirstTime.Invoke();
}
if (ButtonReleased!=null)
{
ButtonReleased.Invoke ();
}
}
}
}
#endif

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 87a286771fa86194e979f187a1691a2a
timeCreated: 1462399350
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMControls/MMTouchButton.cs
uploadId: 830868

View File

@@ -0,0 +1,75 @@
using UnityEngine;
namespace MoreMountains.Tools
{
[RequireComponent(typeof(CanvasGroup))]
[AddComponentMenu("More Mountains/Tools/Controls/MM Touch Controls")]
public class MMTouchControls : MonoBehaviour
{
public enum InputForcedMode { None, Mobile, Desktop }
[MMInformation("If you check Auto Mobile Detection, the engine will automatically switch to mobile controls when your build target is Android or iOS. You can also force mobile or desktop (keyboard, gamepad) controls using the dropdown below.\nNote that if you don't need mobile controls and/or GUI this component can also work on its own, just put it on an empty GameObject instead.", MMInformationAttribute.InformationType.Info,false)]
/// If you check Auto Mobile Detection, the engine will automatically switch to mobile controls when your build target is Android or iOS.
/// You can also force mobile or desktop (keyboard, gamepad) controls using the dropdown below.Note that if you don't need mobile controls
/// and/or GUI this component can also work on its own, just put it on an empty GameObject instead.
[Tooltip("If you check Auto Mobile Detection, the engine will automatically switch to mobile controls when your build target is Android or iOS." +
"You can also force mobile or desktop (keyboard, gamepad) controls using the dropdown below.Note that if you don't need mobile controls " +
"and/or GUI this component can also work on its own, just put it on an empty GameObject instead.")]
public bool AutoMobileDetection = true;
/// Force desktop mode (gamepad, keyboard...) or mobile (touch controls)
[Tooltip("Force desktop mode (gamepad, keyboard...) or mobile (touch controls)")]
public InputForcedMode ForcedMode;
public virtual bool IsMobile { get; protected set; }
protected CanvasGroup _canvasGroup;
protected float _initialMobileControlsAlpha;
/// <summary>
/// We get the player from its tag.
/// </summary>
protected virtual void Start()
{
_canvasGroup = GetComponent<CanvasGroup>();
_initialMobileControlsAlpha = _canvasGroup.alpha;
SetMobileControlsActive(false);
IsMobile=false;
if (AutoMobileDetection)
{
#if UNITY_ANDROID || UNITY_IPHONE
SetMobileControlsActive(true);
IsMobile = true;
#endif
}
if (ForcedMode==InputForcedMode.Mobile)
{
SetMobileControlsActive(true);
IsMobile = true;
}
if (ForcedMode==InputForcedMode.Desktop)
{
SetMobileControlsActive(false);
IsMobile = false;
}
}
/// <summary>
/// Use this method to enable or disable mobile controls
/// </summary>
/// <param name="state"></param>
public virtual void SetMobileControlsActive(bool state)
{
if (_canvasGroup!=null)
{
_canvasGroup.gameObject.SetActive(state);
if (state)
{
_canvasGroup.alpha=_initialMobileControlsAlpha;
}
else
{
_canvasGroup.alpha=0;
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: fec2335f22dc84b549dd9dfdeebca3e0
timeCreated: 1467025573
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMControls/MMTouchControls.cs
uploadId: 830868

View File

@@ -0,0 +1,359 @@
#if MM_UI
using UnityEngine;
using UnityEngine.EventSystems;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace MoreMountains.Tools
{
/// <summary>
/// Add this component to a UI rectangle and it'll act as a detection zone for a follower joystick.
/// Note that this component extends the MMTouchJoystick class so you don't need to add another joystick to it. It's both the detection zone and the stick itself.
/// </summary>
[AddComponentMenu("More Mountains/Tools/Controls/MM Touch Follower Joystick")]
public class MMTouchFollowerJoystick : MMTouchJoystick
{
[MMInspectorGroup("Follower Joystick", true, 23)]
/// the canvas group to use as the joystick's knob - the part that moves under your thumb
[Tooltip("the canvas group to use as the joystick's knob - the part that moves under your thumb")]
public CanvasGroup KnobCanvasGroup;
/// the canvas group to use as the joystick's background
[Tooltip("the canvas group to use as the joystick's background")]
public CanvasGroup BackgroundCanvasGroup;
/// if this is true, the joystick will return back to its initial position when released
[Tooltip("if this is true, the joystick will return back to its initial position when released")]
public bool ResetPositionToInitialOnRelease = false;
/// if this is true, the background will follow its target with interpolation, otherwise it'll be instant movement
[Tooltip("if this is true, the background will follow its target with interpolation, otherwise it'll be instant movement")]
public bool InterpolateFollowMovement = false;
/// if in interpolate mode, this defines the speed at which the backgrounds follows the knob
[Tooltip("if in interpolate mode, this defines the speed at which the backgrounds follows the knob")]
[MMCondition("InterpolateFollowMovement", true)]
public float InterpolateFollowMovementSpeed = 0.3f;
/// whether or not to add a spring to the interpolation of the background movement
[Tooltip("whether or not to add a spring to the interpolation of the background movement")]
[MMCondition("InterpolateFollowMovement", true)]
public bool SpringFollowInterpolation = false;
/// when in SpringFollowInterpolation mode, the amount of damping to apply to the spring
[Tooltip("when in SpringFollowInterpolation mode, the amount of damping to apply to the spring")]
[MMCondition("SpringFollowInterpolation", true)]
public float SpringDamping = 0.6f;
/// when in SpringFollowInterpolation mode, the frequency to apply to the spring
[Tooltip("when in SpringFollowInterpolation mode, the frequency to apply to the spring")]
[MMCondition("SpringFollowInterpolation", true)]
public float SpringFrequency = 4f;
[MMInspectorGroup("Background Constraints", true, 24)]
/// if this is true, the joystick won't be able to travel beyond the bounds of the top level canvas
[Tooltip("if this is true, the joystick won't be able to travel beyond the bounds of the top level canvas")]
public bool ShouldConstrainBackground = true;
/// the rect to consider as a background constraint zone, if left empty, will be auto created
[Tooltip("the rect to consider as a background constraint zone, if left empty, will be auto created")]
public RectTransform BackgroundConstraintRectTransform;
/// the left padding to apply to the background constraint
[Tooltip("the left padding to apply to the background constraint")]
public float BackgroundConstraintPaddingLeft;
/// the right padding to apply to the background constraint
[Tooltip("the right padding to apply to the background constraint")]
public float BackgroundConstraintPaddingRight;
/// the top padding to apply to the background constraint
[Tooltip("the top padding to apply to the background constraint")]
public float BackgroundConstraintPaddingTop;
/// the bottom padding to apply to the background constraint
[Tooltip("the bottom padding to apply to the background constraint")]
public float BackgroundConstraintPaddingBottom;
protected Vector3 _initialPosition;
protected Vector3 _newPosition;
protected RectTransform _rectTransform;
protected RectTransform _backgroundRectTransform;
protected Vector3[] _innerRectCorners = new Vector3[4];
protected Vector3 _newBackgroundPosition;
protected Vector3 _backgroundPositionTarget;
protected Vector3 _innerRectTransformBottomLeft;
protected Vector3 _innerRectTransformTopLeft;
protected Vector3 _innerRectTransformTopRight;
protected Vector3 _innerRectTransformBottomRight;
protected Vector3 _springVelocity;
protected Plane _canvasPlane;
protected bool _canvasPlaneInitialized = false;
/// <summary>
/// On Start, we instantiate our joystick's image if there's one
/// </summary>
protected override void Start()
{
base.Start();
// we store the detection zone's initial position
_rectTransform = GetComponent<RectTransform>();
_backgroundRectTransform = BackgroundCanvasGroup.GetComponent<RectTransform>();
_initialPosition = _backgroundRectTransform.position;
_backgroundPositionTarget = _initialPosition;
CreateInnerRect();
}
/// <summary>
/// On initialize, we set our knob transform
/// </summary>
public override void Initialize()
{
base.Initialize();
SetKnobTransform(KnobCanvasGroup.transform);
_canvasGroup = KnobCanvasGroup;
_initialOpacity = _canvasGroup.alpha;
}
/// <summary>
/// On update, we handle movement interpolation
/// </summary>
protected override void Update()
{
base.Update();
HandleMovementInterpolation();
}
/// <summary>
/// Handles the movement of the background relative to the knob
/// </summary>
protected virtual void HandleMovementInterpolation()
{
if (!InterpolateFollowMovement)
{
BackgroundCanvasGroup.transform.position = _backgroundPositionTarget;
return;
}
if (SpringFollowInterpolation)
{
_newBackgroundPosition = BackgroundCanvasGroup.transform.position;
MMMaths.Spring(ref _newBackgroundPosition, _backgroundPositionTarget, ref _springVelocity, SpringDamping, SpringFrequency, Time.unscaledDeltaTime);
BackgroundCanvasGroup.transform.position = _newBackgroundPosition;
}
else
{
BackgroundCanvasGroup.transform.position = MMMaths.Lerp(BackgroundCanvasGroup.transform.position, _backgroundPositionTarget, InterpolateFollowMovementSpeed, Time.unscaledDeltaTime);
}
}
/// <summary>
/// Creates a constraining inner rect
/// </summary>
protected virtual void CreateInnerRect()
{
if (!ShouldConstrainBackground)
{
return;
}
// we create an inner rect if one wasn't provided
if (BackgroundConstraintRectTransform == null)
{
GameObject innerRect = new GameObject();
innerRect.transform.SetParent(this.transform);
innerRect.name = "BackgroundConstraintRectTransform";
BackgroundConstraintRectTransform = innerRect.AddComponent<RectTransform>();
BackgroundConstraintRectTransform.anchorMin = _rectTransform.anchorMin;
BackgroundConstraintRectTransform.anchorMax = _rectTransform.anchorMax;
BackgroundConstraintRectTransform.position = _rectTransform.position;
BackgroundConstraintRectTransform.localScale = _rectTransform.localScale;
BackgroundConstraintRectTransform.sizeDelta = new Vector2(_rectTransform.sizeDelta.x - _backgroundRectTransform.sizeDelta.y, _rectTransform.sizeDelta.y - _backgroundRectTransform.sizeDelta.y);
}
// we apply the padding
BackgroundConstraintRectTransform.offsetMin += new Vector2(BackgroundConstraintPaddingLeft, BackgroundConstraintPaddingBottom);
BackgroundConstraintRectTransform.offsetMax -= new Vector2(BackgroundConstraintPaddingRight, BackgroundConstraintPaddingTop);
// we store our corners
BackgroundConstraintRectTransform.GetWorldCorners(_innerRectCorners);
_innerRectTransformBottomLeft = _innerRectCorners[0];
_innerRectTransformTopLeft = _innerRectCorners[1];
_innerRectTransformTopRight = _innerRectCorners[2];
_innerRectTransformBottomRight = _innerRectCorners[3];
}
/// <summary>
/// When the zone is pressed, we move our joystick accordingly
/// </summary>
/// <param name="data">Data.</param>
public override void OnPointerDown(PointerEventData data)
{
base.OnPointerDown(data);
if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera && TargetCamera != null)
{
_canvasPlane = new Plane(-TargetCamera.transform.forward, this.transform.position);
_canvasPlaneInitialized = true;
}
Vector3 pointerWorldPos = ConvertToWorld(data.position);
if (_canvasPlaneInitialized)
{
Ray ray = TargetCamera.ScreenPointToRay(data.position);
float enter;
if (_canvasPlane.Raycast(ray, out enter))
{
pointerWorldPos = ray.GetPoint(enter);
}
}
else
{
pointerWorldPos.z = this.transform.position.z;
}
_newPosition = pointerWorldPos;
_backgroundPositionTarget = _newPosition;
ConstrainBackground();
SetNeutralPosition(BackgroundCanvasGroup.transform.position);
_knobTransform.position = _newPosition;
_initialZPosition = _newPosition.z;
ComputeJoystickValue();
}
/// <summary>
/// On drag, we adjust our target and constrain our background
/// </summary>
/// <param name="eventData"></param>
public override void OnDrag(PointerEventData eventData)
{
OnDragEvent.Invoke();
Vector3 pointerWorldPos = ConvertToWorld(eventData.position);
if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera && TargetCamera != null)
{
Plane canvasPlane = new Plane(-TargetCamera.transform.forward, BackgroundCanvasGroup.transform.position);
Ray ray = TargetCamera.ScreenPointToRay(eventData.position);
float enter;
if (canvasPlane.Raycast(ray, out enter))
{
pointerWorldPos = ray.GetPoint(enter);
}
}
_knobTransform.position = pointerWorldPos;
float distance = Vector3.Distance(_knobTransform.position, BackgroundCanvasGroup.transform.position);
if (distance >= ComputedMaxRange)
{
Vector3 direction = (_knobTransform.position - BackgroundCanvasGroup.transform.position).normalized;
_backgroundPositionTarget = BackgroundCanvasGroup.transform.position + direction * (distance - ComputedMaxRange);
}
ConstrainBackground();
ComputeJoystickValue();
}
/// <summary>
/// Determines the value of the joystick by computing the
/// </summary>
protected virtual void ComputeJoystickValue()
{
Vector3 worldDelta = _knobTransform.position - BackgroundCanvasGroup.transform.position;
Vector3 localDelta = TransformToLocalSpace(worldDelta);
float distance = worldDelta.magnitude;
if (distance <= ComputedMaxRange)
{
RawValue.x = EvaluateInputValue(localDelta.x);
RawValue.y = EvaluateInputValue(localDelta.y);
}
else
{
RawValue.x = Mathf.InverseLerp(0, distance, Mathf.Abs(localDelta.x)) * Mathf.Sign(localDelta.x);
RawValue.y = Mathf.InverseLerp(0, distance, Mathf.Abs(localDelta.y)) * Mathf.Sign(localDelta.y);
}
}
/// <summary>
/// Clamps the background inside the inner rect
/// </summary>
protected virtual void ConstrainBackground()
{
if (!ShouldConstrainBackground)
{
return;
}
_newBackgroundPosition = _backgroundPositionTarget;
_newBackgroundPosition.x = Mathf.Clamp(_newBackgroundPosition.x , _innerRectTransformTopLeft.x, _innerRectTransformTopRight.x);
_newBackgroundPosition.y = Mathf.Clamp(_newBackgroundPosition.y , _innerRectTransformBottomLeft.y, _innerRectTransformTopLeft.y);
_backgroundPositionTarget = _newBackgroundPosition;
}
/// <summary>
/// On pointer up we reset our joystick
/// </summary>
/// <param name="data"></param>
public override void OnPointerUp(PointerEventData data)
{
base.OnPointerUp(data);
ResetJoystick();
_knobTransform.position = _backgroundPositionTarget;
if (ResetPositionToInitialOnRelease)
{
_backgroundPositionTarget = _initialPosition;
_knobTransform.position = _initialPosition;
}
}
/// <summary>
/// We don't clamp the stick anymore
/// </summary>
protected override void ClampToBounds()
{
_newTargetPosition = _newTargetPosition - _neutralPosition;
}
#if UNITY_EDITOR
/// <summary>
/// Draws gizmos to show the constraining box' corners
/// </summary>
protected override void OnDrawGizmos()
{
if (!DrawGizmos)
{
return;
}
// Draws max range
Handles.color = MMColors.Orange;
if (!Application.isPlaying)
{
if (KnobCanvasGroup != null)
{
Handles.DrawWireDisc(KnobCanvasGroup.transform.position, this.transform.forward, ComputedMaxRange);
}
else
{
Handles.DrawWireDisc(this.transform.position, this.transform.forward, ComputedMaxRange);
}
}
else
{
Handles.DrawWireDisc(_backgroundRectTransform.position, this.transform.forward, ComputedMaxRange);
}
// Draws corners
if (BackgroundConstraintRectTransform != null)
{
float gizmoSize = 0.3f;
MMDebug.DrawGizmoPoint(_innerRectTransformBottomLeft, Color.cyan, gizmoSize);
MMDebug.DrawGizmoPoint(_innerRectTransformTopLeft, Color.cyan, gizmoSize);
MMDebug.DrawGizmoPoint(_innerRectTransformTopRight, Color.cyan, gizmoSize);
MMDebug.DrawGizmoPoint(_innerRectTransformBottomRight, Color.cyan, gizmoSize);
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 12f6f90faaf30834980e50fda0c74a9c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMControls/MMTouchFollowerJoystick.cs
uploadId: 830868

View File

@@ -0,0 +1,436 @@
#if MM_UI
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using System;
using Unity.Collections;
using UnityEngine.UI;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace MoreMountains.Tools
{
[System.Serializable]
public class JoystickEvent : UnityEvent<Vector2> {}
[System.Serializable]
public class JoystickFloatEvent : UnityEvent<float> {}
/// <summary>
/// Joystick input class.
/// In charge of the behaviour of the joystick mobile touch input.
/// Bind its actions from the inspector
/// Handles mouse and multi touch
/// </summary>
[RequireComponent(typeof(Rect))]
[RequireComponent(typeof(CanvasGroup))]
[AddComponentMenu("More Mountains/Tools/Controls/MM Touch Joystick")]
public class MMTouchJoystick : MMMonoBehaviour, IDragHandler, IEndDragHandler, IPointerDownHandler, IPointerUpHandler
{
public enum MaxRangeModes { Distance, DistanceToTransform }
[MMInspectorGroup("Camera", true, 16)]
/// The camera to use as the reference for any ScreenToWorldPoint computations
[Tooltip("The camera to use as the reference for any ScreenToWorldPoint computations")]
public Camera TargetCamera;
[MMInspectorGroup("Joystick Behaviour", true, 18)]
[Tooltip("Determines whether the horizontal axis of this stick should be enabled. If not, the stick will only move vertically.")]
/// Is horizontal axis allowed
public bool HorizontalAxisEnabled = true;
/// Is vertical axis allowed
[Tooltip("Determines whether the vertical axis of this stick should be enabled. If not, the stick will only move horizontally.")]
public bool VerticalAxisEnabled = true;
/// the mode in which to compute the range. Distance will be a flat value, DistanceToTransform will be a distance to a transform you can move around and potentially resize as you wish for various resolutions
[Tooltip("the mode in which to compute the range. Distance will be a flat value, DistanceToTransform will be a distance to a transform you can move around and potentially resize as you wish for various resolutions")]
public MaxRangeModes MaxRangeMode = MaxRangeModes.Distance;
/// The MaxRange is the maximum distance from its initial center position you can drag the joystick to
[Tooltip("The MaxRange is the maximum distance from its initial center position you can drag the joystick to.")]
[MMEnumCondition("MaxRangeMode", (int)MaxRangeModes.Distance)]
public float MaxRange = 1.5f;
/// in DistanceToTransform mode, the object whose distance to the center will be used to compute the max range. Note that this is computed once, at init. Call RefreshMaxRangeDistance() to recompute it.
[Tooltip("in DistanceToTransform mode, the object whose distance to the center will be used to compute the max range. Note that this is computed once, at init. Call RefreshMaxRangeDistance() to recompute it.")]
[MMEnumCondition("MaxRangeMode", (int)MaxRangeModes.DistanceToTransform)]
public Transform MaxRangeTransform;
public float ComputedMaxRange
{
get
{
if (Application.isPlaying)
{
return MaxRangeMode == MaxRangeModes.Distance ? MaxRange : _maxRangeTransformDistance;
}
else
{
if (MaxRangeMode == MaxRangeModes.Distance)
{
return MaxRange;
}
else
{
if (MaxRangeTransform == null)
{
return -1f;
}
RefreshMaxRangeDistance();
return _maxRangeTransformDistance;
}
}
}
}
[MMInspectorGroup("Value Events", true, 19)]
/// An event to use the raw value of the joystick
[Tooltip("An event to use the raw value of the joystick")]
public JoystickEvent JoystickValue;
/// An event to use the normalized value of the joystick
[Tooltip("An event to use the normalized value of the joystick")]
public JoystickEvent JoystickNormalizedValue;
// An event to use the joystick's amplitude (the magnitude of its Vector2 output)
[Tooltip("An event to use the joystick's amplitude (the magnitude of its Vector2 output)")]
public JoystickFloatEvent JoystickMagnitudeValue;
[MMInspectorGroup("Touch Events", true, 8)]
/// An event triggered when tapping the joystick for the first time
[Tooltip("An event triggered when tapping the joystick for the first time")]
public UnityEvent OnPointerDownEvent;
/// An event triggered when dragging the stick
[Tooltip("An event triggered when dragging the stick")]
public UnityEvent OnDragEvent;
/// An event triggered when releasing the stick
[Tooltip("An event triggered when releasing the stick")]
public UnityEvent OnPointerUpEvent;
[MMInspectorGroup("Rotating Direction Indicator", true, 20)]
/// an object you can rotate to show the direction of the joystick. Will only be visible if the movement is above a threshold
[Tooltip("an object you can rotate to show the direction of the joystick. Will only be visible if the movement is above a threshold")]
public Transform RotatingIndicator;
/// the threshold above which the rotating indicator will appear
[Tooltip("the threshold above which the rotating indicator will appear")]
public float RotatingIndicatorThreshold = 0.1f;
[MMInspectorGroup("Knob Opacity", true, 17)]
/// the new opacity to apply to the canvas group when the button is pressed
[Tooltip("the new opacity to apply to the canvas group when the button is pressed")]
public float PressedOpacity = 0.5f;
/// whether or not to interpolate opacity changes on the knob's canvas group
[Tooltip("whether or not to interpolate opacity changes on the knob's canvas group")]
public bool InterpolateOpacity = true;
/// the speed at which to interpolate opacity
[Tooltip("the speed at which to interpolate opacity")]
[MMCondition("InterpolateOpacity", true)]
public float InterpolateOpacitySpeed = 1f;
[MMInspectorGroup("Debug Output", true, 5)]
/// the raw value of the joystick, from 0 to 1 on each axis
[Tooltip("the raw value of the joystick, from 0 to 1 on each axis")]
[MMReadOnly]
public Vector2 RawValue;
/// the normalized value of the joystick
[Tooltip("the normalized value of the joystick")]
[MMReadOnly]
public Vector2 NormalizedValue;
/// the magnitude of the stick's vector
[Tooltip("the magnitude of the stick's vector")]
[MMReadOnly]
public float Magnitude;
/// whether or not to draw gizmos associated to this stick
[Tooltip("whether or not to draw gizmos associated to this stick")]
public bool DrawGizmos = true;
/// the render mode of the parent canvas this stick is on
public virtual RenderMode ParentCanvasRenderMode { get; protected set; }
protected Vector3 _neutralPosition;
protected Vector3 _newTargetPosition;
protected Vector3 _newJoystickPosition;
protected float _initialZPosition;
protected float _targetOpacity;
protected CanvasGroup _canvasGroup;
protected float _initialOpacity;
protected Transform _knobTransform;
protected bool _rotatingIndicatorIsNotNull = false;
protected float _maxRangeTransformDistance;
protected Canvas _parentCanvas;
/// <summary>
/// On Start we initialize our stick
/// </summary>
protected virtual void Start()
{
Initialize();
}
/// <summary>
/// Initializes the various parts of the stick
/// </summary>
/// <exception cref="Exception"></exception>
public virtual void Initialize()
{
if ((ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera) && (TargetCamera == null))
{
throw new Exception("MMTouchJoystick : you have to set a target camera");
}
_canvasGroup = GetComponent<CanvasGroup>();
_parentCanvas = GetComponentInParent<Canvas>();
_rotatingIndicatorIsNotNull = (RotatingIndicator != null);
RefreshMaxRangeDistance();
SetKnobTransform(this.transform);
SetNeutralPosition();
ParentCanvasRenderMode = GetComponentInParent<Canvas>().renderMode;
_initialZPosition = _knobTransform.position.z;
_initialOpacity = _canvasGroup.alpha;
}
/// <summary>
/// This method is used to compute the max range distance when in DistanceToTransform mode
/// </summary>
public virtual void RefreshMaxRangeDistance()
{
if (MaxRangeMode == MaxRangeModes.DistanceToTransform)
{
_maxRangeTransformDistance = Vector2.Distance(this.transform.position, MaxRangeTransform.position);
}
}
/// <summary>
/// Assigns a new transform as the joystick knob
/// </summary>
/// <param name="newTransform"></param>
public virtual void SetKnobTransform(Transform newTransform)
{
_knobTransform = newTransform;
}
/// <summary>
/// On Update we check for an orientation change if needed, and send our input values.
/// </summary>
protected virtual void Update()
{
NormalizedValue = RawValue.normalized;
Magnitude = RawValue.magnitude;
if (HorizontalAxisEnabled || VerticalAxisEnabled)
{
JoystickValue.Invoke(RawValue);
JoystickNormalizedValue.Invoke(NormalizedValue);
JoystickMagnitudeValue.Invoke(Magnitude);
}
RotateIndicator();
HandleOpacity();
}
/// <summary>
/// Changes or interpolates the opacity of the knob
/// </summary>
protected virtual void HandleOpacity()
{
if (InterpolateOpacity)
{
_canvasGroup.alpha = MMMaths.Lerp(_canvasGroup.alpha, _targetOpacity, InterpolateOpacitySpeed, Time.unscaledDeltaTime);
}
else
{
_canvasGroup.alpha = _targetOpacity;
}
}
/// <summary>
/// Rotates an indicator to match the rotation of the stick
/// </summary>
protected virtual void RotateIndicator()
{
if (!_rotatingIndicatorIsNotNull)
{
return;
}
RotatingIndicator.gameObject.SetActive(RawValue.magnitude > RotatingIndicatorThreshold);
float angle = Mathf.Atan2(RawValue.y, RawValue.x) * Mathf.Rad2Deg;
RotatingIndicator.localRotation = Quaternion.AngleAxis(angle, Vector3.forward);
}
/// <summary>
/// Sets the neutral position of the joystick
/// </summary>
public virtual void SetNeutralPosition()
{
_neutralPosition = _knobTransform.position;
}
public virtual void SetNeutralPosition(Vector3 newPosition)
{
_neutralPosition = newPosition;
}
/// <summary>
/// Handles dragging of the joystick
/// </summary>
public virtual void OnDrag(PointerEventData eventData)
{
OnDragEvent.Invoke();
_newTargetPosition = ConvertToWorld(eventData.position);
Vector3 localDelta = TransformToLocalSpace(_newTargetPosition - _neutralPosition);
localDelta = Vector2.ClampMagnitude(localDelta, ComputedMaxRange);
if (!HorizontalAxisEnabled)
{
localDelta.x = 0;
}
if (!VerticalAxisEnabled)
{
localDelta.y = 0;
}
RawValue.x = EvaluateInputValue(localDelta.x);
RawValue.y = EvaluateInputValue(localDelta.y);
_newTargetPosition = _neutralPosition + TransformToWorldSpace(localDelta);
_newJoystickPosition = _newTargetPosition;
_newJoystickPosition.z = _initialZPosition;
_knobTransform.position = _newJoystickPosition;
}
/// <summary>
/// Transforms a world space vector to the canvas/camera local space
/// </summary>
protected virtual Vector3 TransformToLocalSpace(Vector3 worldVector)
{
if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera && TargetCamera != null)
{
return Quaternion.Inverse(TargetCamera.transform.rotation) * worldVector;
}
return worldVector;
}
/// <summary>
/// Transforms a local space vector back to world space
/// </summary>
protected virtual Vector3 TransformToWorldSpace(Vector3 localVector)
{
if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera && TargetCamera != null)
{
return TargetCamera.transform.rotation * localVector;
}
return localVector;
}
protected virtual Vector3 ConvertToWorld(Vector3 position)
{
if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera)
{
float distance = _parentCanvas != null ? _parentCanvas.planeDistance : 0f;
position.z = distance;
return TargetCamera.ScreenToWorldPoint(position);
}
else
{
return position;
}
}
/// <summary>
/// Clamps the position of the new target
/// </summary>
protected virtual void ClampToBounds()
{
_newTargetPosition = Vector2.ClampMagnitude(_newTargetPosition - _neutralPosition, ComputedMaxRange);
}
/// <summary>
/// Resets the stick's position and values
/// </summary>
public virtual void ResetJoystick()
{
// we reset the stick's position
_newJoystickPosition = _neutralPosition;
_newJoystickPosition.z = _initialZPosition;
_knobTransform.position = _newJoystickPosition;
RawValue.x = 0f;
RawValue.y = 0f;
// we set its opacity back
_targetOpacity = _initialOpacity;
}
/// <summary>
/// We compute the axis value from the interval between neutral position, current stick position (vectorPosition) and max range
/// </summary>
/// <returns>The axis value, a float between -1 and 1</returns>
/// <param name="vectorPosition">stick position.</param>
protected virtual float EvaluateInputValue(float vectorPosition)
{
return Mathf.InverseLerp(0, ComputedMaxRange, Mathf.Abs(vectorPosition)) * Mathf.Sign(vectorPosition);
}
/// <summary>
/// What happens when the stick stops being dragged
/// </summary>
public virtual void OnEndDrag(PointerEventData eventData)
{
}
/// <summary>
/// What happens when the stick is released (even if no drag happened)
/// </summary>
/// <param name="data"></param>
public virtual void OnPointerUp(PointerEventData data)
{
ResetJoystick();
OnPointerUpEvent.Invoke();
}
/// <summary>
/// What happens when the stick is pressed for the first time
/// </summary>
/// <param name="data"></param>
public virtual void OnPointerDown(PointerEventData data)
{
_targetOpacity = PressedOpacity;
OnPointerDownEvent.Invoke();
}
/// <summary>
/// On enable, we initialize our stick
/// </summary>
protected virtual void OnEnable()
{
Initialize();
_targetOpacity = _initialOpacity;
}
#if UNITY_EDITOR
/// <summary>
/// Draws gizmos if needed
/// </summary>
protected virtual void OnDrawGizmos()
{
if (!DrawGizmos)
{
return;
}
Handles.color = MMColors.Orange;
if (!Application.isPlaying)
{
Handles.DrawWireDisc(this.transform.position, this.transform.forward, ComputedMaxRange);
}
else
{
Handles.DrawWireDisc(_neutralPosition, this.transform.forward, ComputedMaxRange);
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: 8f6a0577d46b80a4f929e57370c60650
timeCreated: 1462403537
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMControls/MMTouchJoystick.cs
uploadId: 830868

View File

@@ -0,0 +1,179 @@
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
#if MM_UI
using UnityEngine.EventSystems;
namespace MoreMountains.Tools
{
/// <summary>
/// Add this component to a UI rectangle and it'll act as a detection zone for a joystick.
/// Note that this component extends the MMTouchJoystick class so you don't need to add another joystick to it. It's both the detection zone and the stick itself.
/// </summary>
[AddComponentMenu("More Mountains/Tools/Controls/MM Touch Repositionable Joystick")]
public class MMTouchRepositionableJoystick : MMTouchJoystick, IPointerDownHandler
{
[MMInspectorGroup("Repositionable Joystick", true, 22)]
/// the canvas group to use as the joystick's knob
[Tooltip("the canvas group to use as the joystick's knob")]
public CanvasGroup KnobCanvasGroup;
/// the canvas group to use as the joystick's background
[Tooltip("the canvas group to use as the joystick's background")]
public CanvasGroup BackgroundCanvasGroup;
/// if this is true, the joystick won't be able to travel beyond the bounds of the top level canvas
[Tooltip("if this is true, the joystick won't be able to travel beyond the bounds of the top level canvas")]
public bool ConstrainToInitialRectangle = true;
/// if this is true, the joystick will return back to its initial position when released
[Tooltip("if this is true, the joystick will return back to its initial position when released")]
public bool ResetPositionToInitialOnRelease = false;
protected Vector3 _initialPosition;
protected Vector3 _newPosition;
protected CanvasGroup _knobCanvasGroup;
protected RectTransform _rectTransform;
/// <summary>
/// On Start, we instantiate our joystick's image if there's one
/// </summary>
protected override void Start()
{
base.Start();
_rectTransform = GetComponent<RectTransform>();
_initialPosition = BackgroundCanvasGroup.GetComponent<RectTransform>().position;
}
/// <summary>
/// On init we set our knob transform
/// </summary>
public override void Initialize()
{
base.Initialize();
SetKnobTransform(KnobCanvasGroup.transform);
_canvasGroup = KnobCanvasGroup;
_initialOpacity = _canvasGroup.alpha;
}
/// <summary>
/// When the zone is pressed, we move our joystick accordingly
/// </summary>
/// <param name="data">Data.</param>
public override void OnPointerDown(PointerEventData data)
{
_targetOpacity = PressedOpacity;
OnPointerDownEvent.Invoke();
_newPosition = ConvertToWorld(data.position);
if (!WithinBounds())
{
return;
}
BackgroundCanvasGroup.transform.position = _newPosition;
SetNeutralPosition(_newPosition);
_knobTransform.position = _newPosition;
_initialZPosition = _newPosition.z;
}
/// <summary>
/// Override OnDrag to handle repositionable joystick with rotated camera
/// </summary>
public override void OnDrag(PointerEventData eventData)
{
OnDragEvent.Invoke();
_newTargetPosition = ConvertToWorld(eventData.position);
Vector3 localDelta = TransformToLocalSpace(_newTargetPosition - _neutralPosition);
localDelta = Vector2.ClampMagnitude(localDelta, ComputedMaxRange);
if (!HorizontalAxisEnabled)
{
localDelta.x = 0;
}
if (!VerticalAxisEnabled)
{
localDelta.y = 0;
}
RawValue.x = EvaluateInputValue(localDelta.x);
RawValue.y = EvaluateInputValue(localDelta.y);
_newTargetPosition = _neutralPosition + TransformToWorldSpace(localDelta);
_newJoystickPosition = _newTargetPosition;
_newJoystickPosition.z = _initialZPosition;
_knobTransform.position = _newJoystickPosition;
}
/// <summary>
/// Returns true if the joystick's new position is within the bounds of the top level canvas
/// </summary>
/// <returns></returns>
protected virtual bool WithinBounds()
{
if (!ConstrainToInitialRectangle)
{
return true;
}
Vector2 screenPoint = _newPosition;
if (ParentCanvasRenderMode == RenderMode.ScreenSpaceCamera && TargetCamera != null)
{
screenPoint = TargetCamera.WorldToScreenPoint(_newPosition);
}
return RectTransformUtility.RectangleContainsScreenPoint(_rectTransform, screenPoint, TargetCamera);
}
/// <summary>
/// When the player lets go of the stick, we restore our stick's position if needed
/// </summary>
/// <param name="eventData">Event data.</param>
public override void OnPointerUp(PointerEventData eventData)
{
base.OnPointerUp(eventData);
if (ResetPositionToInitialOnRelease)
{
BackgroundCanvasGroup.transform.position = _initialPosition;
_knobTransform.position = _initialPosition;
}
}
#if UNITY_EDITOR
/// <summary>
/// Draws gizmos if needed
/// </summary>
protected override void OnDrawGizmos()
{
if (!DrawGizmos)
{
return;
}
Handles.color = MMColors.Orange;
if (!Application.isPlaying)
{
if (KnobCanvasGroup != null)
{
Handles.DrawWireDisc(KnobCanvasGroup.transform.position, this.transform.forward, ComputedMaxRange);
}
else
{
Handles.DrawWireDisc(this.transform.position, this.transform.forward, ComputedMaxRange);
}
}
else
{
Handles.DrawWireDisc(_neutralPosition, this.transform.forward, ComputedMaxRange);
}
}
#endif
}
}
#endif

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 1a60ed48e41f7574f890b8669d6fbfe0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMControls/MMTouchRepositionableJoystick.cs
uploadId: 830868

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 59c6e3abd829d7a458ad398f8dbf0a16
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,364 @@
using UnityEngine;
using MoreMountains.Feedbacks;
#if UNITY_EDITOR
using UnityEditor;
#endif
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
using UnityEngine.InputSystem;
#endif
#if MM_UI
namespace MoreMountains.Tools
{
/// <summary>
/// Add this class to a camera and you'll be able to pilot it using the horizontal/vertical axis, and up/down controls set via its inspector.
/// It's got an activation button, a run button, and an option to slow down time (this will require a MMTimeManager present in the scene)
/// </summary>
[AddComponentMenu("More Mountains/Tools/Camera/MM Ghost Camera")]
public class MMGhostCamera : MonoBehaviour
{
[Header("Speed")]
/// the camera's movement speed
public float MovementSpeed = 10f;
/// the factor by which to multiply the speed when "running"
public float RunFactor = 4f;
/// the movement's acceleration
public float Acceleration = 5f;
/// the movement's deceleration
public float Deceleration = 5f;
/// the speed at which the camera rotates
public float RotationSpeed = 40f;
[Header("Controls")]
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
public InputAction HorizontalAction;
public InputAction VerticalAction;
public InputAction MousePositionAction;
/// the button used to toggle the camera on/off
public Key ActivateKey = Key.LeftShift;
/// the button to use to go up
public Key UpKey = Key.Space;
/// the button to use to go down
public Key DownKey = Key.C;
/// the button to use to switch between mobile and desktop control mode
public Key ControlsModeSwitchKey = Key.M;
/// the button used to modify the timescale
public Key TimescaleModificationKey = Key.F;
/// the button used to run while it's pressed
public Key RunKey = Key.RightShift;
#else
/// the button used to toggle the camera on/off
public KeyCode ActivateButton = KeyCode.LeftShift;
/// the name of the InputManager's horizontal axis
public string HorizontalAxisName = "Horizontal";
/// the name of the InputManager's vertical axis
public string VerticalAxisName = "Vertical";
/// the button to use to go up
public KeyCode UpButton = KeyCode.Space;
/// the button to use to go down
public KeyCode DownButton = KeyCode.C;
/// the button to use to switch between mobile and desktop control mode
public KeyCode ControlsModeSwitch = KeyCode.M;
/// the button used to modify the timescale
public KeyCode TimescaleModificationButton = KeyCode.F;
/// the button used to run while it's pressed
public KeyCode RunButton = KeyCode.RightShift;
#endif
[Header("Mouse")]
/// the mouse's sensitivity
public float MouseSensitivity = 0.02f;
/// the right stick sensitivity
public float MobileStickSensitivity = 2f;
[Header("Timescale Modification")]
/// the amount to modify the timescale by when pressing the timescale button
public float TimescaleModifier = 0.5f;
[Header("Settings")]
/// whether or not this camera should activate on start
public bool AutoActivation = true;
/// whether or not movement (up/down/left/right/forward/backward) is enabled
public bool MovementEnabled = true;
// whether or not rotation is enabled
public bool RotationEnabled = true;
[MMReadOnly]
/// whether this camera is active or not right now
public bool Active = false;
[MMReadOnly]
/// whether time is being altered right now or not
public bool TimeAltered = false;
[Header("Virtual Joysticks")]
public bool UseMobileControls;
[MMCondition("UseMobileControls", true)]
public GameObject LeftStickContainer;
[MMCondition("UseMobileControls", true)]
public GameObject RightStickContainer;
[MMCondition("UseMobileControls", true)]
public MMTouchJoystick LeftStick;
[MMCondition("UseMobileControls", true)]
public MMTouchJoystick RightStick;
protected Vector3 _currentInput;
protected Vector3 _lerpedInput;
protected Vector3 _normalizedInput;
protected float _acceleration;
protected float _deceleration;
protected Vector3 _movementVector = Vector3.zero;
protected float _speedMultiplier;
protected Vector3 _newEulerAngles;
protected Vector2 _mouseInput;
/// <summary>
/// On start, activate our camera if needed
/// </summary>
protected virtual void Start()
{
if (AutoActivation)
{
ToggleFreeCamera();
}
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
HorizontalAction.Enable();
VerticalAction.Enable();
MousePositionAction.Enable();
HorizontalAction.performed += context => _currentInput.x = context.ReadValue<float>();
VerticalAction.performed += context => _currentInput.z = context.ReadValue<float>();
MousePositionAction.performed += context => _mouseInput = context.ReadValue<Vector2>();
HorizontalAction.canceled += context => _currentInput.x = 0f;
VerticalAction.canceled += context => _currentInput.z = 0f;
MousePositionAction.canceled += context => _mouseInput = Vector2.zero;
#endif
}
/// <summary>
/// On Update we grab our input and move accordingly
/// </summary>
protected virtual void Update()
{
bool activateButtonInput = false;
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
activateButtonInput = Keyboard.current[ActivateKey].wasPressedThisFrame;
#else
activateButtonInput = Input.GetKeyDown(ActivateButton);
#endif
if (activateButtonInput)
{
ToggleFreeCamera();
}
if (!Active)
{
return;
}
GetInput();
Translate();
Rotate();
Move();
HandleMobileControls();
}
/// <summary>
/// Grabs and stores the various input values
/// </summary>
protected virtual void GetInput()
{
if (!UseMobileControls || (LeftStick == null))
{
#if !ENABLE_INPUT_SYSTEM || ENABLE_LEGACY_INPUT_MANAGER
_currentInput.x = Input.GetAxis("Horizontal");
_currentInput.y = 0f;
_currentInput.z = Input.GetAxis("Vertical");
_mouseInput.x = Input.GetAxis("Mouse X");
_mouseInput.y = Input.GetAxis("Mouse Y");
#endif
}
else
{
_currentInput.x = LeftStick.RawValue.x;
_currentInput.y = 0f;
_currentInput.z = LeftStick.RawValue.y;
}
bool upButton = false;
bool downButton = false;
bool runButton = false;
bool timeScaleButton = false;
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
upButton = Keyboard.current[UpKey].isPressed;
downButton = Keyboard.current[DownKey].isPressed;
runButton = Keyboard.current[RunKey].isPressed;
timeScaleButton = Keyboard.current[TimescaleModificationKey].wasPressedThisFrame;
#else
upButton = Input.GetKey(UpButton);
downButton = Input.GetKey(DownButton);
runButton = Input.GetKey(RunButton);
timeScaleButton = Input.GetKeyDown(TimescaleModificationButton);
#endif
_currentInput.y = 0f;
if (upButton)
{
_currentInput.y = 1f;
}
if (downButton)
{
_currentInput.y = -1f;
}
_speedMultiplier = runButton ? RunFactor : 1f;
_normalizedInput = _currentInput.normalized;
if (timeScaleButton)
{
ToggleSlowMotion();
}
}
/// <summary>
/// Turns controls to mobile if needed
/// </summary>
protected virtual void HandleMobileControls()
{
bool mobileSwitch = false;
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
mobileSwitch = Keyboard.current[ControlsModeSwitchKey].wasPressedThisFrame;
#else
mobileSwitch = Input.GetKeyDown(ControlsModeSwitch);
#endif
if (mobileSwitch)
{
UseMobileControls = !UseMobileControls;
}
if (UseMobileControls)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else if (Active)
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
if (LeftStickContainer != null)
{
LeftStickContainer?.SetActive(UseMobileControls);
}
if (RightStickContainer != null)
{
RightStickContainer?.SetActive(UseMobileControls);
}
}
/// <summary>
/// Computes the new position
/// </summary>
protected virtual void Translate()
{
if (!MovementEnabled)
{
return;
}
if ((Acceleration == 0) || (Deceleration == 0))
{
_lerpedInput = _currentInput;
}
else
{
if (_normalizedInput.magnitude == 0)
{
_acceleration = Mathf.Lerp(_acceleration, 0f, Deceleration * Time.deltaTime);
_lerpedInput = Vector3.Lerp(_lerpedInput, _lerpedInput * _acceleration, Time.deltaTime * Deceleration);
}
else
{
_acceleration = Mathf.Lerp(_acceleration, 1f, Acceleration * Time.deltaTime);
_lerpedInput = Vector3.ClampMagnitude(_normalizedInput, _acceleration);
}
}
_movementVector = _lerpedInput;
_movementVector *= MovementSpeed * _speedMultiplier;
if (_movementVector.magnitude > MovementSpeed * _speedMultiplier)
{
_movementVector = Vector3.ClampMagnitude(_movementVector, MovementSpeed * _speedMultiplier);
}
}
/// <summary>
/// Computes the new rotation
/// </summary>
protected virtual void Rotate()
{
if (!RotationEnabled)
{
return;
}
_newEulerAngles = this.transform.eulerAngles;
if (!UseMobileControls || (LeftStick == null))
{
_newEulerAngles.x += -_mouseInput.y * 359f * MouseSensitivity;
_newEulerAngles.y += _mouseInput.x * 359f * MouseSensitivity;
}
else
{
_newEulerAngles.x += -RightStick.RawValue.y * MobileStickSensitivity;
_newEulerAngles.y += RightStick.RawValue.x * MobileStickSensitivity;
}
_newEulerAngles = Vector3.Lerp(this.transform.eulerAngles, _newEulerAngles, Time.deltaTime * RotationSpeed);
}
/// <summary>
/// Modifies the camera's transform's position and rotation
/// </summary>
protected virtual void Move()
{
transform.eulerAngles = _newEulerAngles;
transform.position += transform.rotation * _movementVector * Time.deltaTime;
}
/// <summary>
/// Toggles the timescale modification
/// </summary>
protected virtual void ToggleSlowMotion()
{
TimeAltered = !TimeAltered;
if (TimeAltered)
{
MMTimeScaleEvent.Trigger(MMTimeScaleMethods.For, TimescaleModifier, 1f, true, 5f, true);
}
else
{
MMTimeScaleEvent.Trigger(MMTimeScaleMethods.Unfreeze, 1f, 0f, false, 0f, false);
}
}
/// <summary>
/// Toggles the camera's active state
/// </summary>
protected virtual void ToggleFreeCamera()
{
Active = !Active;
Cursor.lockState = Active ? CursorLockMode.Locked : CursorLockMode.None;
Cursor.visible = !Active;
}
}
}
#endif

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d179f1ca35fc44c4fb760f847e59aa88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMGhostCamera/MMGhostCamera.cs
uploadId: 830868

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5d848dd9bb4718e4bbcb3a54d61e6cab
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,47 @@
using System.Collections;
using System.Collections.Generic;
using MoreMountains.Tools;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// A class defining the contents of a MMLootTable
/// </summary>
/// <typeparam name="T"></typeparam>
public class MMLoot<T>
{
/// the object to return
public T Loot;
/// the weight attributed to this specific object in the table
public float Weight = 1f;
/// the chance percentage to display for this object to be looted. ChancePercentages are meant to be computed by the MMLootTable class
[MMReadOnly]
public float ChancePercentage;
/// the computed low bound of this object's range
public virtual float RangeFrom { get; set; }
/// the computed high bound of this object's range
public virtual float RangeTo { get; set; }
}
/// <summary>
/// a MMLoot implementation for gameobjects
/// </summary>
[System.Serializable]
public class MMLootGameObject : MMLoot<GameObject> { }
/// <summary>
/// a MMLoot implementation for strings
/// </summary>
[System.Serializable]
public class MMLootString : MMLoot<string> { }
/// <summary>
/// a MMLoot implementation for floats
/// </summary>
[System.Serializable]
public class MMLootFloat : MMLoot<float> { }
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ae83134bbe174934c889c5969a608e7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMLoot/MMLoot.cs
uploadId: 830868

View File

@@ -0,0 +1,126 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MoreMountains.Tools
{
/// <summary>
/// A loot table helper that can be used to randomly pick objects out of a weighted list
/// This design pattern was described in more details by Daniel Cook in 2014 in his blog :
/// https://lostgarden.home.blog/2014/12/08/loot-drop-tables/
///
/// This generic LootTable defines a list of objects to loot, each of them weighted.
/// The weights don't have to add to a certain number, they're relative to each other.
/// The ComputeWeights method determines, based on these weights, the chance percentage of each object to be picked
/// The GetLoot method returns one object, picked randomly from the table
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="V"></typeparam>
public class MMLootTable<T,V> where T:MMLoot<V>
{
/// the list of objects that have a chance of being returned by the table
[SerializeField]
public List<T> ObjectsToLoot;
/// the total amount of weights, for debug purposes only
[Header("Debug")]
[MMReadOnly]
public float WeightsTotal;
protected float _maximumWeightSoFar = 0f;
protected bool _weightsComputed = false;
/// <summary>
/// Determines, for each object in the table, its chance percentage, based on the specified weights
/// </summary>
public virtual void ComputeWeights()
{
if (ObjectsToLoot == null)
{
return;
}
if (ObjectsToLoot.Count == 0)
{
return;
}
_maximumWeightSoFar = 0f;
foreach(T lootDropItem in ObjectsToLoot)
{
if(lootDropItem.Weight >= 0f)
{
lootDropItem.RangeFrom = _maximumWeightSoFar;
_maximumWeightSoFar += lootDropItem.Weight;
lootDropItem.RangeTo = _maximumWeightSoFar;
}
else
{
lootDropItem.Weight = 0f;
}
}
WeightsTotal = _maximumWeightSoFar;
foreach(T lootDropItem in ObjectsToLoot)
{
lootDropItem.ChancePercentage = ((lootDropItem.Weight) / WeightsTotal) * 100;
}
_weightsComputed = true;
}
/// <summary>
/// Returns one object from the table, picked randomly
/// </summary>
/// <returns></returns>
public virtual T GetLoot()
{
if (ObjectsToLoot == null)
{
return null;
}
if (ObjectsToLoot.Count == 0)
{
return null;
}
if (!_weightsComputed)
{
ComputeWeights();
}
float index = Random.Range(0, WeightsTotal);
foreach (T lootDropItem in ObjectsToLoot)
{
if ((index > lootDropItem.RangeFrom) && (index < lootDropItem.RangeTo))
{
return lootDropItem;
}
}
return null;
}
}
/// <summary>
/// A MMLootTable implementation for GameObjects
/// </summary>
[System.Serializable]
public class MMLootTableGameObject : MMLootTable<MMLootGameObject, GameObject> { }
/// <summary>
/// A MMLootTable implementation for floats
/// </summary>
[System.Serializable]
public class MMLootTableFloat : MMLootTable<MMLootFloat, float> { }
/// <summary>
/// A MMLootTable implementation for strings
/// </summary>
[System.Serializable]
public class MMLootTableString : MMLootTable<MMLootString, string> { }
}

View File

@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 5e6301cb44b6ff94f98077ceb3446fe2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 183370
packageName: Feel
packageVersion: 5.9.1
assetPath: Assets/Feel/MMTools/Foundation/MMLoot/MMLootTable.cs
uploadId: 830868

Some files were not shown because too many files have changed in this diff Show More