feat: Event & Teleporter
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Arena.Combat;
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
@@ -18,6 +19,7 @@ namespace Arena.Characters
|
||||
{
|
||||
public CharacterController Controller;
|
||||
public InputActionReference MoveAction;
|
||||
public InputActionReference InteractAction;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
@@ -37,8 +39,12 @@ namespace Arena.Characters
|
||||
[SerializeField] private StateContainer _state;
|
||||
[SerializeField] private Health _health;
|
||||
|
||||
public StateContainer State => _state;
|
||||
public Action OnInteract;
|
||||
|
||||
public StateContainer State => _state;
|
||||
public Health Health => _health;
|
||||
|
||||
private bool _isInteracting;
|
||||
private Vector2 _moveInput;
|
||||
private Camera _camera;
|
||||
|
||||
@@ -65,12 +71,16 @@ namespace Arena.Characters
|
||||
void OnEnable()
|
||||
{
|
||||
_references.MoveAction.action.Enable();
|
||||
_references.InteractAction.action.Enable();
|
||||
}
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
if (Instance == this)
|
||||
{
|
||||
_references.MoveAction.action.Disable();
|
||||
_references.InteractAction.action.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
@@ -88,20 +98,47 @@ namespace Arena.Characters
|
||||
|
||||
GetInputs();
|
||||
Move(t);
|
||||
Interact();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region PUBLIC METHODS
|
||||
|
||||
public void TakeDamage(int amount)
|
||||
{
|
||||
_health.TakeDamage(amount);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Teleports the player.
|
||||
/// </summary>
|
||||
/// <param name="pose">x, y, z: position. w: angle around the vertical axis, in degrees.</param>
|
||||
public void Teleport(Vector4 pose)
|
||||
{
|
||||
// An enabled CharacterController would overwrite the new position
|
||||
_references.Controller.enabled = false;
|
||||
|
||||
Vector3 position = new Vector3(pose.x, pose.y, pose.z);
|
||||
Quaternion rotation = Quaternion.Euler(0f, pose.w, 0f);
|
||||
transform.SetPositionAndRotation(position, rotation);
|
||||
|
||||
_references.Controller.enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Teleports the player to the position and heading of a target, such as a spawn point.
|
||||
/// </summary>
|
||||
public void Teleport(Transform target)
|
||||
{
|
||||
Vector3 position = target.position;
|
||||
Vector4 pose = new Vector4(position.x, position.y, position.z, target.eulerAngles.y);
|
||||
Teleport(pose);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
#region PRIVATE METHODS
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[ContextMenu("Debug/Take 25 Damage")]
|
||||
@@ -114,6 +151,7 @@ namespace Arena.Characters
|
||||
private void GetInputs()
|
||||
{
|
||||
_moveInput = _references.MoveAction.action.ReadValue<Vector2>();
|
||||
_isInteracting = _references.InteractAction.action.WasPressedThisFrame();
|
||||
}
|
||||
|
||||
private void Move(float t)
|
||||
@@ -126,6 +164,14 @@ namespace Arena.Characters
|
||||
_references.Controller.Move(velocity * t);
|
||||
}
|
||||
|
||||
private void Interact()
|
||||
{
|
||||
if (_isInteracting)
|
||||
{
|
||||
OnInteract?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 ToWorldDirection(Vector2 input)
|
||||
{
|
||||
// Only the camera rotation around the vertical axis is kept
|
||||
|
||||
@@ -1,16 +1,89 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
public class PlayerEvent : MonoBehaviour
|
||||
namespace Arena.Characters
|
||||
{
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
public class PlayerEvent : MonoBehaviour
|
||||
{
|
||||
public enum Trigger
|
||||
{
|
||||
OnPlayerEnter,
|
||||
OnPlayerInteract
|
||||
}
|
||||
|
||||
}
|
||||
[System.Serializable]
|
||||
public class Settings
|
||||
{
|
||||
[Tooltip("OnPlayerEnter: as soon as the player enters. OnPlayerInteract: when the player interacts inside the zone.")]
|
||||
public Trigger Trigger;
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
[SerializeField] private Settings _settings;
|
||||
[SerializeField] private UnityEvent _onPlayer;
|
||||
|
||||
private bool _isListening;
|
||||
|
||||
#region UNITY LIFECYCLES
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
// The object can be hidden while the player is inside
|
||||
StopListening();
|
||||
}
|
||||
|
||||
void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (!IsPlayer(other))
|
||||
return;
|
||||
|
||||
if (_settings.Trigger == Trigger.OnPlayerEnter)
|
||||
_onPlayer.Invoke();
|
||||
else
|
||||
StartListening();
|
||||
}
|
||||
|
||||
void OnTriggerExit(Collider other)
|
||||
{
|
||||
if (IsPlayer(other))
|
||||
StopListening();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private bool IsPlayer(Collider other)
|
||||
{
|
||||
return Player.Instance != null && other.gameObject == Player.Instance.gameObject;
|
||||
}
|
||||
|
||||
private void StartListening()
|
||||
{
|
||||
Player.Instance.OnInteract += HandleInteract;
|
||||
_isListening = true;
|
||||
}
|
||||
|
||||
private void StopListening()
|
||||
{
|
||||
if (!_isListening)
|
||||
return;
|
||||
|
||||
if (Player.Instance != null)
|
||||
Player.Instance.OnInteract -= HandleInteract;
|
||||
|
||||
_isListening = false;
|
||||
}
|
||||
|
||||
private void HandleInteract()
|
||||
{
|
||||
_onPlayer.Invoke();
|
||||
}
|
||||
|
||||
public void Teleport(Transform target)
|
||||
{
|
||||
if (!Player.Instance)
|
||||
return;
|
||||
|
||||
StopListening();
|
||||
Player.Instance.Teleport(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using UnityEngine;
|
||||
namespace Arena.Combat
|
||||
{
|
||||
/// <summary>
|
||||
/// Hit points of an entity. Serialized inside the component that owns it (Player, Enemy),
|
||||
/// Health points of an entity. Serialized inside the component that owns it (Player, Enemy),
|
||||
/// which calls Initialize in its Awake.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
@@ -12,19 +12,19 @@ namespace Arena.Combat
|
||||
{
|
||||
#region Fields
|
||||
|
||||
[Tooltip("Hit points when full.")]
|
||||
[Tooltip("Health points when full.")]
|
||||
[Min(1)] public int Max = 100;
|
||||
|
||||
[Tooltip("Current hit points, visible for debugging.")]
|
||||
[Tooltip("Current health points, visible for debugging.")]
|
||||
[SerializeField] private int _current;
|
||||
|
||||
/// <summary>
|
||||
/// Raised with the current and max hit points each time they change.
|
||||
/// Raised with the current and max health points each time they change.
|
||||
/// </summary>
|
||||
public event Action<int, int> OnChange;
|
||||
|
||||
/// <summary>
|
||||
/// Raised once, when the hit points reach zero.
|
||||
/// Raised once, when the health points reach zero.
|
||||
/// </summary>
|
||||
public event Action OnDied;
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Arena.Combat
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Fills the hit points. Called by the owner in its Awake.
|
||||
/// Fills the health points. Called by the owner in its Awake.
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -44,7 +44,7 @@ namespace Arena.Combat
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes hit points and raises OnDied the first time they reach zero.
|
||||
/// Removes health points and raises OnDied the first time they reach zero.
|
||||
/// </summary>
|
||||
public void TakeDamage(int amount)
|
||||
{
|
||||
@@ -58,7 +58,7 @@ namespace Arena.Combat
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives hit points back without exceeding Max. Has no effect once dead.
|
||||
/// Gives health points back without exceeding Max. Has no effect once dead.
|
||||
/// </summary>
|
||||
public void Heal(int amount)
|
||||
{
|
||||
|
||||
68
Assets/_Content/Scripts/Combat/Projectile.cs
Normal file
68
Assets/_Content/Scripts/Combat/Projectile.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Arena.Combat
|
||||
{
|
||||
/// <summary>
|
||||
/// Moves straight ahead and damages the first IDamageable it touches.
|
||||
/// Needs a trigger collider and a kinematic Rigidbody.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
public class Projectile : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Settings
|
||||
{
|
||||
[Tooltip("Speed, in meters per second.")]
|
||||
[Min(0f)] public float Speed = 20f;
|
||||
|
||||
[Tooltip("Time before an unused projectile is destroyed, in seconds.")]
|
||||
[Min(0.1f)] public float Lifetime = 2f;
|
||||
}
|
||||
|
||||
#region Fields
|
||||
|
||||
[SerializeField] private Settings _settings;
|
||||
|
||||
private int _damage;
|
||||
private GameObject _owner;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unity Lifecycle
|
||||
|
||||
private void Update()
|
||||
{
|
||||
transform.position += transform.forward * _settings.Speed * Time.deltaTime;
|
||||
}
|
||||
|
||||
private void OnTriggerEnter(Collider other)
|
||||
{
|
||||
// Ignore the shooter and the other trigger zones
|
||||
if (other.isTrigger || other.gameObject == _owner)
|
||||
return;
|
||||
|
||||
if (other.TryGetComponent(out IDamageable target))
|
||||
target.TakeDamage(_damage);
|
||||
|
||||
Destroy(gameObject);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Starts the projectile. Called right after Instantiate by whoever fires it.
|
||||
/// </summary>
|
||||
/// <param name="damage">Damage dealt to the IDamageable it touches.</param>
|
||||
/// <param name="owner">Shooter, ignored by the projectile.</param>
|
||||
public void Launch(int damage, GameObject owner)
|
||||
{
|
||||
_damage = damage;
|
||||
_owner = owner;
|
||||
Destroy(gameObject, _settings.Lifetime);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2
Assets/_Content/Scripts/Combat/Projectile.cs.meta
Normal file
2
Assets/_Content/Scripts/Combat/Projectile.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 875f6e6257f7027489dceee7bdfec3cb
|
||||
16
Assets/_Content/Scripts/Combat/ProjectileLauncher.cs
Normal file
16
Assets/_Content/Scripts/Combat/ProjectileLauncher.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class ProjectileLauncher : MonoBehaviour
|
||||
{
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
void Update()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bb53e2fd0884a746883755fca366194
|
||||
8
Assets/_Content/Scripts/Interactions.meta
Normal file
8
Assets/_Content/Scripts/Interactions.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88dab0d441585ab4cbae1aa9191deffe
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
Assets/_Content/Scripts/Interactions/HealEffect.cs
Normal file
34
Assets/_Content/Scripts/Interactions/HealEffect.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using UnityEngine;
|
||||
using Arena.Characters;
|
||||
|
||||
namespace Arena.Interactions
|
||||
{
|
||||
/// <summary>
|
||||
/// Heals the player. Apply is meant to be linked to a PlayerEvent.
|
||||
/// </summary>
|
||||
public class HealEffect : MonoBehaviour
|
||||
{
|
||||
[System.Serializable]
|
||||
public class Settings
|
||||
{
|
||||
[Tooltip("Hit points given to the player.")]
|
||||
[Min(1)] public int Amount = 25;
|
||||
}
|
||||
|
||||
#region Fields
|
||||
|
||||
[SerializeField] private Settings _settings;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
if (Player.Instance != null)
|
||||
Player.Instance.Health.Heal(_settings.Amount);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2
Assets/_Content/Scripts/Interactions/HealEffect.cs.meta
Normal file
2
Assets/_Content/Scripts/Interactions/HealEffect.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e30510fdd4a8b64da1115732d8ee972
|
||||
Reference in New Issue
Block a user