wip: OctoPlayerDriver & New Triggers for OctoWorldEvent & Critical Path V1
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
using OctoCore.Inputs;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace OctoCore.Components
|
||||
{
|
||||
@@ -21,13 +20,7 @@ namespace OctoCore.Components
|
||||
public bool IsRunning;
|
||||
public float Yaw;
|
||||
public float Pitch;
|
||||
|
||||
[Space(10)]
|
||||
|
||||
public bool IsDriven;
|
||||
[Range(0, 1)] public float DriventIntensity;
|
||||
public Transform DriveTarget;
|
||||
public Transform DriveLookAt;
|
||||
public OctoPlayerDriver Driver;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
@@ -68,7 +61,7 @@ namespace OctoCore.Components
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
private class FirstPerson
|
||||
public class FirstPerson
|
||||
{
|
||||
[Header("References")]
|
||||
|
||||
@@ -110,6 +103,7 @@ namespace OctoCore.Components
|
||||
|
||||
public bool IsPaused => State.IsPaused;
|
||||
public float Radius => _references.Controller ? _references.Controller.radius : 0;
|
||||
public FirstPerson _FirstPerson => _firstPerson;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
@@ -143,6 +137,17 @@ namespace OctoCore.Components
|
||||
HandleMovement();
|
||||
}
|
||||
|
||||
public void Teleport(string positionAndYaw)
|
||||
{
|
||||
string[] s = positionAndYaw.Replace(" ", string.Empty).Split(',');
|
||||
float x = float.Parse(s[0], CultureInfo.InvariantCulture.NumberFormat);
|
||||
float y = float.Parse(s[1], CultureInfo.InvariantCulture.NumberFormat);
|
||||
float z = float.Parse(s[2], CultureInfo.InvariantCulture.NumberFormat);
|
||||
float a = float.Parse(s[3], CultureInfo.InvariantCulture.NumberFormat);
|
||||
|
||||
Teleport(new Vector3(x, y, z), Quaternion.Euler(0, a, 0));
|
||||
}
|
||||
|
||||
public void Teleport(Vector3 position, Quaternion rotation)
|
||||
{
|
||||
State.IsPaused = _jumpInput = _jumpping = _runInput = false;
|
||||
@@ -169,20 +174,9 @@ namespace OctoCore.Components
|
||||
if (OctoCore.Instance)
|
||||
OctoCore.Instance.UI.SetCursor();
|
||||
}
|
||||
|
||||
public void Drive(Transform target, Transform lookAt)
|
||||
{
|
||||
State.DriveTarget = target;
|
||||
State.DriveLookAt = lookAt;
|
||||
State.IsDriven = true;
|
||||
}
|
||||
|
||||
public void ReleaseDrive()
|
||||
{
|
||||
State.IsDriven = false;
|
||||
State.DriveTarget = null;
|
||||
State.DriveLookAt = null;
|
||||
}
|
||||
|
||||
public void Drive(OctoPlayerDriver driver) { State.Driver = driver; }
|
||||
public void ReleaseDrive() { State.Driver = null; }
|
||||
|
||||
public void SetYaw(float yaw) { State.Yaw = yaw; }
|
||||
public void SetPitch(float pitch) { State.Yaw = pitch; }
|
||||
@@ -259,21 +253,8 @@ namespace OctoCore.Components
|
||||
move = camRight * move.x + camForward * move.z;
|
||||
}
|
||||
|
||||
if (State.IsDriven)
|
||||
{
|
||||
State.DriventIntensity = Mathf.Clamp01(State.DriventIntensity + move.magnitude * t * 1);
|
||||
|
||||
Vector3 driveDir = State.DriveTarget.position - transform.position;
|
||||
|
||||
float driveAtten = 1 - Mathf.Clamp01(driveDir.magnitude);
|
||||
|
||||
Vector3 driveMove = driveDir;
|
||||
driveMove.y = 0;
|
||||
driveMove = driveMove.normalized * move.magnitude;
|
||||
driveMove = Vector3.Lerp(driveMove, Vector3.zero, driveAtten);
|
||||
|
||||
move = Vector3.Lerp(move, driveMove, State.DriventIntensity);
|
||||
}
|
||||
if (State.Driver)
|
||||
State.Driver.ApplyMoveInfluence(move, move.magnitude * t, out move);
|
||||
|
||||
float deltaSmooth = t * (accelerating ? 30 : 15);
|
||||
_moveDirection = Vector3.Lerp(_moveDirection, move, deltaSmooth * 1.5f);
|
||||
@@ -349,25 +330,8 @@ namespace OctoCore.Components
|
||||
State.Yaw += mouseX;
|
||||
State.Pitch = Mathf.Clamp(State.Pitch + mouseY, _firstPerson.MinPitch, _firstPerson.MaxPitch);
|
||||
|
||||
if (State.IsDriven)
|
||||
{
|
||||
Vector3 lookAt = State.DriveLookAt.position - _firstPerson.Camera.transform.position;
|
||||
lookAt = lookAt.normalized;
|
||||
|
||||
Vector3 playerPos2D = transform.position;
|
||||
Vector3 targetPos2D = State.DriveTarget.position;
|
||||
playerPos2D.y = targetPos2D.y = 0;
|
||||
|
||||
float yaw = Mathf.Atan2(lookAt.x, lookAt.z) * Mathf.Rad2Deg;
|
||||
float pitch = Mathf.Asin(-lookAt.y) * Mathf.Rad2Deg;
|
||||
|
||||
float lerp = 1 - Mathf.Clamp01(Vector3.Distance(playerPos2D, targetPos2D) / 2);
|
||||
float yawRange = Mathf.Lerp(180, 0, lerp);
|
||||
float pitchRange = Mathf.Lerp(90, 0, lerp);
|
||||
|
||||
State.Yaw = yaw + Mathf.Clamp(Mathf.DeltaAngle(yaw, State.Yaw), -yawRange, yawRange);
|
||||
State.Pitch = pitch + Mathf.Clamp(Mathf.DeltaAngle(pitch, State.Pitch), -pitchRange, pitchRange);
|
||||
}
|
||||
if (State.Driver)
|
||||
State.Driver.ApplyLookInfluence(State.Yaw, State.Pitch, Time.deltaTime, out State.Yaw, out State.Pitch);
|
||||
|
||||
Quaternion YawRot = Quaternion.Euler(0, State.Yaw, 0);
|
||||
Quaternion PitchRot = Quaternion.Euler(State.Pitch, 0, 0);
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace OctoCore.Components
|
||||
{
|
||||
public class OctoPlayerDriver : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public class State
|
||||
{
|
||||
[Range(0, 1)] public float MoveInfluence;
|
||||
[Range(0, 1)] public float LookInfluence;
|
||||
[Range(0, 180)] public float YawRange = 180;
|
||||
[Range(0, 90)] public float PitchRange = 90;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class References
|
||||
{
|
||||
@@ -11,21 +21,145 @@ namespace OctoCore.Components
|
||||
public Transform LookAt;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class Settings
|
||||
{
|
||||
[Min(.1f)] public float DriveStrength = 1;
|
||||
[Range(0, 1)] public float LookAtStrength = 1;
|
||||
[Range(.1f, 3)] public float LookAtSmooth = 1;
|
||||
[Range(0, 1)] public float LookAtPoint = .5f;
|
||||
public AnimationCurve DriveCurve = AnimationCurve.EaseInOut(0, 0, 1 , 1);
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class Events
|
||||
{
|
||||
public UnityEvent OnDrive;
|
||||
public UnityEvent OnFullInfluence;
|
||||
public UnityEvent OnRelease;
|
||||
}
|
||||
|
||||
[SerializeField, OctoReadOnly] private State _state;
|
||||
[SerializeField] private References _references;
|
||||
[SerializeField] private Settings _settings;
|
||||
[SerializeField] private Events _events;
|
||||
|
||||
public References _References => _references;
|
||||
public State _State => _state;
|
||||
|
||||
private bool _fullInfluenceReached;
|
||||
|
||||
void OnDrawGizmos()
|
||||
{
|
||||
UnityEditor.Handles.color = Color.cyan;
|
||||
|
||||
const float size = .3f;
|
||||
|
||||
if (_references.Target)
|
||||
{
|
||||
UnityEditor.Handles.matrix = _references.Target.localToWorldMatrix;
|
||||
UnityEditor.Handles.DrawLine(Vector3.forward * size, Vector3.back * size);
|
||||
UnityEditor.Handles.DrawLine(Vector3.left * size, Vector3.right * size);
|
||||
}
|
||||
|
||||
if (_references.LookAt)
|
||||
{
|
||||
UnityEditor.Handles.matrix = _references.LookAt.localToWorldMatrix;
|
||||
UnityEditor.Handles.DrawWireCube(Vector3.zero, Vector3.one * size * .75f);
|
||||
}
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void DrivePlayer()
|
||||
{
|
||||
OctoPlayer.Instance.Drive(_references.Target, _references.LookAt);
|
||||
_fullInfluenceReached = false;
|
||||
|
||||
_state.YawRange = 180;
|
||||
_state.PitchRange = 90;
|
||||
|
||||
OctoPlayer.Instance.Drive(this);
|
||||
|
||||
_events.OnDrive?.Invoke();
|
||||
}
|
||||
|
||||
public void ReleasePlayer()
|
||||
{
|
||||
OctoPlayer.Instance.ReleaseDrive();
|
||||
|
||||
_state.YawRange = 180;
|
||||
_state.PitchRange = 90;
|
||||
|
||||
_events.OnRelease?.Invoke();
|
||||
}
|
||||
|
||||
public void ApplyMoveInfluence(Vector3 inMove, float delta, out Vector3 outMove)
|
||||
{
|
||||
if (!OctoPlayer.Instance || !OctoPlayer.Instance._FirstPerson.Camera)
|
||||
{
|
||||
outMove = inMove;
|
||||
return;
|
||||
}
|
||||
|
||||
_state.MoveInfluence = Mathf.Clamp01(_state.MoveInfluence + delta * _settings.DriveStrength);
|
||||
float moveInfluence = _settings.DriveCurve.Evaluate(_state.MoveInfluence);
|
||||
|
||||
if (!_fullInfluenceReached && _state.MoveInfluence >= 1)
|
||||
{
|
||||
_events.OnFullInfluence?.Invoke();
|
||||
_fullInfluenceReached = true;
|
||||
}
|
||||
|
||||
Vector3 offset = _references.Target.position - OctoPlayer.Instance.transform.position;
|
||||
Vector3 offset2D = offset;
|
||||
offset2D.y = 0;
|
||||
|
||||
float driveAtten = Mathf.InverseLerp(1, .1f, offset2D.magnitude);
|
||||
|
||||
Vector3 driveMove = offset;
|
||||
driveMove.y = 0;
|
||||
driveMove = driveMove.normalized * inMove.magnitude;
|
||||
driveMove = Vector3.Lerp(driveMove, Vector3.zero, driveAtten);
|
||||
|
||||
outMove = Vector3.Lerp(inMove, driveMove, moveInfluence);
|
||||
}
|
||||
|
||||
public void ApplyLookInfluence(float inYaw, float inPitch, float deltaTime, out float outYaw, out float outPitch)
|
||||
{
|
||||
if (!OctoPlayer.Instance || !OctoPlayer.Instance._FirstPerson.Camera)
|
||||
{
|
||||
outYaw = inYaw;
|
||||
outPitch = inPitch;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 lookAt = _references.LookAt.position - OctoPlayer.Instance._FirstPerson.Camera.transform.position;
|
||||
lookAt = lookAt.normalized;
|
||||
|
||||
Vector3 offset = _references.Target.position - OctoPlayer.Instance.transform.position;
|
||||
Vector3 offset2D = offset;
|
||||
offset2D.y = 0;
|
||||
|
||||
float lookAtYaw = Mathf.Atan2(lookAt.x, lookAt.z) * Mathf.Rad2Deg;
|
||||
float lookAtPitch = Mathf.Asin(-lookAt.y) * Mathf.Rad2Deg;
|
||||
|
||||
_state.LookInfluence = Mathf.InverseLerp(_settings.LookAtPoint, 1, _state.MoveInfluence) * _settings.LookAtStrength;
|
||||
float lookInfluence = _settings.DriveCurve.Evaluate(_state.LookInfluence);
|
||||
|
||||
float t = deltaTime * _settings.LookAtSmooth;
|
||||
_state.YawRange = Mathf.Lerp(_state.YawRange, Mathf.Lerp(180, 0, lookInfluence), t);
|
||||
_state.PitchRange = Mathf.Lerp(_state.PitchRange, Mathf.Lerp(90, 0, lookInfluence), t);
|
||||
|
||||
outYaw = lookAtYaw + Mathf.Clamp(Mathf.DeltaAngle(lookAtYaw, inYaw), -_state.YawRange, _state.YawRange);
|
||||
outPitch = lookAtPitch + Mathf.Clamp(Mathf.DeltaAngle(lookAtPitch, inPitch), -_state.PitchRange, _state.PitchRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace OctoCore.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Procedural animation supporting translation, rotation, and scaling.
|
||||
/// Supports multiple animation types (Constant, PingPong, Loop, Single) with easing.
|
||||
/// </summary>
|
||||
[DefaultExecutionOrder(0)]
|
||||
public class OctoProceduralAnimator : MonoBehaviour
|
||||
{
|
||||
public enum AnimationSpace
|
||||
{
|
||||
Local,
|
||||
World
|
||||
}
|
||||
|
||||
public enum AnimationType
|
||||
{
|
||||
Constant, // Continuous movement without return
|
||||
PingPong, // Back and forth (A → B → A)
|
||||
Loop, // Loop with teleport (A → B, teleport to A)
|
||||
Single // Play once then stop
|
||||
}
|
||||
|
||||
public enum AnimationBalance
|
||||
{
|
||||
Centered,
|
||||
Forward,
|
||||
Backward
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class Settings
|
||||
{
|
||||
[Tooltip("Animation space")]
|
||||
public AnimationSpace Space = AnimationSpace.Local;
|
||||
|
||||
[Tooltip("Type of animation loop")]
|
||||
public AnimationType Type = AnimationType.PingPong;
|
||||
|
||||
[Tooltip("TCenter of animation")]
|
||||
public AnimationBalance Balance = AnimationBalance.Centered;
|
||||
|
||||
[Tooltip("Custom animation curve")]
|
||||
public AnimationCurve Curve = AnimationCurve.EaseInOut(0, 0, 1, 1);
|
||||
|
||||
[Tooltip("Play on Awake")]
|
||||
public bool PlayOnAwake = true;
|
||||
|
||||
[Tooltip("Animation speed multiplier")]
|
||||
public float Duration = 4;
|
||||
|
||||
[Tooltip("Time offset to desynchronize multiple animations")]
|
||||
public float TimeOffset = 0;
|
||||
|
||||
[Tooltip("Translation amount in local space (meters)")]
|
||||
public Vector3 Translation = Vector3.zero;
|
||||
|
||||
[Tooltip("Rotation amount in degrees")]
|
||||
public Vector3 Rotation = Vector3.zero;
|
||||
|
||||
[Tooltip("Scale amount relative to initial scale")]
|
||||
public Vector3 Scale = Vector3.zero;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class State
|
||||
{
|
||||
[Tooltip("Current animation time")]
|
||||
public float CurrentTime;
|
||||
|
||||
[Tooltip("Is animation finished? (Single only)")]
|
||||
public bool IsFinished;
|
||||
|
||||
[HideInInspector] public Vector3 InitialPosition;
|
||||
[HideInInspector] public Quaternion InitialRotation;
|
||||
[HideInInspector] public Vector3 InitialScale;
|
||||
}
|
||||
|
||||
[SerializeField] private Settings _settings;
|
||||
[SerializeField] private State _state;
|
||||
|
||||
private bool _started;
|
||||
private float _timeOffset;
|
||||
|
||||
void OnDrawGizmosSelected()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
_timeOffset = Time.timeSinceLevelLoad;
|
||||
_started = _settings.PlayOnAwake;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
_state.InitialPosition = transform.localPosition;
|
||||
_state.InitialRotation = transform.localRotation;
|
||||
_state.InitialScale = transform.localScale;
|
||||
_state.CurrentTime = _settings.TimeOffset;
|
||||
_state.IsFinished = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_state.IsFinished || !_started)
|
||||
return;
|
||||
|
||||
_state.CurrentTime = Time.timeSinceLevelLoad + _settings.TimeOffset * _settings.Duration - _timeOffset;
|
||||
|
||||
float normalizedTime = GetNormalizedTime(_state.CurrentTime);
|
||||
float easedTime = ApplyEasing(normalizedTime);
|
||||
float balanceTime = ApplyBalance(easedTime);
|
||||
|
||||
ApplyTranslation(balanceTime);
|
||||
ApplyRotation(balanceTime);
|
||||
ApplyScale(balanceTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play animation from initial state.
|
||||
/// </summary>
|
||||
[ContextMenu("PlayAnimation")]
|
||||
public void PlayAnimation()
|
||||
{
|
||||
ResetAnimation();
|
||||
_timeOffset = Time.timeSinceLevelLoad;
|
||||
_started = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets animation to initial state.
|
||||
/// </summary>
|
||||
public void ResetAnimation()
|
||||
{
|
||||
_state.CurrentTime = _settings.TimeOffset;
|
||||
_state.IsFinished = false;
|
||||
transform.localPosition = _state.InitialPosition;
|
||||
transform.localRotation = _state.InitialRotation;
|
||||
transform.localScale = _state.InitialScale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses or resumes animation.
|
||||
/// </summary>
|
||||
public void SetPaused(bool paused)
|
||||
{
|
||||
enabled = !paused;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets normalized time [0,1] based on animation type.
|
||||
/// </summary>
|
||||
private float GetNormalizedTime(float time)
|
||||
{
|
||||
if (_settings.Duration == 0)
|
||||
return 0;
|
||||
|
||||
switch (_settings.Type)
|
||||
{
|
||||
case AnimationType.Constant:
|
||||
return time / _settings.Duration;
|
||||
|
||||
case AnimationType.PingPong:
|
||||
return Mathf.PingPong(time / _settings.Duration, 1);
|
||||
|
||||
case AnimationType.Loop:
|
||||
return Mathf.Repeat(time / _settings.Duration, 1f);
|
||||
|
||||
case AnimationType.Single:
|
||||
if (time >= _settings.Duration)
|
||||
{
|
||||
_state.IsFinished = true;
|
||||
return 1;
|
||||
}
|
||||
return time / _settings.Duration;
|
||||
|
||||
default:
|
||||
return 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private float ApplyBalance(float time)
|
||||
{
|
||||
switch (_settings.Balance)
|
||||
{
|
||||
case AnimationBalance.Centered:
|
||||
return time - .5f;
|
||||
|
||||
case AnimationBalance.Backward:
|
||||
return -time;
|
||||
|
||||
default:
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies easing function to normalized time.
|
||||
/// </summary>
|
||||
private float ApplyEasing(float time)
|
||||
{
|
||||
switch (_settings.Type)
|
||||
{
|
||||
default:
|
||||
return _settings.Curve.Evaluate(time); ;
|
||||
|
||||
case AnimationType.Constant:
|
||||
return time;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies translation based on eased time.
|
||||
/// </summary>
|
||||
private void ApplyTranslation(float time)
|
||||
{
|
||||
if (_settings.Translation == Vector3.zero)
|
||||
return;
|
||||
|
||||
Vector3 offset = _settings.Translation * time;
|
||||
if (_settings.Space == AnimationSpace.Local)
|
||||
offset = _state.InitialRotation * offset;
|
||||
|
||||
transform.localPosition = _state.InitialPosition + offset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies rotation based on eased time.
|
||||
/// </summary>
|
||||
private void ApplyRotation(float time)
|
||||
{
|
||||
if (_settings.Rotation == Vector3.zero)
|
||||
return;
|
||||
|
||||
Vector3 rotation = _settings.Rotation * time;
|
||||
Quaternion rotationOffset = Quaternion.Euler(rotation);
|
||||
if (_settings.Space == AnimationSpace.Local)
|
||||
rotationOffset = _state.InitialRotation * rotationOffset;
|
||||
else
|
||||
rotationOffset = rotationOffset * _state.InitialRotation;
|
||||
|
||||
transform.localRotation = rotationOffset;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies scale based on eased time.
|
||||
/// </summary>
|
||||
private void ApplyScale(float t)
|
||||
{
|
||||
if (_settings.Scale == Vector3.zero)
|
||||
return;
|
||||
|
||||
Vector3 scale = Vector3.one + _settings.Scale * t;
|
||||
transform.localScale = Vector3.Scale(_state.InitialScale, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0097589062bdfad4a96fa20e8effbe2f
|
||||
@@ -1,32 +1,51 @@
|
||||
using OctoCore.Inputs;
|
||||
using OctoCore.Utilities;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace OctoCore.Components
|
||||
{
|
||||
[DefaultExecutionOrder(100)]
|
||||
public class OctoWorldEvent : MonoBehaviour
|
||||
{
|
||||
public enum Conditions { None, Player, Tags, Targets }
|
||||
|
||||
//[SerializeField] private InputActionAsset _actionAsset;
|
||||
//[SerializeField] private InputActionReference _actionReference;
|
||||
//[Space(10)]
|
||||
public enum ExecutionModes
|
||||
{
|
||||
Immediate,
|
||||
Delayed,
|
||||
ImmediateAndDelayed
|
||||
}
|
||||
|
||||
[SerializeField] private Utilities.OctoEvents _trigger;
|
||||
[SerializeField] private OctoActions _action;
|
||||
[SerializeField] private Conditions _condition;
|
||||
|
||||
[SerializeField] private string[] _tags;
|
||||
[SerializeField] private GameObject[] _targets;
|
||||
|
||||
[Range(0f, 1f)]
|
||||
[SerializeField] private float _tolerance = .5f;
|
||||
|
||||
[SerializeField] private float _duration = 1f;
|
||||
|
||||
[SerializeField] private ExecutionModes _execution;
|
||||
[SerializeField, Min(0f)] private float _delay = 1f;
|
||||
|
||||
[SerializeField] private UnityEvent _onTrigger;
|
||||
[SerializeField] private UnityEvent _onTriggerDelayed;
|
||||
|
||||
private bool _playerInside;
|
||||
private bool[] _tagsInside;
|
||||
private bool[] _targetsInside;
|
||||
|
||||
private float _lookAtTimer;
|
||||
private bool _lookAtTriggered;
|
||||
|
||||
private Coroutine _delayedCoroutine;
|
||||
|
||||
private const string PlayerTag = "Player";
|
||||
|
||||
void Awake()
|
||||
@@ -37,11 +56,11 @@ namespace OctoCore.Components
|
||||
switch (_trigger)
|
||||
{
|
||||
case Utilities.OctoEvents.Awake:
|
||||
SetTrigger();
|
||||
ExecuteTrigger();
|
||||
break;
|
||||
|
||||
case Utilities.OctoEvents.Action:
|
||||
OctoCore.Instance.Inputs.SubscribeAction(_action, SetTrigger);
|
||||
OctoCore.Instance.Inputs.SubscribeAction(_action, ExecuteTrigger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -49,40 +68,42 @@ namespace OctoCore.Components
|
||||
void OnEnable()
|
||||
{
|
||||
if (_trigger == Utilities.OctoEvents.OnEnable)
|
||||
SetTrigger();
|
||||
ExecuteTrigger();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (_trigger == Utilities.OctoEvents.Start)
|
||||
SetTrigger();
|
||||
ExecuteTrigger();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
if (_trigger == Utilities.OctoEvents.Update)
|
||||
SetTrigger();
|
||||
ExecuteTrigger();
|
||||
|
||||
if (_trigger == Utilities.OctoEvents.OnPlayerLookAt)
|
||||
UpdatePlayerLookAt();
|
||||
}
|
||||
|
||||
void LateUpdate()
|
||||
{
|
||||
if (_trigger == Utilities.OctoEvents.LateUpdate)
|
||||
SetTrigger();
|
||||
ExecuteTrigger();
|
||||
}
|
||||
|
||||
void FixedUpdate()
|
||||
{
|
||||
if (_trigger == Utilities.OctoEvents.FixedUpdate)
|
||||
SetTrigger();
|
||||
ExecuteTrigger();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (_trigger == Utilities.OctoEvents.OnDisable)
|
||||
SetTrigger();
|
||||
ExecuteTrigger();
|
||||
|
||||
_playerInside = false;
|
||||
_tagsInside = new bool[_tags.Length];
|
||||
_targetsInside = new bool[_targets.Length];
|
||||
ResetState();
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
@@ -90,49 +111,53 @@ namespace OctoCore.Components
|
||||
switch (_trigger)
|
||||
{
|
||||
case Utilities.OctoEvents.OnDestroy:
|
||||
SetTrigger();
|
||||
ExecuteTrigger();
|
||||
break;
|
||||
|
||||
case Utilities.OctoEvents.Action:
|
||||
OctoCore.Instance?.Inputs.UnsubscribeAction(_action, SetTrigger);
|
||||
OctoCore.Instance?.Inputs.UnsubscribeAction(_action, ExecuteTrigger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnTriggerEnter(Collider col)
|
||||
{
|
||||
if (_trigger == Utilities.OctoEvents.OnTriggerEnter)
|
||||
{
|
||||
EvaluateTrigger(col);
|
||||
SetTrigger();
|
||||
}
|
||||
if (_trigger != Utilities.OctoEvents.OnTriggerEnter)
|
||||
return;
|
||||
|
||||
EvaluateTrigger(col);
|
||||
TriggerWithCondition();
|
||||
}
|
||||
|
||||
void OnTriggerStay(Collider col)
|
||||
{
|
||||
if (_trigger != Utilities.OctoEvents.OnTriggerStay)
|
||||
return;
|
||||
|
||||
EvaluateTrigger(col);
|
||||
if (_trigger == Utilities.OctoEvents.OnTriggerStay)
|
||||
SetTrigger();
|
||||
TriggerWithCondition();
|
||||
}
|
||||
|
||||
void OnTriggerExit(Collider col)
|
||||
{
|
||||
if (_trigger == Utilities.OctoEvents.OnTriggerExit)
|
||||
SetTrigger();
|
||||
if (_trigger != Utilities.OctoEvents.OnTriggerExit)
|
||||
return;
|
||||
|
||||
TriggerWithCondition();
|
||||
EvaluateTrigger(col, false);
|
||||
}
|
||||
|
||||
public void SetTrigger()
|
||||
void TriggerWithCondition()
|
||||
{
|
||||
switch (_condition)
|
||||
{
|
||||
case Conditions.None:
|
||||
_onTrigger?.Invoke();
|
||||
ExecuteTrigger();
|
||||
break;
|
||||
|
||||
case Conditions.Player:
|
||||
if (_playerInside)
|
||||
_onTrigger?.Invoke();
|
||||
ExecuteTrigger();
|
||||
break;
|
||||
|
||||
case Conditions.Tags:
|
||||
@@ -140,7 +165,7 @@ namespace OctoCore.Components
|
||||
{
|
||||
if (_tagsInside[i])
|
||||
{
|
||||
_onTrigger?.Invoke();
|
||||
ExecuteTrigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -151,7 +176,7 @@ namespace OctoCore.Components
|
||||
{
|
||||
if (_targetsInside[i])
|
||||
{
|
||||
_onTrigger?.Invoke();
|
||||
ExecuteTrigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -159,43 +184,122 @@ namespace OctoCore.Components
|
||||
}
|
||||
}
|
||||
|
||||
void ExecuteTrigger()
|
||||
{
|
||||
switch (_execution)
|
||||
{
|
||||
case ExecutionModes.Immediate:
|
||||
_onTrigger?.Invoke();
|
||||
break;
|
||||
|
||||
case ExecutionModes.Delayed:
|
||||
StartDelayedTrigger();
|
||||
break;
|
||||
|
||||
case ExecutionModes.ImmediateAndDelayed:
|
||||
_onTrigger?.Invoke();
|
||||
StartDelayedTrigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void StartDelayedTrigger()
|
||||
{
|
||||
if (_delayedCoroutine != null)
|
||||
StopCoroutine(_delayedCoroutine);
|
||||
|
||||
_delayedCoroutine = StartCoroutine(DelayedTrigger());
|
||||
}
|
||||
|
||||
IEnumerator DelayedTrigger()
|
||||
{
|
||||
yield return new WaitForSeconds(_delay);
|
||||
|
||||
_onTriggerDelayed?.Invoke();
|
||||
_delayedCoroutine = null;
|
||||
}
|
||||
|
||||
void UpdatePlayerLookAt()
|
||||
{
|
||||
Camera cam = Camera.main;
|
||||
|
||||
if (cam == null)
|
||||
return;
|
||||
|
||||
Vector3 viewportPosition = cam.WorldToViewportPoint(transform.position);
|
||||
bool isInFront = viewportPosition.z > 0f;
|
||||
|
||||
Vector2 screenPosition = new Vector2(
|
||||
viewportPosition.x * Screen.width,
|
||||
viewportPosition.y * Screen.height
|
||||
);
|
||||
|
||||
Vector2 screenCenter = new Vector2(
|
||||
Screen.width * .5f,
|
||||
Screen.height * .5f
|
||||
);
|
||||
|
||||
float radius = Mathf.Min(Screen.width, Screen.height) * _tolerance * .5f;
|
||||
bool isInsideCircle = Vector2.Distance(screenPosition, screenCenter) <= radius;
|
||||
|
||||
if (isInFront && isInsideCircle)
|
||||
{
|
||||
_lookAtTimer += Time.deltaTime;
|
||||
|
||||
if (!_lookAtTriggered && _lookAtTimer >= _duration)
|
||||
{
|
||||
_lookAtTriggered = true;
|
||||
ExecuteTrigger();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_lookAtTimer = 0f;
|
||||
_lookAtTriggered = false;
|
||||
}
|
||||
}
|
||||
|
||||
void EvaluateTrigger(Collider col, bool inside = true)
|
||||
{
|
||||
switch (_condition)
|
||||
{
|
||||
case Conditions.Player:
|
||||
if (col.tag == PlayerTag)
|
||||
if (col.CompareTag(PlayerTag))
|
||||
_playerInside = inside;
|
||||
break;
|
||||
|
||||
case Conditions.Tags:
|
||||
if (_tags.Contains(col.tag))
|
||||
for (int i = 0; i < _tags.Length; i++)
|
||||
{
|
||||
for (int i = 0; i < _tags.Length; i++)
|
||||
{
|
||||
bool tagMatch = col.tag == _tags[i];
|
||||
if (tagMatch)
|
||||
{
|
||||
_tagsInside[i] = inside ? tagMatch : !tagMatch;
|
||||
}
|
||||
}
|
||||
if (col.CompareTag(_tags[i]))
|
||||
_tagsInside[i] = inside;
|
||||
}
|
||||
break;
|
||||
|
||||
case Conditions.Targets:
|
||||
if (_targets.Contains(col.gameObject))
|
||||
for (int i = 0; i < _targets.Length; i++)
|
||||
{
|
||||
for (int i = 0; i < _targets.Length; i++)
|
||||
{
|
||||
bool targetMatch = col.gameObject == _targets[i];
|
||||
if (targetMatch)
|
||||
{
|
||||
_targetsInside[i] = inside ? targetMatch : !targetMatch;
|
||||
}
|
||||
}
|
||||
if (col.gameObject == _targets[i])
|
||||
_targetsInside[i] = inside;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ResetState()
|
||||
{
|
||||
_playerInside = false;
|
||||
_tagsInside = new bool[_tags.Length];
|
||||
_targetsInside = new bool[_targets.Length];
|
||||
|
||||
_lookAtTimer = 0f;
|
||||
_lookAtTriggered = false;
|
||||
|
||||
if (_delayedCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_delayedCoroutine);
|
||||
_delayedCoroutine = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using OctoCore.Utilities;
|
||||
using UnityEditor;
|
||||
|
||||
namespace OctoCore.Components.Editor
|
||||
{
|
||||
[CustomEditor(typeof(OctoWorldEvent))]
|
||||
public class OctoWorldEventEditor : UnityEditor.Editor
|
||||
{
|
||||
SerializedProperty _trigger;
|
||||
SerializedProperty _action;
|
||||
SerializedProperty _condition;
|
||||
SerializedProperty _tags;
|
||||
SerializedProperty _targets;
|
||||
SerializedProperty _tolerance;
|
||||
SerializedProperty _duration;
|
||||
SerializedProperty _execution;
|
||||
SerializedProperty _delay;
|
||||
SerializedProperty _onTrigger;
|
||||
SerializedProperty _onTriggerDelayed;
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
_trigger = serializedObject.FindProperty("_trigger");
|
||||
_action = serializedObject.FindProperty("_action");
|
||||
_condition = serializedObject.FindProperty("_condition");
|
||||
_tags = serializedObject.FindProperty("_tags");
|
||||
_targets = serializedObject.FindProperty("_targets");
|
||||
_tolerance = serializedObject.FindProperty("_tolerance");
|
||||
_duration = serializedObject.FindProperty("_duration");
|
||||
_execution = serializedObject.FindProperty("_execution");
|
||||
_delay = serializedObject.FindProperty("_delay");
|
||||
_onTrigger = serializedObject.FindProperty("_onTrigger");
|
||||
_onTriggerDelayed = serializedObject.FindProperty("_onTriggerDelayed");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.PropertyField(_trigger);
|
||||
|
||||
var trigger = (Utilities.OctoEvents)_trigger.enumValueIndex;
|
||||
|
||||
if (trigger == Utilities.OctoEvents.Action)
|
||||
EditorGUILayout.PropertyField(_action);
|
||||
|
||||
if (trigger == Utilities.OctoEvents.OnPlayerLookAt)
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.PropertyField(_tolerance);
|
||||
EditorGUILayout.PropertyField(_duration);
|
||||
}
|
||||
|
||||
if (UsesTriggerCondition(trigger))
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.PropertyField(_condition);
|
||||
|
||||
var condition = (OctoWorldEvent.Conditions)_condition.enumValueIndex;
|
||||
|
||||
if (condition == OctoWorldEvent.Conditions.Tags)
|
||||
EditorGUILayout.PropertyField(_tags, true);
|
||||
|
||||
if (condition == OctoWorldEvent.Conditions.Targets)
|
||||
EditorGUILayout.PropertyField(_targets, true);
|
||||
}
|
||||
|
||||
DrawExecutionEvents();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
void DrawExecutionEvents()
|
||||
{
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.PropertyField(_execution);
|
||||
|
||||
var execution = (OctoWorldEvent.ExecutionModes)_execution.enumValueIndex;
|
||||
|
||||
if (execution == OctoWorldEvent.ExecutionModes.Immediate ||
|
||||
execution == OctoWorldEvent.ExecutionModes.ImmediateAndDelayed)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_onTrigger);
|
||||
}
|
||||
|
||||
if (execution == OctoWorldEvent.ExecutionModes.Delayed ||
|
||||
execution == OctoWorldEvent.ExecutionModes.ImmediateAndDelayed)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_delay);
|
||||
EditorGUILayout.PropertyField(_onTriggerDelayed);
|
||||
}
|
||||
}
|
||||
|
||||
bool UsesTriggerCondition(Utilities.OctoEvents trigger)
|
||||
{
|
||||
return trigger == Utilities.OctoEvents.OnTriggerEnter ||
|
||||
trigger == Utilities.OctoEvents.OnTriggerStay ||
|
||||
trigger == Utilities.OctoEvents.OnTriggerExit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe9cee260bac87a4b80f9bbb38b71211
|
||||
Reference in New Issue
Block a user