This commit is contained in:
2026-06-14 23:57:44 +07:00
parent 20f9010787
commit 78d7b2f5a7
5775 changed files with 4796241 additions and 5 deletions

View File

@@ -0,0 +1,213 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
#if NET_4_6 || UNITY_2018_3_OR_NEWER || UNITY_WEBGL || UNITY_IOS || UNITY_ANDROID || UNITY_WII || UNITY_WIIU || UNITY_SWITCH || UNITY_PS3 || UNITY_PS4 || UNITY_XBOXONE || UNITY_WSA
namespace Opsive.UltimateCharacterController.StateSystem
{
using System;
using UnityEngine;
// This class is required in order for the preset system to work with AOT platforms. The preset system uses reflection to generate the delegates
// and reflection doesn't play well with AOT because the classes need to be defined ahead of time. Define the classes here so the compiler will
// add in the correct type. This code is not actually used anywhere, it is purely for the compiler.
public class AOTLinker : MonoBehaviour
{
public void Linker()
{
#pragma warning disable 0219
var intGenericDelegate = new Preset.GenericDelegate<int>();
var intFuncDelegate = new Func<int>(() => { return 0; });
var intActionDelegate = new Action<int>((int value) => { });
var floatGenericDelegate = new Preset.GenericDelegate<float>();
var floatFuncDelegate = new Func<float>(() => { return 0; });
var floatActionDelegate = new Action<float>((float value) => { });
var uintGenericDelegate = new Preset.GenericDelegate<uint>();
var uintFuncDelegate = new Func<uint>(() => { return 0; });
var uintActionDelegate = new Action<uint>((uint value) => { });
var doubleGenericDelegate = new Preset.GenericDelegate<double>();
var doubleFuncDelegate = new Func<double>(() => { return 0; });
var doubleActionDelegate = new Action<double>((double value) => { });
var longGenericDelegate = new Preset.GenericDelegate<long>();
var longFuncDelegate = new Func<long>(() => { return 0; });
var longActionDelegate = new Action<long>((long value) => { });
var boolGenericDelegate = new Preset.GenericDelegate<bool>();
var boolFuncDelegate = new Func<bool>(() => { return true; });
var boolActionDelegate = new Action<bool>((bool value) => { });
var stringGenericDelegate = new Preset.GenericDelegate<string>();
var stringFuncDelegate = new Func<string>(() => { return string.Empty; });
var stringActionDelegate = new Action<string>((string value) => { });
var byteGenericDelegate = new Preset.GenericDelegate<byte>();
var byteFuncDelegate = new Func<byte>(() => { return new byte(); });
var byteActionDelegate = new Action<byte>((byte value) => { });
var vector2GenericDelegate = new Preset.GenericDelegate<Vector2>();
var vector2FuncDelegate = new Func<Vector2>(() => { return Vector2.zero; });
var vector2ActionDelegate = new Action<Vector2>((Vector2 value) => { });
var vector3GenericDelegate = new Preset.GenericDelegate<Vector3>();
var vector3FuncDelegate = new Func<Vector3>(() => { return Vector3.zero; });
var vector3ActionDelegate = new Action<Vector3>((Vector3 value) => { });
var vector4GenericDelegate = new Preset.GenericDelegate<Vector4>();
var vector4FuncDelegate = new Func<Vector4>(() => { return Vector4.zero; });
var vector4ActionDelegate = new Action<Vector4>((Vector4 value) => { });
var quaternionGenericDelegate = new Preset.GenericDelegate<Quaternion>();
var quaternionFuncDelegate = new Func<Quaternion>(() => { return Quaternion.identity; });
var quaternionActionDelegate = new Action<Quaternion>((Quaternion value) => { });
var colorGenericDelegate = new Preset.GenericDelegate<Color>();
var colorFuncDelegate = new Func<Color>(() => { return Color.white; });
var colorActionDelegate = new Action<Color>((Color value) => { });
var rectGenericDelegate = new Preset.GenericDelegate<Rect>();
var rectFuncDelegate = new Func<Rect>(() => { return Rect.zero; });
var rectActionDelegate = new Action<Rect>((Rect value) => { });
var matrix4x4GenericDelegate = new Preset.GenericDelegate<Matrix4x4>();
var matrix4x4FuncDelegate = new Func<Matrix4x4>(() => { return Matrix4x4.zero; });
var matrix4x4ActionDelegate = new Action<Matrix4x4>((Matrix4x4 value) => { });
var animationCurveGenericDelegate = new Preset.GenericDelegate<AnimationCurve>();
var animationCurveFuncDelegate = new Func<AnimationCurve>(() => { return new AnimationCurve(); });
var animationCurveActionDelegate = new Action<AnimationCurve>((AnimationCurve value) => { });
var layerMaskGenericDelegate = new Preset.GenericDelegate<LayerMask>();
var layerMaskFuncDelegate = new Func<LayerMask>(() => { return new LayerMask(); });
var layerMaskActionDelegate = new Action<LayerMask>((LayerMask value) => { });
var humanBodyBonesGenericDelegate = new Preset.GenericDelegate<HumanBodyBones>();
var humanBodyBonesFuncDelegate = new Func<HumanBodyBones>(() => { return 0; });
var humanBodyBonesActionDelegate = new Action<HumanBodyBones>((HumanBodyBones value) => { });
var queryTriggerInteractionGenericDelegate = new Preset.GenericDelegate<QueryTriggerInteraction>();
var queryTriggerInteractionFuncDelegate = new Func<QueryTriggerInteraction>(() => { return 0; });
var queryTriggerInteractionActionDelegate = new Action<QueryTriggerInteraction>((QueryTriggerInteraction value) => { });
var forceModeGenericDelegate = new Preset.GenericDelegate<ForceMode>();
var forceModeFuncDelegate = new Func<ForceMode>(() => { return 0; });
var forceModeActionDelegate = new Action<ForceMode>((ForceMode value) => { });
var unityObjectGenericDelegate = new Preset.GenericDelegate<UnityEngine.Object>();
var unityObjectFuncDelegate = new Func<UnityEngine.Object>(() => { return new UnityEngine.Object(); });
var unityObjectActionDelegate = new Action<UnityEngine.Object>((UnityEngine.Object value) => { });
var gameObjectGenericDelegate = new Preset.GenericDelegate<GameObject>();
var gameObjectFuncDelegate = new Func<GameObject>(() => { return null; });
var gameObjectActionDelegate = new Action<GameObject>((GameObject value) => { });
var transformGenericDelegate = new Preset.GenericDelegate<Transform>();
var transformFuncDelegate = new Func<Transform>(() => { return null; });
var transformActionDelegate = new Action<Transform>((Transform value) => { });
var minMaxFloatGenericDelegate = new Preset.GenericDelegate<Utility.MinMaxFloat>();
var minMaxFloatFuncDelegate = new Func<Utility.MinMaxFloat>(() => { return new Utility.MinMaxFloat(); });
var minMaxFloatActionDelegate = new Action<Utility.MinMaxFloat>((Utility.MinMaxFloat value) => { });
var minMaxVector3GenericDelegate = new Preset.GenericDelegate<Utility.MinMaxVector3>();
var minMaxVector3FuncDelegate = new Func<Utility.MinMaxVector3>(() => { return new Utility.MinMaxVector3(); });
var minMaxVector3ActionDelegate = new Action<Utility.MinMaxVector3>((Utility.MinMaxVector3 value) => { });
var lookVectorModeGenericDelegate = new Preset.GenericDelegate<UltimateCharacterController.Input.PlayerInput.LookVectorMode>();
var lookVectorModeFuncDelegate = new Func<UltimateCharacterController.Input.PlayerInput.LookVectorMode>(() => { return 0; });
var lookVectorModeActionDelegate = new Action<UltimateCharacterController.Input.PlayerInput.LookVectorMode>((UltimateCharacterController.Input.PlayerInput.LookVectorMode value) => { });
var preloadedPrefabGenericDelegate = new Preset.GenericDelegate<Shared.Game.ObjectPool.PreloadedPrefab>();
var preloadedPrefabFuncDelegate = new Func<Shared.Game.ObjectPool.PreloadedPrefab>(() => { return new Shared.Game.ObjectPool.PreloadedPrefab(); });
var preloadedPrefabActionDelegate = new Action<Shared.Game.ObjectPool.PreloadedPrefab>((Shared.Game.ObjectPool.PreloadedPrefab value) => { });
var abilityStartTypeGenericDelegate = new Preset.GenericDelegate<Character.Abilities.Ability.AbilityStartType>();
var abilityStartTypeFuncDelegate = new Func<Character.Abilities.Ability.AbilityStartType>(() => { return 0; });
var abilityStartTypeActionDelegate = new Action<Character.Abilities.Ability.AbilityStartType>((Character.Abilities.Ability.AbilityStartType value) => { });
var abilityStopTypeGenericDelegate = new Preset.GenericDelegate<Character.Abilities.Ability.AbilityStopType>();
var abilityStopTypeFuncDelegate = new Func<Character.Abilities.Ability.AbilityStopType>(() => { return 0; });
var abilityStopTypeActionDelegate = new Action<Character.Abilities.Ability.AbilityStopType>((Character.Abilities.Ability.AbilityStopType value) => { });
var abilityBoolOverrideGenericDelegate = new Preset.GenericDelegate<Character.Abilities.Ability.AbilityBoolOverride>();
var abilityBoolOverrideFuncDelegate = new Func<Character.Abilities.Ability.AbilityBoolOverride>(() => { return 0; });
var abilityBoolOverrideActionDelegate = new Action<Character.Abilities.Ability.AbilityBoolOverride>((Character.Abilities.Ability.AbilityBoolOverride value) => { });
var comboInputElementGenericDelegate = new Preset.GenericDelegate<Character.Abilities.Starters.ComboTimeout.ComboInputElement>();
var comboInputElementFuncDelegate = new Func<Character.Abilities.Starters.ComboTimeout.ComboInputElement>(() => { return new Character.Abilities.Starters.ComboTimeout.ComboInputElement(); });
var comboInputElementActionDelegate = new Action<Character.Abilities.Starters.ComboTimeout.ComboInputElement>((Character.Abilities.Starters.ComboTimeout.ComboInputElement value) => { });
var restrictionTypeGenericDelegate = new Preset.GenericDelegate<Character.Abilities.RestrictPosition.RestrictionType>();
var restrictionTypeFuncDelegate = new Func<Character.Abilities.RestrictPosition.RestrictionType>(() => { return 0; });
var restrictionTypeActionDelegate = new Action<Character.Abilities.RestrictPosition.RestrictionType>((Character.Abilities.RestrictPosition.RestrictionType value) => { });
var objectDetectionModeGenericDelegate = new Preset.GenericDelegate<Character.Abilities.DetectObjectAbilityBase.ObjectDetectionMode>();
var objectDetectionModeTypeFuncDelegate = new Func<Character.Abilities.DetectObjectAbilityBase.ObjectDetectionMode>(() => { return 0; });
var objectDetectionModeTypeActionDelegate = new Action<Character.Abilities.DetectObjectAbilityBase.ObjectDetectionMode>((Character.Abilities.DetectObjectAbilityBase.ObjectDetectionMode value) => { });
var autoEquipTypeGenericDelegate = new Preset.GenericDelegate<Character.Abilities.Items.EquipUnequip.AutoEquipType>();
var autoEquipTypeFuncDelegate = new Func<Character.Abilities.Items.EquipUnequip.AutoEquipType>(() => { return 0; });
var autoEquipTypeActionDelegate = new Action<Character.Abilities.Items.EquipUnequip.AutoEquipType>((Character.Abilities.Items.EquipUnequip.AutoEquipType value) => { });
var shakeTargetGenericDelegate = new Preset.GenericDelegate<Character.Effects.Shake.ShakeTarget>();
var shakeTargetFuncDelegate = new Func<Character.Effects.Shake.ShakeTarget>(() => { return 0; });
var shakeTargetActionDelegate = new Action<Character.Effects.Shake.ShakeTarget>((Character.Effects.Shake.ShakeTarget value) => { });
var attributeAutoUpdateValueTypeGenericDelegate = new Preset.GenericDelegate<Traits.Attribute.AutoUpdateValue>();
var attributeAutoUpdateFuncDelegate = new Func<Traits.Attribute.AutoUpdateValue>(() => { return 0; });
var attributeAutoUpdateActionDelegate = new Action<Traits.Attribute.AutoUpdateValue>((Traits.Attribute.AutoUpdateValue value) => { });
var surfaceImpactGenericDelegate = new Preset.GenericDelegate<SurfaceSystem.SurfaceImpact>();
var surfaceImpactFuncDelegate = new Func<SurfaceSystem.SurfaceImpact>(() => { return null; });
var surfaceImpactActionDelegate = new Action<SurfaceSystem.SurfaceImpact>((SurfaceSystem.SurfaceImpact value) => { });
var uvTextureGenericDelegate = new Preset.GenericDelegate<SurfaceSystem.UVTexture>();
var uvTextureFuncDelegate = new Func<SurfaceSystem.UVTexture>(() => { return new SurfaceSystem.UVTexture(); });
var uvTextureActionDelegate = new Action<SurfaceSystem.UVTexture>((SurfaceSystem.UVTexture value) => { });
var objectSurfaceGenericDelegate = new Preset.GenericDelegate<SurfaceSystem.ObjectSurface>();
var objectSurfaceFuncDelegate = new Func<SurfaceSystem.ObjectSurface>(() => { return new SurfaceSystem.ObjectSurface(); });
var objectSurfaceActionDelegate = new Action<SurfaceSystem.ObjectSurface>((SurfaceSystem.ObjectSurface value) => { });
var objectSpawnInfoGenericDelegate = new Preset.GenericDelegate<Utility.ObjectSpawnInfo>();
var objectSpawnInfoFuncDelegate = new Func<Utility.ObjectSpawnInfo>(() => { return null; });
var objectSpawnInfoActionDelegate = new Action<Utility.ObjectSpawnInfo>((Utility.ObjectSpawnInfo value) => { });
var animationEventTriggerGenericDelegate = new Preset.GenericDelegate<Utility.AnimationEventTrigger>();
var animationEventTriggerFuncDelegate = new Func<Utility.AnimationEventTrigger>(() => { return null; });
var animationEventTriggerActionDelegate = new Action<Utility.AnimationEventTrigger>((Utility.AnimationEventTrigger value) => { });
var characterFootEffectsFootGenericDelegate = new Preset.GenericDelegate<Character.CharacterFootEffects.Foot>();
var characterFootEffectsFootFuncDelegate = new Func<Character.CharacterFootEffects.Foot>(() => { return new Character.CharacterFootEffects.Foot(); });
var characterFootEffectsFootActionDelegate = new Action<Character.CharacterFootEffects.Foot>((Character.CharacterFootEffects.Foot value) => { });
var characterFootEffectsFootstepPlacementModeGenericDelegate = new Preset.GenericDelegate<Character.CharacterFootEffects.FootstepPlacementMode>();
var characterFootEffectsFootstepPlacementModeFuncDelegate = new Func<Character.CharacterFootEffects.FootstepPlacementMode>(() => { return 0; });
var characterFootEffectsFootstepPlacementModeActionDelegate = new Action<Character.CharacterFootEffects.FootstepPlacementMode>((Character.CharacterFootEffects.FootstepPlacementMode value) => { });
var spawnPointSpawnShapeGenericDelegate = new Preset.GenericDelegate<Game.SpawnPoint.SpawnShape>();
var spawnShapeFuncDelegate = new Func<Game.SpawnPoint.SpawnShape>(() => { return 0; });
var spawnShapeActionDelegate = new Action<Game.SpawnPoint.SpawnShape>((Game.SpawnPoint.SpawnShape value) => { });
var respawnerSpawnPositioningModeGenericDelegate = new Preset.GenericDelegate<Traits.Respawner.SpawnPositioningMode>();
var respawnerSpawnPositioningFuncDelegate = new Func<Traits.Respawner.SpawnPositioningMode>(() => { return 0; });
var respawnerSpawnPositioningActionDelegate = new Action<Traits.Respawner.SpawnPositioningMode>((Traits.Respawner.SpawnPositioningMode value) => { });
var movingPlatformWaypointGenericDelegate = new Preset.GenericDelegate<Objects.MovingPlatform.Waypoint>();
var movingPlatformWaypointFuncDelegate = new Func<Objects.MovingPlatform.Waypoint>(() => { return new Objects.MovingPlatform.Waypoint(); });
var movingPlatformWaypointActionDelegate = new Action<Objects.MovingPlatform.Waypoint>((Objects.MovingPlatform.Waypoint value) => { });
var movingPlatformPathMovementTypeGenericDelegate = new Preset.GenericDelegate<Objects.MovingPlatform.PathMovementType>();
var movingPlatformPathMovementTypeFuncDelegate = new Func<Objects.MovingPlatform.PathMovementType> (() => { return 0; });
var movingPlatformPathMovementTypeActionDelegate = new Action<Objects.MovingPlatform.PathMovementType>((Objects.MovingPlatform.PathMovementType value) => { });
var movingPlatformPathDirectionGenericDelegate = new Preset.GenericDelegate<Objects.MovingPlatform.PathDirection>();
var movingPlatformPathDirectionFuncDelegate = new Func<Objects.MovingPlatform.PathDirection>(() => { return 0; });
var movingPlatformPathDirectionActionDelegate = new Action<Objects.MovingPlatform.PathDirection>((Objects.MovingPlatform.PathDirection value) => { });
var movingPlatformMovementInterpolationModeGenericDelegate = new Preset.GenericDelegate<Objects.MovingPlatform.MovementInterpolationMode>();
var movingPlatformMovementInterpolationModeFuncDelegate = new Func<Objects.MovingPlatform.MovementInterpolationMode>(() => { return 0; });
var movingPlatformMovementInterpolationModeActionDelegate = new Action<Objects.MovingPlatform.MovementInterpolationMode>((Objects.MovingPlatform.MovementInterpolationMode value) => { });
var movingPlatformRotateInterpolationModeGenericDelegate = new Preset.GenericDelegate<Objects.MovingPlatform.RotateInterpolationMode>();
var movingPlatformRotateInterpolationModeFuncDelegate = new Func<Objects.MovingPlatform.RotateInterpolationMode>(() => { return 0; });
var movingPlatformRotateInterpolationModeActionDelegate = new Action<Objects.MovingPlatform.RotateInterpolationMode>((Objects.MovingPlatform.RotateInterpolationMode value) => { });
var audioClipSetGenericDelegate = new Preset.GenericDelegate<Audio.AudioClipSet>();
var audioClipSetFuncDelegate = new Func<Audio.AudioClipSet>(() => { return null; });
var audioClipSetActionDelegate = new Action<Audio.AudioClipSet>((Audio.AudioClipSet value) => { });
#if ULTIMATE_CHARACTER_CONTROLLER_SHOOTER
var autoReloadTypeGenericDelegate = new Preset.GenericDelegate<Character.Abilities.Items.Reload.AutoReloadType>();
var autoReloadTypeFuncDelegate = new Func<Character.Abilities.Items.Reload.AutoReloadType>(() => { return 0; });
var autoReloadTypeModeActionDelegate = new Action<Character.Abilities.Items.Reload.AutoReloadType>((Character.Abilities.Items.Reload.AutoReloadType value) => { });
var shootableWeaponFireModeGenericDelegate = new Preset.GenericDelegate<Items.Actions.ShootableWeapon.FireMode>();
var shootableWeaponFireModeFuncDelegate = new Func<Items.Actions.ShootableWeapon.FireMode>(() => { return 0; });
var shootableWeaponFireModeActionDelegate = new Action<Items.Actions.ShootableWeapon.FireMode>((Items.Actions.ShootableWeapon.FireMode value) => { });
var shootableWeaponFireTypeGenericDelegate = new Preset.GenericDelegate<Items.Actions.ShootableWeapon.FireType>();
var shootableWeaponFireTypeFuncDelegate = new Func<Items.Actions.ShootableWeapon.FireType>(() => { return 0; });
var shootableWeaponFireTypeActionDelegate = new Action<Items.Actions.ShootableWeapon.FireType>((Items.Actions.ShootableWeapon.FireType value) => { });
var shootableWeaponProjectileVisibilityGenericDelegate = new Preset.GenericDelegate<Items.Actions.ShootableWeapon.ProjectileVisiblityType>();
var shootableWeaponProjectileVisiblityTypeFuncDelegate = new Func<Items.Actions.ShootableWeapon.ProjectileVisiblityType>(() => { return 0; });
var shootableWeaponProjectileVisiblityTypeActionDelegate = new Action<Items.Actions.ShootableWeapon.ProjectileVisiblityType>((Items.Actions.ShootableWeapon.ProjectileVisiblityType value) => { });
var shootableWeaponReloadClipTypeGenericDelegate = new Preset.GenericDelegate<Items.Actions.ShootableWeapon.ReloadClipType>();
var shootableWeaponReloadClipTypeFuncDelegate = new Func<Items.Actions.ShootableWeapon.ReloadClipType>(() => { return 0; });
var shootableWeaponReloadClipTypeActionDelegate = new Action<Items.Actions.ShootableWeapon.ReloadClipType>((Items.Actions.ShootableWeapon.ReloadClipType value) => { });
#endif
#if ULTIMATE_CHARACTER_CONTROLLER_MELEE
var meleeWeaponTrailVisibilityGenericDelegate = new Preset.GenericDelegate<Items.Actions.MeleeWeapon.TrailVisibilityType>();
var meleeWeaponTrailVisibilityFuncDelegate = new Func<Items.Actions.MeleeWeapon.TrailVisibilityType>(() => { return 0; });
var meleeWeaponTrailVisibilityActionDelegate = new Action<Items.Actions.MeleeWeapon.TrailVisibilityType>((Items.Actions.MeleeWeapon.TrailVisibilityType value) => { });
#endif
var magicItemCastDirectionGenericDelegate = new Preset.GenericDelegate<Items.Actions.MagicItem.CastDirection>();
var magicItemCastDirectionFuncDelegate = new Func<Items.Actions.MagicItem.CastDirection>(() => { return 0; });
var magicItemCastDirectionActionDelegate = new Action<Items.Actions.MagicItem.CastDirection>((Items.Actions.MagicItem.CastDirection value) => { });
var magicItemCastUseTypeGenericDelegate = new Preset.GenericDelegate<Items.Actions.MagicItem.CastUseType>();
var magicItemCastUseTypeFuncDelegate = new Func<Items.Actions.MagicItem.CastUseType>(() => { return 0; });
var magicItemCastUseTypeActionDelegate = new Action<Items.Actions.MagicItem.CastUseType>((Items.Actions.MagicItem.CastUseType value) => { });
var magicItemCastInterruptSourceGenericDelegate = new Preset.GenericDelegate<Items.Actions.MagicItem.CastInterruptSource>();
var magicItemCastInterruptSourceFuncDelegate = new Func<Items.Actions.MagicItem.CastInterruptSource>(() => { return 0; });
var magicItemCastInterruptSourceActionDelegate = new Action<Items.Actions.MagicItem.CastInterruptSource>((Items.Actions.MagicItem.CastInterruptSource value) => { });
var healthFlashMonitorFlashGenericDelegate = new Preset.GenericDelegate<UI.HealthFlashMonitor.Flash>();
var healthFlashMonitorFlashFuncDelegate = new Func<UI.HealthFlashMonitor.Flash>(() => { return new UI.HealthFlashMonitor.Flash(); });
var healthFlashMonitorFlashActionDelegate = new Action<UI.HealthFlashMonitor.Flash>((UI.HealthFlashMonitor.Flash value) => { });
#pragma warning restore 0219
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 076144a8918352549981a20b956086ee
timeCreated: 1486426095
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,59 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using Opsive.Shared.Utility;
using System.Collections.Generic;
using System.Reflection;
/// <summary>
/// Used by the state system for the default preset. The default preset will only set the property value when there has been a change from another preset.
/// </summary>
public class DefaultPreset : Preset
{
private Dictionary<MethodInfo, int> m_DelegateIndexMap;
/// <summary>
/// Creates a default preset.
/// </summary>
/// <returns>The created preset.</returns>
public static DefaultPreset CreateDefaultPreset()
{
var preset = CreateInstance<DefaultPreset>();
preset.hideFlags = UnityEngine.HideFlags.HideAndDontSave;
return preset;
}
/// <summary>
/// Initializes the preset with the specified visiblity. The preset must be initialized before the preset values are applied so the delegates can be created.
/// </summary>
/// <param name="obj">The object to map the delegates to.</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
public override void Initialize(object obj, MemberVisibility visibility)
{
base.Initialize(obj, visibility);
m_DelegateIndexMap = new Dictionary<MethodInfo, int>();
for (int i = 0; i < m_Delegates.Length; ++i) {
m_DelegateIndexMap.Add(m_Delegates[i].SetMethod, i);
}
}
/// <summary>
/// Applies the values to the component specified by the delegates.
/// </summary>
/// <param name="delegates">The properties that were changed.</param>
public override void ApplyValues(BaseDelegate[] delegates)
{
// Only apply the properties that were changed. This is determined by the delegates array.
for (int i = 0; i < delegates.Length; ++i) {
var index = m_DelegateIndexMap[delegates[i].SetMethod];
m_Delegates[index].ApplyValue();
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: bf4578a7142c06d46b296d998c7c0b45
timeCreated: 1481960214
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e2150fcaaf9a0a34baf8e5554d64da37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,24 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
/// <summary>
/// Interface for any object which uses the StateSystem. Allows the StateManager to interact with a common object.
/// </summary>
public interface IStateOwner
{
/// <summary>
/// Callback when the StateManager will change the active state on the current object.
/// </summary>
void StateWillChange();
/// <summary>
/// Callback when the StateManager has changed the active state on the current object.
/// </summary>
void StateChange();
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 24863ed59b5907a4f9eb7f3c9850b9db
timeCreated: 1497724626
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,90 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using Opsive.Shared.Utility;
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Allows the Preset component to serialized the property value.
/// </summary>
public class PersistablePreset : Preset
{
[Tooltip("The serialized properties.")]
[SerializeField] protected Serialization m_Data;
public Serialization Data { get { return m_Data; } set { m_Data = value; } }
/// <summary>
/// Creates a persistable preset based off of the specified component.
/// </summary>
/// <param name="obj">The object to retrieve the property values of.</param>
/// <returns>The created preset. Null if no properties have been found to create the preset with.</returns>
public static PersistablePreset CreatePreset(object obj)
{
return CreatePreset(obj, MemberVisibility.None);
}
/// <summary>
/// Creates a persistable preset based off of the specified component and visibility.
/// </summary>
/// <param name="obj">The object to retrieve the property values of.</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
/// <returns>The created preset. Null if no properties have been found to create the preset with.</returns>
public static PersistablePreset CreatePreset(object obj, MemberVisibility visibility)
{
var data = new Serialization();
data.Serialize(obj, false, visibility);
var preset = CreateInstance<PersistablePreset>();
preset.Data = data;
return preset;
}
/// <summary>
/// Initializes the preset with the specified visiblity. The preset must be initialized before the preset values are applied so the delegates can be created.
/// </summary>
/// <param name="obj">The object to map the delegates to.</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
public override void Initialize(object obj, MemberVisibility visibility)
{
m_Delegates = new BaseDelegate[m_Data.ValueHashes.Length];
var valuePositionMap = new Dictionary<int, int>(m_Data.ValueHashes.Length);
for (int i = 0; i < m_Data.ValueHashes.Length; ++i) {
valuePositionMap.Add(m_Data.ValueHashes[i], i);
}
var valueCount = 0;
var properties = Serialization.GetSerializedProperties(obj.GetType(), visibility);
for (int i = 0; i < properties.Length; ++i) {
var hash = Serialization.StringHash(properties[i].PropertyType.FullName) + Serialization.StringHash(properties[i].Name);
int position;
if (!valuePositionMap.TryGetValue(hash, out position)) {
continue;
}
// Create a generic delegate based on the property type.
var genericDelegateType = typeof(GenericDelegate<>).MakeGenericType(properties[i].PropertyType);
m_Delegates[valueCount] = Activator.CreateInstance(genericDelegateType) as BaseDelegate;
// Initialize the delegate.
if (m_Delegates[valueCount] != null) {
m_Delegates[valueCount].Initialize(obj, properties[i], valuePositionMap, m_Data, visibility);
} else {
Debug.LogWarning("Warning: Unable to create preset of type " + properties[i].PropertyType);
}
valueCount++;
}
// The delegate length may not match if a property has been added but no longer exists.
if (m_Delegates.Length != valueCount) {
Array.Resize(ref m_Delegates, valueCount);
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 114f4adff47823d4aadb77159ef6731f
timeCreated: 1481960214
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e2150fcaaf9a0a34baf8e5554d64da37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,313 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using Opsive.Shared.Utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
/// <summary>
/// Represents a set of values for any number of component properties. In order for the value to be applied to a property a getter and setter must exist,
/// along with a derived class from BaseDelegate which creates the delegate which interfaces with the property getter and setter. Properties can be
/// ignored with the [Opsive.UltimateCharacterController.Opsive.Shared.Utility.NonSerialized] attribute.
/// </summary>
public class Preset : ScriptableObject
{
protected BaseDelegate[] m_Delegates;
public bool IsInitialized { get { return m_Delegates != null; } }
public BaseDelegate[] Delegates { get { return m_Delegates; } }
/// <summary>
/// Creates a preset based off of the specified component.
/// </summary>
/// <param name="obj">The object to retrieve the property values of.</param>
/// <returns>The created preset. Null if no properties have been found to create the preset with.</returns>
public static Preset CreatePreset()
{
var preset = CreateInstance<Preset>();
preset.hideFlags = HideFlags.HideAndDontSave;
return preset;
}
/// <summary>
/// Initializes the preset. The preset must be initialized before the preset values are applied so the delegates can be created.
/// </summary>
/// <param name="obj">The object to map the delegates to.</param>
public void Initialize(object obj)
{
Initialize(obj, MemberVisibility.Public);
}
/// <summary>
/// Initializes the preset with the specified visiblity. The preset must be initialized before the preset values are applied so the delegates can be created.
/// </summary>
/// <param name="obj">The object to map the delegates to.</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
public virtual void Initialize(object obj, MemberVisibility visibility)
{
var properties = Serialization.GetSerializedProperties(obj.GetType(), visibility);
var valueCount = 0;
m_Delegates = new BaseDelegate[properties.Length];
for (int i = 0; i < properties.Length; ++i) {
// The property may not be valid.
if (Serialization.GetValidGetMethod(properties[i], visibility) == null) {
continue;
}
// Create a generic delegate based on the property type.
var genericDelegateType = typeof(GenericDelegate<>).MakeGenericType(properties[i].PropertyType);
m_Delegates[valueCount] = Activator.CreateInstance(genericDelegateType) as BaseDelegate;
// Initialize the delegate.
if (m_Delegates[valueCount] != null) {
m_Delegates[valueCount].Initialize(obj, properties[i], visibility);
} else {
Debug.LogWarning("Warning: Unable to create preset of type " + properties[i].PropertyType);
}
valueCount++;
}
if (m_Delegates.Length != valueCount) {
Array.Resize(ref m_Delegates, valueCount);
}
}
/// <summary>
/// Updates the stored value with the current property value.
/// </summary>
public virtual void UpdateValue()
{
for (int i = 0; i < m_Delegates.Length; ++i) {
m_Delegates[i].UpdateValue();
}
}
/// <summary>
/// Applies the values to the component.
/// </summary>
public void ApplyValues()
{
for (int i = 0; i < m_Delegates.Length; ++i) {
m_Delegates[i].ApplyValue();
}
}
/// <summary>
/// Applies the values to the component specified by the delegates.
/// </summary>
/// <param name="delegates">The properties that were changed.</param>
public virtual void ApplyValues(BaseDelegate[] delegates) { }
/// <summary>
/// Abstract class which allows for a delegate to be created which can be called on when the preset value should be applied.
/// </summary>
[UnityEngine.Scripting.Preserve]
public abstract class BaseDelegate
{
public abstract MethodInfo SetMethod { get; }
/// <summary>
/// Initialize the delegate and value.
/// </summary>
/// <param name="obj">The object which the delegate operates on.</param>
/// <param name="property">The property that the delegate will invoke.</param>
/// <param name="valuePositionMap">A mapping between the value hash and position.</param>
/// <param name="data">The serialization data which contains the values for the property (as well as all other properties).</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
public abstract void Initialize(object obj, PropertyInfo property, Dictionary<int, int> valuePositionMap, Serialization data, MemberVisibility visibility);
/// <summary>
/// Initialize the delegate.
/// </summary>
/// <param name="obj">The object which the delegate operates on.</param>
/// <param name="property">The property that the delegate will invoke.</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
public abstract void Initialize(object obj, PropertyInfo property, MemberVisibility visibility);
/// <summary>
/// Updates the stored value with the current property value.
/// </summary>
/// <param name="obj">The object which the delegate operates on.</param>
/// <param name="property">The property that the delegate will invoke.</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
public abstract void UpdateValue();
/// <summary>
/// Applies the preset value to the delegate.
/// </summary>
public abstract void ApplyValue();
}
/// <summary>
/// Generic class which implements a type specific delegate and value that can be called on when the preset value should be applied.
/// See AOTLinker for an explanation of why a different class name is used for AOT platforms.
/// </summary>
[UnityEngine.Scripting.Preserve]
public class GenericDelegate<T> : BaseDelegate
{
private T m_Value;
private MethodInfo m_SetMethod;
private Action<T> m_Setter;
private Func<T> m_Getter;
private bool m_IsIList;
public override MethodInfo SetMethod { get { return m_SetMethod; } }
/// <summary>
/// Initialize the delegate and value.
/// </summary>
/// <param name="obj">The object which the delegate operates on.</param>
/// <param name="property">The property that the delegate will invoke.</param>
/// <param name="valuePositionMap">A mapping between the value hash and position.</param>
/// <param name="data">The serialization data which contains the values for the property (as well as all other properties).</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
public override void Initialize(object obj, PropertyInfo property, Dictionary<int, int> valuePositionMap, Serialization data, MemberVisibility visibility)
{
m_SetMethod = property.GetSetMethod(visibility != MemberVisibility.Public);
if (m_SetMethod != null) {
m_Setter = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), obj, m_SetMethod);
var bitwiseHash = new Version(data.Version).CompareTo(new Version("3.1")) >= 0;
var value = Serializer.BytesToValue(typeof(T), property.Name, valuePositionMap, 0, data.Values, data.ValuePositions, data.UnityObjects, false, visibility, bitwiseHash);
if (value != null && !value.Equals(null)) {
m_Value = (T)value;
}
var type = typeof(T);
m_IsIList = typeof(IList).IsAssignableFrom(type);
if (m_IsIList) {
// The Get method only needs to be assigned if the type is an IList because the actual object isn't copied by reference for arrays.
// Each individual element within the array needs to be interated on.
var getMethod = property.GetGetMethod(visibility != MemberVisibility.Public);
if (getMethod != null) {
m_Getter = (Func<T>)Delegate.CreateDelegate(typeof(Func<T>), obj, getMethod);
}
}
}
}
/// <summary>
/// Initialize the delegate.
/// </summary>
/// <param name="obj">The object which the delegate operates on.</param>
/// <param name="property">The property that the delegate will invoke.</param>
/// <param name="visibility">Specifies the visibility of the field/properties that should be retrieved.</param>
public override void Initialize(object obj, PropertyInfo property, MemberVisibility visibility)
{
m_SetMethod = property.GetSetMethod(visibility != MemberVisibility.Public);
if (m_SetMethod != null) {
m_Setter = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), obj, m_SetMethod);
}
var getMethod = property.GetGetMethod(visibility != MemberVisibility.Public);
if (getMethod != null) {
m_Getter = (Func<T>)Delegate.CreateDelegate(typeof(Func<T>), obj, getMethod);
// Create an instance of the value if it is an array or a list. This will allow a snapshot of the array/list elements to be saved without having the
// array/list change because it is later modified by reference.
var type = typeof(T);
m_IsIList = typeof(IList).IsAssignableFrom(type);
if (m_IsIList) {
if (typeof(T).IsArray) {
var value = m_Getter() as Array;
m_Value = (T)(object)Array.CreateInstance(type.GetElementType(), value == null ? 0 : value.Length);
} else {
var baseType = type;
while (!baseType.IsGenericType) {
baseType = baseType.BaseType;
}
var elementType = baseType.GetGenericArguments()[0];
if (type.IsGenericType) {
m_Value = (T)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType));
} else {
m_Value = (T)Activator.CreateInstance(type);
}
}
}
// The value should be set at the same time the delegate is initailized.
UpdateValue();
}
}
/// <summary>
/// Updates the stored value with the current property value.
/// </summary>
public override void UpdateValue()
{
if (m_Getter == null) {
Debug.LogError("Error: Unable to retrieve an updated value - the Get method is null.");
return;
}
// Update the individual elements if the value is a list or an array. This will prevent the array/list from being changed because it is
// later modified by reference.
if (m_IsIList) {
UpdateIList(m_Getter(), m_Value);
} else { // Not an array/list.
m_Value = m_Getter();
}
}
/// <summary>
/// Applies the preset value to the delegate.
/// </summary>
public override void ApplyValue()
{
if (m_IsIList) {
UpdateIList(m_Value, m_Getter());
} else {
m_Setter(m_Value);
}
}
/// <summary>
/// Updates the source array/list elements to the destination elements.
/// </summary>
/// <param name="source">The array/list to copy the references from.</param>
/// <param name="destination">The array/list to copy the references to.</param>
private void UpdateIList(T source, T destination)
{
var type = typeof(T);
if (type.IsArray) {
var sourceArray = source as Array;
var destinationArray = destination as Array;
if (sourceArray != null && destinationArray != null) {
// The array sizes need to match.
if (destinationArray != null && sourceArray.Length != destinationArray.Length) {
destinationArray = Array.CreateInstance(typeof(T).GetElementType(), sourceArray.Length);
// There's no way to avoid the boxing/unboxing. It is recommended that arrays are not changed to avoid this.
m_Setter((T)(object)destinationArray);
}
for (int i = 0; i < sourceArray.Length; ++i) {
destinationArray.SetValue(sourceArray.GetValue(i), i);
}
}
} else {
var sourceList = source as IList<T>;
var destinationList = destination as IList<T>;
// Remove any extra elements.
if (destinationList.Count > sourceList.Count) {
var removeCount = destinationList.Count - sourceList.Count;
for (int i = 0; i < removeCount; ++i) {
destinationList.RemoveAt(destinationList.Count - 1);
}
}
// Update the element referance.
for (int i = 0; i < sourceList.Count; ++i) {
if (i < destinationList.Count) {
destinationList[i] = sourceList[i];
} else {
destinationList.Add(sourceList[i]);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e9bd6539f57969b4284097c894d4f578
timeCreated: 1481960214
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {fileID: 2800000, guid: e2150fcaaf9a0a34baf8e5554d64da37, type: 3}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,165 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using Opsive.Shared.Utility;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// A State contains a preset which can change property values at runtime. The state has a name associated with it which allows for easy reference to it.
/// </summary>
[System.Serializable]
public class State
{
[Tooltip("The name of the state.")]
[SerializeField] protected string m_Name;
[Tooltip("The preset used by the state.")]
[SerializeField] protected Preset m_Preset;
[Tooltip("A list of other states that the current state can prevent from being enabled if the current state is enabled.")]
[SerializeField] protected string[] m_BlockList;
[Tooltip("Is the state the default state?")]
[SerializeField] protected bool m_Default;
[Tooltip("Is the state active?")]
[System.NonSerialized] [SerializeField] protected bool m_Active;
private IStateOwner m_Owner;
public IStateOwner Owner { get { return m_Owner; } }
public string Name { get { return m_Name; }
#if UNITY_EDITOR
set { m_Name = value; }
#endif
}
public Preset Preset { get { return m_Preset; } set { m_Preset = value; } }
#if UNITY_EDITOR
public string[] BlockList { get { return m_BlockList; } set { m_BlockList = value; } }
#endif
public bool Default { get { return m_Default; }
#if UNITY_EDITOR
set { m_Default = value; }
#endif
}
public bool Active { get { return m_Active; } set { m_Active = value; } }
[System.NonSerialized] private State[] m_BlockStates;
/// <summary>
/// Default constructor for State.
/// </summary>
public State() { }
/// <summary>
/// Constructor for State. Used by the component inspector.
/// </summary>
/// <param name="name">The name of the state.</param>
/// <param name="defaultState">Is the state the default state?</param>
public State(string name, bool defaultState)
{
m_Name = name;
m_Default = defaultState;
}
/// <summary>
/// Constructor for State. Used by the State Configuration.
/// </summary>
/// <param name="name">The name of the state.</param>
/// <param name="preset">The preset that the state uses.</param>
/// <param name="blockList">The list of blocked states.</param>
public State(string name, Preset preset, string[] blockList)
{
m_Name = name;
m_Preset = preset;
m_BlockList = blockList;
}
/// <summary>
/// Initializes the state - binds the object and applies any blocking rules.
/// </summary>
/// <param name="owner">The object which is bound to the state.</param>
/// <param name="nameStateMap">A mapping between the state name and the state object.</param>
public void Initialize(IStateOwner owner, Dictionary<string, State> nameStateMap)
{
// The preset can be null if the Component has no valid properties that can be changed.
if (m_Preset != null) {
// The preset may be used by another object of the same type.
// Initialize a new preset to prevent the delegates from conflicting with each other.
if (m_Preset.IsInitialized) {
m_Preset = Object.Instantiate(m_Preset);
}
m_Preset.Initialize(owner, MemberVisibility.Public);
}
m_Owner = owner;
UpdateBlockList(nameStateMap);
}
/// <summary>
/// Update the block list to point to the state object.
/// </summary>
/// <param name="nameStateMap">A mapping between the state name and the state object.</param>
private void UpdateBlockList(Dictionary<string, State> nameStateMap)
{
if (m_BlockList != null && m_BlockList.Length > 0) {
m_BlockStates = new State[m_BlockList.Length];
State state;
var count = 0;
for (int i = 0; i < m_BlockList.Length; ++i) {
if (m_BlockList[i] == null) {
continue;
}
if (nameStateMap.TryGetValue(m_BlockList[count], out state)) {
m_BlockStates[count] = state;
} else {
Debug.LogWarning("Error: Unable to find block state with name \"" + m_BlockList[count] + "\" within state " + m_Name + " on object " + m_Owner);
}
count++;
}
if (m_BlockList.Length != count) {
System.Array.Resize(ref m_BlockList, count);
System.Array.Resize(ref m_BlockStates, count);
}
}
}
/// <summary>
/// Is the state blocked by another enabed state?
/// </summary>
/// <returns>True if the state is blocked.</returns>
public bool IsBlocked()
{
if (m_BlockStates != null) {
for (int i = 0; i < m_BlockStates.Length; ++i) {
if (m_BlockStates[i] != null && m_BlockStates[i].Active && !m_BlockStates[i].IsBlocked()) {
return true;
}
}
}
return false;
}
/// <summary>
/// Applies the values to the component specified within the preset object.
/// </summary>
public void ApplyValues()
{
if (m_Preset != null) {
m_Preset.ApplyValues();
}
}
/// <summary>
/// Applies the values to the component specified within the preset object.
/// </summary>
public void ApplyValues(Preset.BaseDelegate[] delegates)
{
if (m_Preset != null) {
m_Preset.ApplyValues(delegates);
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: c8d40df4ef0129e44b885f12b0609b95
timeCreated: 1482788438
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,51 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using UnityEngine;
/// <summary>
/// Acts as the parent component which can use the state system to change property values.
/// </summary>
public class StateBehavior : MonoBehaviour, IStateOwner
{
[Tooltip("A list of all states that the component can change to.")]
[HideInInspector] [SerializeField] protected State[] m_States = new State[] { new State("Default", true) };
[Opsive.Shared.Utility.NonSerialized] public State[] States { get { return m_States; } set { m_States = value; } }
/// <summary>
/// Initialize the default values.
/// </summary>
protected virtual void Awake()
{
if (Application.isPlaying) {
StateManager.Initialize(gameObject, this, m_States);
}
}
/// <summary>
/// Activates or deactivates the specified state.
/// </summary>
/// <param name="stateName">The name of the state to change the activate status of.</param>
/// <param name="active">Should the state be activated?</param>
public void SetState(string stateName, bool active)
{
StateManager.SetState(this, m_States, stateName, active);
}
/// <summary>
/// Callback when the StateManager will change the active state on the current object.
/// </summary>
public virtual void StateWillChange() { }
/// <summary>
/// Callback when the StateManager has changed the active state on the current object.
/// </summary>
public virtual void StateChange() { }
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d1e534f058bdcbf4cb5c680370f77a86
timeCreated: 1482746434
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,357 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using Opsive.Shared.Utility;
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// The StateConfiguration class contains an array of profiles with prespecified states that can be added to an object.
/// </summary>
public class StateConfiguration : ScriptableObject
{
[Tooltip("An array of profiles which map a name to a list of states.")]
[SerializeField] protected Profile[] m_Profiles;
public Profile[] Profiles { get { return m_Profiles; } set { m_Profiles = value; ResetInitialization(); } }
private Dictionary<string, Dictionary<Type, Profile.StateElement[]>> m_ProfileStateMap;
/// <summary>
/// The Profile class contains an array of states that should be added.
/// </summary>
[Serializable]
public class Profile
{
/// <summary>
/// Specifies the type of object that represents the profile.
/// </summary>
public enum ProfileType { Character, Item, Camera }
/// <summary>
/// A representation of the StateSystem.State object, used to restore states on an object.
/// </summary>
[Serializable]
public class StateElement
{
[Tooltip("The name of the state.")]
[SerializeField] protected string m_Name;
[Tooltip("The preset which the state belongs to.")]
[SerializeField] protected PersistablePreset m_Preset;
[Tooltip("Any other states that the current state can block.")]
[SerializeField] protected string[] m_BlockList;
[Tooltip("Is the state the default state? Only one state can be the default for each object type.")]
[SerializeField] protected bool m_Default;
public string Name { get { return m_Name; } set { m_Name = value; } }
public PersistablePreset Preset { get { return m_Preset; } set { m_Preset = value; } }
public string[] BlockList { get { return m_BlockList; } set { m_BlockList = value; } }
public bool Default { get { return m_Default; } set { m_Default = value; } }
/// <summary>
/// Default constructor.
/// </summary>
public StateElement() { }
/// <summary>
/// Three parameter constructor.
/// </summary>
/// <param name="name">The name of the state.</param>
/// <param name="preset">The preset used by the state.</param>
/// <param name="blockList">The list of states that the current state should block.</param>
/// <param name="defaultState">Is the state a default state?</param>
public StateElement(string name, PersistablePreset preset, string[] blockList, bool defaultState)
{
m_Name = name;
m_Preset = preset;
m_BlockList = blockList;
m_Default = defaultState;
}
}
[Tooltip("The name of the profile.")]
[SerializeField] protected string m_Name;
[Tooltip("The type of object the profile represents.")]
[SerializeField] protected ProfileType m_Type;
[Tooltip("The states which belong to the profile.")]
[SerializeField] protected StateElement[] m_StateElements;
public string Name { get { return m_Name; } set { m_Name = value; } }
public ProfileType Type { get { return m_Type; } set { m_Type = value; } }
public StateElement[] StateElements { get { return m_StateElements; } set { m_StateElements = value; } }
}
/// <summary>
/// Creates a mapping for all of the profiles.
/// </summary>
private void Initialize()
{
// The mapping may have already been initialized.
if (m_ProfileStateMap != null) {
return;
}
m_ProfileStateMap = new Dictionary<string, Dictionary<Type, Profile.StateElement[]>>();
if (m_Profiles == null) {
return;
}
for (int i = 0; i < m_Profiles.Length; ++i) {
var profileStateElements = m_Profiles[i].StateElements;
var typeStateMap = new Dictionary<Type, Profile.StateElement[]>();
m_ProfileStateMap.Add(m_Profiles[i].Name, typeStateMap);
for (int j = 0; j < profileStateElements.Length; ++j) {
// The state must be valid.
if (string.IsNullOrEmpty(profileStateElements[j].Name) || profileStateElements[j].Preset == null) {
continue;
}
var objType = Utility.UnityEngineUtility.GetType(profileStateElements[j].Preset.Data.ObjectType);
if (objType == null) {
continue;
}
Profile.StateElement[] stateElements;
if (!typeStateMap.TryGetValue(objType, out stateElements)) {
stateElements = new Profile.StateElement[0];
typeStateMap.Add(objType, stateElements);
}
// Add the state to the array that is specific for the object type.
Array.Resize(ref stateElements, stateElements.Length + 1);
stateElements[stateElements.Length - 1] = profileStateElements[j];
// Allocating a new array requires the dictionary element to be updated.
typeStateMap[objType] = stateElements;
}
}
}
/// <summary>
/// After a change the state map must be cleared so it can be initialized again.
/// </summary>
public void ResetInitialization()
{
m_ProfileStateMap = null;
}
/// <summary>
/// Returns a list of profiles that have been added to the specified GameObject.
/// </summary>
/// <param name="gameObject">The GameObject to retrieve the profiles for.</param>
/// <param name="type">The type of profiles that should be shown.</param>
/// <returns>A list of profiles that have been added to the specified GameObject.</returns>
public List<string> GetProfilesForGameObject(GameObject gameObject, Profile.ProfileType type)
{
Initialize();
var profileNames = new List<string>();
if (gameObject == null) {
// All of the profiles for the specfied type should be shown if the GameObject is null.
if (m_Profiles != null) {
for (int i = 0; i < m_Profiles.Length; ++i) {
if (type != m_Profiles[i].Type) {
continue;
}
profileNames.Add(m_Profiles[i].Name);
}
}
return profileNames;
}
var stateBehaviors = gameObject.GetComponents<StateBehavior>();
for (int i = 0; i < stateBehaviors.Length; ++i) {
GetProfilesForType(stateBehaviors[i].GetType(), type, profileNames);
// The GameObject may contain the UltimateCharacterLocomotion component where the
// movement types, abilities, item abilities, and effects also need to be searched.
if (stateBehaviors[i] is Character.UltimateCharacterLocomotion) {
var characterLocomotion = stateBehaviors[i] as Character.UltimateCharacterLocomotion;
characterLocomotion.DeserializeMovementTypes();
for (int j = 0; j < characterLocomotion.MovementTypes.Length; ++j) {
GetProfilesForType(characterLocomotion.MovementTypes[j].GetType(), type, profileNames);
}
characterLocomotion.DeserializeAbilities();
if (characterLocomotion.Abilities != null) {
for (int j = 0; j < characterLocomotion.Abilities.Length; ++j) {
GetProfilesForType(characterLocomotion.Abilities[j].GetType(), type, profileNames);
}
}
characterLocomotion.DeserializeItemAbilities();
if (characterLocomotion.ItemAbilities != null) {
for (int j = 0; j < characterLocomotion.ItemAbilities.Length; ++j) {
GetProfilesForType(characterLocomotion.ItemAbilities[j].GetType(), type, profileNames);
}
}
characterLocomotion.DeserializeEffects();
if (characterLocomotion.Effects != null) {
for (int j = 0; j < characterLocomotion.Effects.Length; ++j) {
GetProfilesForType(characterLocomotion.Effects[j].GetType(), type, profileNames);
}
}
}
// The GameObject may contain the CameraController component where the view types also need to be searched.
if (stateBehaviors[i] is UltimateCharacterController.Camera.CameraController) {
var cameraController = stateBehaviors[i] as UltimateCharacterController.Camera.CameraController;
cameraController.DeserializeViewTypes();
for (int j = 0; j < cameraController.ViewTypes.Length; ++j) {
GetProfilesForType(cameraController.ViewTypes[j].GetType(), type, profileNames);
}
}
}
return profileNames;
}
/// <summary>
/// Adds to a list of profiles that contain the specified type.
/// </summary>
/// <param name="objType">The type of object to retrieve the profiles for.</param>
/// <param name="profileNames">A list of profiles that contain the specifeid type.</param>
private void GetProfilesForType(Type objType, Profile.ProfileType profileType, List<string> profileNames)
{
for (int i = 0; i < m_Profiles.Length; ++i) {
if (m_Profiles[i].Type != profileType) {
continue;
}
Dictionary<Type, Profile.StateElement[]> typeStateMap;
if (!m_ProfileStateMap.TryGetValue(m_Profiles[i].Name, out typeStateMap)) {
continue;
}
// Add the profile name if the type exists.
if (typeStateMap.ContainsKey(objType) && !profileNames.Contains(m_Profiles[i].Name)) {
profileNames.Add(m_Profiles[i].Name);
}
}
}
/// <summary>
/// Add the states to the GameObject that have been added to the specified profile name.
/// </summary>
/// <param name="profileName">The name of the profile to retrieve the states from.</param>
/// <param name="type">The type of profiles that should be shown.</param>
/// <param name="gameObject">The GameObject to add the states to.</param>
public void AddStatesToGameObject(string profileName, GameObject gameObject)
{
Initialize();
var stateBehaviors = gameObject.GetComponents<StateBehavior>();
for (int i = 0; i < stateBehaviors.Length; ++i) {
AddStatesToObject(profileName, stateBehaviors[i]);
// The GameObject may contain the UltimateCharacterLocomotion component where the
// movement types, abilities, item abilities, and effects also need to be searched.
if (stateBehaviors[i] is Character.UltimateCharacterLocomotion) {
var characterLocomotion = stateBehaviors[i] as Character.UltimateCharacterLocomotion;
characterLocomotion.DeserializeMovementTypes();
if (characterLocomotion.MovementTypes != null) {
for (int j = 0; j < characterLocomotion.MovementTypes.Length; ++j) {
AddStatesToObject(profileName, characterLocomotion.MovementTypes[j]);
}
var movementTypes = new List<Character.MovementTypes.MovementType>(characterLocomotion.MovementTypes);
characterLocomotion.MovementTypeData = Shared.Utility.Serialization.Serialize<Character.MovementTypes.MovementType>(movementTypes);
characterLocomotion.MovementTypes = movementTypes.ToArray();
}
characterLocomotion.DeserializeAbilities();
if (characterLocomotion.Abilities != null) {
for (int j = 0; j < characterLocomotion.Abilities.Length; ++j) {
AddStatesToObject(profileName, characterLocomotion.Abilities[j]);
}
Utility.Builders.AbilityBuilder.SerializeAbilities(characterLocomotion);
}
characterLocomotion.DeserializeItemAbilities();
if (characterLocomotion.ItemAbilities != null) {
for (int j = 0; j < characterLocomotion.ItemAbilities.Length; ++j) {
AddStatesToObject(profileName, characterLocomotion.ItemAbilities[j]);
}
Utility.Builders.AbilityBuilder.SerializeItemAbilities(characterLocomotion);
}
characterLocomotion.DeserializeEffects();
if (characterLocomotion.Effects != null) {
for (int j = 0; j < characterLocomotion.Effects.Length; ++j) {
AddStatesToObject(profileName, characterLocomotion.Effects[j]);
}
var effects = new List<Character.Effects.Effect>(characterLocomotion.Effects);
characterLocomotion.EffectData = Shared.Utility.Serialization.Serialize<Character.Effects.Effect>(effects);
characterLocomotion.Effects = effects.ToArray();
}
}
// The GameObject may contain the CameraController component where the view types also need to be searched.
if (stateBehaviors[i] is UltimateCharacterController.Camera.CameraController) {
var cameraController = stateBehaviors[i] as UltimateCharacterController.Camera.CameraController;
cameraController.DeserializeViewTypes();
if (cameraController.ViewTypes != null) {
for (int j = 0; j < cameraController.ViewTypes.Length; ++j) {
AddStatesToObject(profileName, cameraController.ViewTypes[j]);
}
Utility.Builders.ViewTypeBuilder.SerializeViewTypes(cameraController);
}
}
}
}
/// <summary>
/// Add the states to the object that have been added to the specified profile name.
/// </summary>
/// <param name="profileName">The name of the profile to retrieve the states from.</param>
/// <param name="obj">The object to add the states to.</param>
private void AddStatesToObject(string profileName, object obj)
{
Dictionary<Type, Profile.StateElement[]> typeStateMap;
if (!m_ProfileStateMap.TryGetValue(profileName, out typeStateMap)) {
return;
}
Profile.StateElement[] stateElements;
if (!typeStateMap.TryGetValue(obj.GetType(), out stateElements)) {
return;
}
var defaultIndex = -1;
for (int i = 0; i < stateElements.Length; ++i) {
if (stateElements[i].Default) {
stateElements[i].Preset.Initialize(obj, MemberVisibility.Public);
stateElements[i].Preset.ApplyValues();
defaultIndex = i;
break;
}
}
// The profile may contain states from more than one of the same object type. Only one set of states should be applied.
if (defaultIndex != -1 && defaultIndex < stateElements.Length - 1) {
Array.Resize(ref stateElements, defaultIndex + 1);
}
var states = new State[stateElements.Length + (defaultIndex != -1 ? 0 : 1)]; // One element is reserved for the default state.
for (int i = 0; i < stateElements.Length; ++i) {
if (stateElements[i].Default) {
continue;
}
states[i] = new State(stateElements[i].Name, stateElements[i].Preset, stateElements[i].BlockList);
}
if (obj is StateBehavior) {
var stateBehavior = obj as StateBehavior;
states[states.Length - 1] = stateBehavior.States[stateBehavior.States.Length - 1];
stateBehavior.States = states;
} else { // StateObject.
var stateObject = obj as StateObject;
states[states.Length - 1] = stateObject.States[stateObject.States.Length - 1];
stateObject.States = states;
}
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3179c486391da9443aa57f9ce8c54954
timeCreated: 1516916575
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,21 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
#if UNITY_EDITOR
namespace Opsive.UltimateCharacterController.StateSystem
{
using UnityEngine;
/// <summary>
/// Inspector helper class for the UltimateCharacterControllerInspector to be able to display states within the ReorderableList.
/// </summary>
public class StateInspectorHelper : MonoBehaviour
{
[SerializeField] private int[] m_StateIndexData;
public int[] StateIndexData { get { return m_StateIndexData; } set { m_StateIndexData = value; } }
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: cfe2c7470df2c0946ac594d45fdee43b
timeCreated: 1492140661
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,512 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using Opsive.Shared.Events;
using Opsive.Shared.Game;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
/// <summary>
/// Handles the activation and deactivation of states.
/// </summary>
public class StateManager : MonoBehaviour
{
[Tooltip("Should the OnStateChange event be sent when the state changes active status?")]
[SerializeField] protected bool m_SendStateChangeEvent;
public bool SendStateChangeEvent { get { return m_SendStateChangeEvent; } set { m_SendStateChangeEvent = value; } }
private static StateManager s_Instance;
private static StateManager Instance
{
get
{
if (!s_Initialized) {
s_Instance = new GameObject("State Manager").AddComponent<StateManager>();
s_Initialized = true;
}
return s_Instance;
}
}
private static bool s_Initialized;
private Dictionary<object, Dictionary<string, State>> m_ObjectNameStateMap = new Dictionary<object, Dictionary<string, State>>();
private Dictionary<GameObject, Dictionary<string, List<State>>> m_GameObjectNameStateList = new Dictionary<GameObject, Dictionary<string, List<State>>>();
private Dictionary<GameObject, List<GameObject>> m_LinkedGameObjectList = new Dictionary<GameObject, List<GameObject>>();
private Dictionary<State, State[]> m_StateArrayMap = new Dictionary<State, State[]>();
private Dictionary<GameObject, HashSet<string>> m_ActiveCharacterStates = new Dictionary<GameObject, HashSet<string>>();
private Dictionary<GameObject, Dictionary<string, ScheduledEventBase>> m_DisableStateTimerMap;
/// <summary>
/// The object has been enabled.
/// </summary>
private void OnEnable()
{
// The object may have been enabled outside of the scene unloading.
if (s_Instance == null) {
s_Instance = this;
s_Initialized = true;
SceneManager.sceneUnloaded -= SceneUnloaded;
}
}
/// <summary>
/// Initializes the states belonging to the owner on the GameObject.
/// </summary>
/// <param name="gameObject">The GameObject to enable or disable all of the states on.</param>
/// <param name="owner">The object that state belongs to.</param>
/// <param name="states">A list of all of the states which the owner contains.</param>
public static void Initialize(GameObject gameObject, IStateOwner owner, State[] states)
{
Instance.InitializeInternal(gameObject, owner, states);
}
/// <summary>
/// Internal method which initializes the states belonging to the owner on the GameObject.
/// </summary>
/// <param name="gameObject">The GameObject to enable or disable all of the states on.</param>
/// <param name="owner">The object that state belongs to.</param>
/// <param name="states">A list of all of the states which the owner contains.</param>
private void InitializeInternal(GameObject gameObject, IStateOwner owner, State[] states)
{
// The last state will always be reserved for the default state.
if (states[states.Length - 1] == null) {
states[states.Length - 1] = new State("Default", true);
}
states[states.Length - 1].Preset = DefaultPreset.CreateDefaultPreset();
Dictionary<string, State> nameStateMap;
if (!m_ObjectNameStateMap.TryGetValue(owner, out nameStateMap)) {
nameStateMap = new Dictionary<string, State>();
m_ObjectNameStateMap.Add(owner, nameStateMap);
}
// Populate the maps for quick lookup based on owner and GameObject.
GameObject characterGameObject = null;
var characterLocomotion = gameObject.GetCachedParentComponent<Character.UltimateCharacterLocomotion>();
if (characterLocomotion != null) {
characterGameObject = characterLocomotion.gameObject;
} else {
var cameraController = gameObject.GetCachedParentComponent<UltimateCharacterController.Camera.CameraController>();
if (cameraController != null) {
characterGameObject = cameraController.Character;
}
}
for (int i = 0; i < states.Length; ++i) {
if (states[i].Preset == null) {
Debug.LogError(string.Format("Error: The state {0} on {1} does not have a preset. Ensure each non-default state contains a preset.", states[i].Name, owner), owner as Object);
}
nameStateMap.Add(states[i].Name, states[i]);
Dictionary<string, List<State>> nameStateList;
if (!m_GameObjectNameStateList.TryGetValue(gameObject, out nameStateList)) {
nameStateList = new Dictionary<string, List<State>>();
m_GameObjectNameStateList.Add(gameObject, nameStateList);
}
// Child GameObjects should listen for states set on the parent. This for example allows an item to react to a state change even if that state change
// is set on the character. The character GameObject does not need to be made aware of the Default state.
if (i != states.Length - 1) {
if (characterGameObject != null && gameObject != characterGameObject) {
Dictionary<string, List<State>> characterNameStateList;
if (!m_GameObjectNameStateList.TryGetValue(characterGameObject, out characterNameStateList)) {
characterNameStateList = new Dictionary<string, List<State>>();
m_GameObjectNameStateList.Add(characterGameObject, characterNameStateList);
}
List<State> characterStateList;
if (!characterNameStateList.TryGetValue(states[i].Name, out characterStateList)) {
characterStateList = new List<State>();
characterNameStateList.Add(states[i].Name, characterStateList);
}
characterStateList.Add(states[i]);
}
}
List<State> stateList;
if (!nameStateList.TryGetValue(states[i].Name, out stateList)) {
stateList = new List<State>();
nameStateList.Add(states[i].Name, stateList);
}
stateList.Add(states[i]);
m_StateArrayMap.Add(states[i], states);
}
// Initialize the state after the map has been created.
for (int i = 0; i < states.Length; ++i) {
states[i].Initialize(owner, nameStateMap);
}
// The default state is always last.
states[states.Length - 1].Active = true;
// Remember the active character states so if a GameObject is initialized after a state has already been activated that newly initialized GameObject
// can start the correct states. As an example an item could be picked up after the character is already aiming. That item should go directly
// into the aim state instead of requring the character to aim again.
if (characterGameObject != null) {
if (characterGameObject == gameObject) {
// If the current GameObject is the character then the active states should be tracked.
if (!m_ActiveCharacterStates.ContainsKey(gameObject)) {
m_ActiveCharacterStates.Add(gameObject, new HashSet<string>());
}
} else {
// If the current GameObject is not the character then the active character states should be applied to the child object.
HashSet<string> activeStates;
if (m_ActiveCharacterStates.TryGetValue(characterGameObject, out activeStates)) {
if (activeStates.Count > 0) {
foreach (var stateName in activeStates) {
SetState(gameObject, stateName, true);
}
}
}
}
}
}
/// <summary>
/// Links the original GameObject to the linked GameObject. When GameObjects are linked the state will be updated for each GameObject even when only the
/// original GameObject is set.
/// </summary>
/// <param name="original">The original GameObject to link.</param>
/// <param name="linkedGameObject">The GameObject that should be linked to the original GameObject.</param>
/// <param name="link">Should the GameObjects be linked. If fales the GameObjects will be unlinked.</param>
public static void LinkGameObjects(GameObject original, GameObject linkedGameObject, bool link)
{
Instance.LinkGameObjectsInternal(original, linkedGameObject, link);
}
/// <summary>
/// Internal method which links the original GameObject to the linked GameObject. When GameObjects are linked the state will be updated for each
/// GameObject even when only the original GameObject is set.
/// </summary>
/// <param name="original">The original GameObject to link.</param>
/// <param name="linkedGameObject">The GameObject that should be linked to the original GameObject.</param>
/// <param name="link">Should the GameObjects be linked. If fales the GameObjects will be unlinked.</param>
private void LinkGameObjectsInternal(GameObject original, GameObject linkedGameObject, bool link)
{
List<GameObject> linkedGameObjectList;
if (!m_LinkedGameObjectList.TryGetValue(original, out linkedGameObjectList) && link) {
linkedGameObjectList = new List<GameObject>();
m_LinkedGameObjectList.Add(original, linkedGameObjectList);
}
if (linkedGameObjectList != null) {
if (link) {
linkedGameObjectList.Add(linkedGameObject);
// If the current GameObject is not the character then the active character states should be applied to the child object.
HashSet<string> activeStates;
if (m_ActiveCharacterStates.TryGetValue(original, out activeStates)) {
if (activeStates.Count > 0) {
foreach (var stateName in activeStates) {
SetState(linkedGameObject, stateName, true);
}
}
}
} else {
linkedGameObjectList.Remove(linkedGameObject);
}
}
}
/// <summary>
/// Activates or deactivates the specified state.
/// </summary>
/// <param name="owner">The object that state belongs to.</param>
/// <param name="states">A list of all of the states which the owner contains.</param>
/// <param name="stateName">The name of the state to change the active status of.</param>
/// <param name="active">Should the state be activated?</param>
public static void SetState(object owner, State[] states, string stateName, bool active)
{
Instance.SetStateInternal(owner, states, stateName, active);
}
/// <summary>
/// Internal method which activates or deactivates the specified state.
/// </summary>
/// <param name="owner">The object that state belongs to.</param>
/// <param name="states">A list of all of the states which the owner contains.</param>
/// <param name="stateName">The name of the state to change the active status of.</param>
/// <param name="active">Should the state be activated?</param>
private void SetStateInternal(object owner, State[] states, string stateName, bool active)
{
// Lookup the state by owner.
Dictionary<string, State> nameStateMap;
if (!m_ObjectNameStateMap.TryGetValue(owner, out nameStateMap)) {
Debug.LogWarning("Warning: Unable to find the name state map on object " + owner);
return;
}
// Lookup the state by name.
State state;
if (!nameStateMap.TryGetValue(stateName, out state)) {
Debug.LogWarning("Warning: Unable to find the state with name " + stateName);
return;
}
// The state has been found, activate or deactivate the states.
if (state.Active != active) {
ActivateStateInternal(state, active, states);
}
}
/// <summary>
/// Activates or deactivates all of the states on the specified GameObject with the specified name.
/// </summary>
/// <param name="gameObject">The GameObject to enable or disable all of the states on.</param>
/// <param name="stateName">The name of the state to change the active status of.</param>
/// <param name="active">Should the state be activated?</param>
public static void SetState(GameObject gameObject, string stateName, bool active)
{
Instance.SetStateInternal(gameObject, stateName, active);
}
/// <summary>
/// Internal method which activates or deactivates all of the states on the specified GameObject with the specified name.
/// </summary>
/// <param name="gameObject">The GameObject to enable or disable all of the states on.</param>
/// <param name="stateName">The name of the state to change the active status of.</param>
/// <param name="active">Should the state be activated?</param>
private void SetStateInternal(GameObject gameObject, string stateName, bool active)
{
// Remember the active character status.
var characterLocomotion = gameObject.GetCachedComponent<Character.UltimateCharacterLocomotion>();
if (characterLocomotion != null) {
HashSet<string> activeStates;
if (m_ActiveCharacterStates.TryGetValue(gameObject, out activeStates)) {
// If the state name appears within the set then the state is active.
if (active) {
activeStates.Add(stateName);
} else {
activeStates.Remove(stateName);
}
}
}
// Lookup the states by GameObject.
Dictionary<string, List<State>> nameStateList;
if (!m_GameObjectNameStateList.TryGetValue(gameObject, out nameStateList)) {
SetLinkStateInternal(gameObject, stateName, active);
return;
}
// Lookup the states by name.
List<State> stateList;
if (!nameStateList.TryGetValue(stateName, out stateList)) {
SetLinkStateInternal(gameObject, stateName, active);
return;
}
// An event can be sent when the active status changes. This is useful for multiplayer in that it allows the networking implementation
// to send the state changes across the network.
if (m_SendStateChangeEvent) {
EventHandler.ExecuteEvent("OnStateChange", gameObject, stateName, active);
}
// The states have been found, activate or deactivate the states.
for (int i = 0; i < stateList.Count; ++i) {
if (stateList[i].Active != active) {
// The state array must exist to be able to apply the changes.
State[] states;
if (!m_StateArrayMap.TryGetValue(stateList[i], out states)) {
Debug.LogWarning("Warning: Unable to find the state array with state name " + stateName);
return;
}
// Notify the owner that the states will change.
stateList[i].Owner.StateWillChange();
ActivateStateInternal(stateList[i], active, states);
// Notify the owner that the state has changed.
stateList[i].Owner.StateChange();
}
}
SetLinkStateInternal(gameObject, stateName, active);
}
/// <summary>
/// Internal method which activates or deactivates all of the states on the GameObjects linked from the GameObject with the specified name.
/// </summary>
/// <param name="gameObject">The GameObject to enable or disable all of the states on.</param>
/// <param name="stateName">The name of the state to change the active status of.</param>
/// <param name="active">Should the state be activated?</param>
private void SetLinkStateInternal(GameObject gameObject, string stateName, bool active)
{
List<GameObject> linkedGameObjects;
if (m_LinkedGameObjectList.TryGetValue(gameObject, out linkedGameObjects)) {
for (int i = 0; i < linkedGameObjects.Count; ++i) {
SetStateInternal(linkedGameObjects[i], stateName, active);
}
}
}
/// <summary>
/// Activates or deactivates the specified state. In most cases SetState should be used instead of ActivateState.
/// </summary>
/// <param name="state">The state to activate or deactivate.</param>
/// <param name="active">Should the state be activated?</param>
/// <param name="states">The array of states that the state belongs to.</param>
public static void ActivateState(State state, bool active, State[] states)
{
Instance.ActivateStateInternal(state, active, states);
}
/// <summary>
/// Internal method which activates or deactivates the specified state. In most cases SetState should be used instead of ActivateState.
/// </summary>
/// <param name="state">The state to activate or deactivate.</param>
/// <param name="active">Should the state be activated?</param>
/// <param name="states">The array of states that the state belongs to.</param>
private void ActivateStateInternal(State state, bool active, State[] states)
{
// Return early if there no work needs to be done.
if (state.Active == active) {
return;
}
// Set the active state.
state.Active = active;
// Apply the changes.
CombineStates(state, active, states);
}
/// <summary>
/// Loops through the states and applies the value. The states are looped in the order specified within the inspector from top to bottom.
/// </summary>
/// <param name="state">The state that was activated or deactivated.</param>
/// <param name="active">Was the activated?</param>
/// <param name="states">The array of states that the state belongs to.</param>
private void CombineStates(State state, bool active, State[] states)
{
if (active) {
// Apply the default value of the blocked states before looping through all of the states. This will ensure the default value
// is set for that property if no other states set the property value.
for (int i = states.Length - 2; i > -1; --i) {
if (states[i].Active && states[i].IsBlocked()) {
states[states.Length - 1].ApplyValues(states[i].Preset.Delegates);
}
}
} else {
// Restore the default values if the state is no longer active.
states[states.Length - 1].ApplyValues(state.Preset.Delegates);
}
// Loop backwards so the higher priority states are applied first. Do not apply the default state because it was applied above.
for (int i = states.Length - 2; i > -1; --i) {
// Don't apply the state if the state isn't active.
if (!states[i].Active) {
continue;
}
// Do not apply the state if it is currently blocked by another state.
if (states[i].IsBlocked()) {
continue;
}
states[i].ApplyValues();
}
}
/// <summary>
/// Activates the state and then deactivates the state after the specified amount of time.
/// </summary>
/// <param name="gameObject">The Gameobject to set the state on.</param>
/// <param name="stateName">The name of the state to activate and then deactivate.</param>
/// <param name="time">The amount of time that should elapse before the state is disabled.</param>
public static void DeactivateStateTimer(GameObject gameObject, string stateName, float time)
{
Instance.DeactivateStateTimerInternal(gameObject, stateName, time);
}
/// <summary>
/// Internal method which activates the state and then deactivates the state after the specified amount of time.
/// </summary>
/// <param name="gameObject">The Gameobject to set the state on.</param>
/// <param name="stateName">The name of the state to activate and then deactivate.</param>
/// <param name="time">The amount of time that should elapse before the state is disabled.</param>
private void DeactivateStateTimerInternal(GameObject gameObject, string stateName, float time)
{
if (m_DisableStateTimerMap == null) {
m_DisableStateTimerMap = new Dictionary<GameObject, Dictionary<string, ScheduledEventBase>>();
}
Dictionary<string, ScheduledEventBase> stateNameEventMap;
if (m_DisableStateTimerMap.TryGetValue(gameObject, out stateNameEventMap)) {
ScheduledEventBase disableEvent;
if (stateNameEventMap.TryGetValue(stateName, out disableEvent)) {
// The state name exists. This means that the timer is currently active and should first been cancelled.
Scheduler.Cancel(disableEvent);
disableEvent = Scheduler.Schedule(time, DeactivateState, gameObject, stateName);
} else {
// The state name hasn't been added yet. Add it to the map.
disableEvent = Scheduler.Schedule(time, DeactivateState, gameObject, stateName);
stateNameEventMap.Add(stateName, disableEvent);
}
} else {
// Neither the GameObject nor the state has been activated. Create the maps.
stateNameEventMap = new Dictionary<string, ScheduledEventBase>();
var disableEvent = Scheduler.Schedule(time, DeactivateState, gameObject, stateName);
stateNameEventMap.Add(stateName, disableEvent);
m_DisableStateTimerMap.Add(gameObject, stateNameEventMap);
}
}
/// <summary>
/// Deactives the specified state and removes it form the timer map.
/// </summary>
/// <param name="gameObject">The GameObject to set the state on.</param>
/// <param name="stateName">The name of the state to set.</param>
private void DeactivateState(GameObject gameObject, string stateName)
{
SetState(gameObject, stateName, false);
Dictionary<string, ScheduledEventBase> stateNameEventMap;
if (m_DisableStateTimerMap.TryGetValue(gameObject, out stateNameEventMap)) {
stateNameEventMap.Remove(stateName);
}
}
/// <summary>
/// Reset the initialized variable when the scene is no longer loaded.
/// </summary>
/// <param name="scene">The scene that was unloaded.</param>
private void SceneUnloaded(Scene scene)
{
s_Initialized = false;
s_Instance = null;
SceneManager.sceneUnloaded -= SceneUnloaded;
}
/// <summary>
/// The object has been disabled.
/// </summary>
private void OnDisable()
{
SceneManager.sceneUnloaded += SceneUnloaded;
}
#if UNITY_2019_3_OR_NEWER
/// <summary>
/// Reset the static variables for domain reloading.
/// </summary>
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void DomainReset()
{
s_Initialized = false;
s_Instance = null;
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0a172b92538d3594daa1dca445dfeb43
timeCreated: 1504636212
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: -400
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using System;
using UnityEngine;
/// <summary>
/// Acts as the parent object which can use the state system to change property values.
/// </summary>
public class StateObject : IStateOwner
{
[Tooltip("A list of all states that the component can change to.")]
[HideInInspector] [SerializeField] protected State[] m_States = new State[] { new State("Default", true) };
[Opsive.Shared.Utility.NonSerialized] public State[] States { get { return m_States; } set { m_States = value; } }
/// <summary>
/// Initializes the default values.
/// </summary>
/// <param name="gameObject">The GameObject this object is attached to.</param>
public virtual void Initialize(GameObject gameObject)
{
if (Application.isPlaying) {
StateManager.Initialize(gameObject, this, m_States);
}
}
/// <summary>
/// Activates or deactivates the specified state.
/// </summary>
/// <param name="stateName">The name of the state to change the active status of.</param>
/// <param name="active">Should the state be activated?</param>
public void SetState(string stateName, bool active)
{
StateManager.SetState(this, m_States, stateName, active);
}
/// <summary>
/// Callback when the StateManager will change the active state on the current object.
/// </summary>
public virtual void StateWillChange() { }
/// <summary>
/// Callback when the StateManager has changed the active state on the current object.
/// </summary>
public virtual void StateChange() { }
}
/// <summary>
/// Attribute which allows the a state to automatically be added.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class AddState : Attribute
{
private string m_Name;
private string m_PresetGUID;
public string Name { get { return m_Name; } }
public string PresetGUID { get { return m_PresetGUID; } }
public AddState(string name, string presetGUID)
{
m_Name = name;
m_PresetGUID = presetGUID;
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 76aa3b7ef3f16494eb72fd2a9c1836c8
timeCreated: 1482746434
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,147 @@
/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.StateSystem
{
using Opsive.Shared.Events;
using Opsive.Shared.Game;
using Opsive.UltimateCharacterController.Audio;
using Opsive.UltimateCharacterController.Game;
using Opsive.UltimateCharacterController.Utility;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// Activates the specified state when the object enters the state, and deactivates the sate when the object leaves.
/// </summary>
public class StateTrigger : MonoBehaviour
{
[Tooltip("The name of the state to activate/deactivate.")]
[SerializeField] protected string m_StateName;
[Tooltip("The delay before the state should be enabled.")]
[SerializeField] protected float m_Delay;
[Tooltip("The amount of time the state should be enabled for.")]
[SerializeField] protected float m_Duration;
[Tooltip("The LayerMask that the trigger can set the state of.")]
[SerializeField] protected LayerMask m_LayerMask = 1 << LayerManager.Character;
[Tooltip("Should the state change only be applied to the character?")]
[SerializeField] protected bool m_RequireCharacter = true;
[Tooltip("Does the state require the character changing transforms?")]
[SerializeField] protected bool m_CharacterTransformChange;
[Tooltip("A set of AudioClips that can be played when the state is activated.")]
[SerializeField] protected AudioClipSet m_ActivateAudioClipSet = new AudioClipSet();
public AudioClipSet ActivateAudioClipSet { get { return m_ActivateAudioClipSet; } set { m_ActivateAudioClipSet = value; } }
private ScheduledEventBase m_ActivateStateEvent;
private List<GameObject> m_DeathDeactivations;
/// <summary>
/// Initialize the default values.
/// </summary>
private void Awake()
{
if (string.IsNullOrEmpty(m_StateName)) {
enabled = false;
}
}
/// <summary>
/// The other collider has entered the trigger.
/// </summary>
/// <param name="other">The collider which entered the trigger.</param>
private void OnTriggerEnter(Collider other)
{
if (!MathUtility.InLayerMask(other.gameObject.layer, m_LayerMask)) {
return;
}
StateBehavior stateBehavior;
if ((m_RequireCharacter && (stateBehavior = other.GetComponentInParent<Character.UltimateCharacterLocomotion>()) != null) ||
(!m_RequireCharacter && (stateBehavior = other.GetComponentInParent<StateBehavior>()) != null)) {
m_ActivateStateEvent = Scheduler.Schedule(m_Delay, ChangeState, stateBehavior.gameObject, true);
m_ActivateAudioClipSet.PlayAudioClip(null);
}
}
/// <summary>
/// Activates or deactivates the state on the specified GameObject.
/// </summary>
/// <param name="gameObject">The GameObject to activate the state on.</param>
/// <param name="activate">Should the state be activated?</param>
private void ChangeState(GameObject gameObject, bool activate)
{
StateManager.SetState(gameObject, m_StateName, activate);
if (m_CharacterTransformChange) {
EventHandler.ExecuteEvent(gameObject, "OnCharacterImmediateTransformChange", true);
}
m_ActivateStateEvent = null;
int index;
if (m_DeathDeactivations != null && (index = m_DeathDeactivations.IndexOf(gameObject)) > 0) {
m_DeathDeactivations.RemoveAt(index);
EventHandler.UnregisterEvent(gameObject, "OnRespawn", OnRespawn);
}
// The state can be disabled automatically.
if (activate && m_Duration > 0) {
Scheduler.Schedule(m_Duration, ChangeState, gameObject, false);
}
}
/// <summary>
/// The other collider has exited the trigger.
/// </summary>
/// <param name="other">The collider which exited the trigger.</param>
private void OnTriggerExit(Collider other)
{
if (m_Duration > 0 || !MathUtility.InLayerMask(other.gameObject.layer, m_LayerMask)) {
return;
}
StateBehavior stateBehavior;
if ((m_RequireCharacter && (stateBehavior = other.GetComponentInParent<Character.UltimateCharacterLocomotion>()) != null) ||
(!m_RequireCharacter && (stateBehavior = other.GetComponentInParent<StateBehavior>()) != null)) {
if (m_ActivateStateEvent != null && m_ActivateStateEvent.Active) {
Scheduler.Cancel(m_ActivateStateEvent);
m_ActivateStateEvent = null;
} else {
// The state shouldn't change when the object dies. It can be changed when the character respawns.
var health = other.gameObject.GetCachedParentComponent<Traits.Health>();
if (health != null && !health.IsAlive()) {
// When the character respawns the trigger enter/exit event may not fire. Register that the state should be deactivated so when the
// character respawns the state can then be disabled.
if (m_DeathDeactivations == null) {
m_DeathDeactivations = new List<GameObject>();
}
if (!m_DeathDeactivations.Contains(stateBehavior.gameObject)) {
m_DeathDeactivations.Add(stateBehavior.gameObject);
EventHandler.RegisterEvent(stateBehavior.gameObject, "OnRespawn", OnRespawn);
}
return;
}
StateManager.SetState(stateBehavior.gameObject, m_StateName, false);
if (m_CharacterTransformChange) {
EventHandler.ExecuteEvent(stateBehavior.gameObject, "OnCharacterImmediateTransformChange", true);
}
}
}
}
/// <summary>
/// The character has respawned.
/// </summary>
private void OnRespawn()
{
for (int i = m_DeathDeactivations.Count - 1; i > -1; --i) {
StateManager.SetState(m_DeathDeactivations[i].gameObject, m_StateName, false);
EventHandler.UnregisterEvent(m_DeathDeactivations[i].gameObject, "OnRespawn", OnRespawn);
}
m_DeathDeactivations.Clear();
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 64f2fa1f4fdfea445b980a7530be3231
timeCreated: 1512934167
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: