299 lines
7.9 KiB
C#
299 lines
7.9 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
|
|
[RequireComponent(typeof(Rigidbody2D))]
|
|
[RequireComponent(typeof(BoxCollider2D))]
|
|
public class PlayerPlatformerController : MonoBehaviour
|
|
{
|
|
[SerializeField] private InputActionAsset inputActions;
|
|
[SerializeField] private string actionMapName = "Player";
|
|
[SerializeField] private string moveActionName = "Move";
|
|
[SerializeField] private string jumpActionName = "Jump";
|
|
[SerializeField] private string healActionName = "Heal";
|
|
[SerializeField] private string controlSchemeName = "";
|
|
[SerializeField] private int gamepadIndex = -1;
|
|
[SerializeField] private float moveSpeed = 6f;
|
|
[SerializeField] private float jumpImpulse = 8f;
|
|
[SerializeField] private LayerMask groundMask = ~0;
|
|
[SerializeField] private float groundCheckDistance = 0.05f;
|
|
|
|
private PlayerStressController playerStressController;
|
|
private PlayerPickupController playerPickupController;
|
|
private Animator animator;
|
|
|
|
private Rigidbody2D body;
|
|
private BoxCollider2D bodyCollider;
|
|
private InputActionAsset runtimeActions;
|
|
private InputAction moveAction;
|
|
private InputAction jumpAction;
|
|
private InputAction healAction;
|
|
public float LastMoveX { get; private set; }
|
|
private bool deviceFilterEnabled;
|
|
private int healCoolDownTicks = 15;
|
|
private int currentHealCoolDown = 0;
|
|
private bool canHeal = true;
|
|
|
|
private void Awake()
|
|
{
|
|
body = GetComponent<Rigidbody2D>();
|
|
bodyCollider = GetComponent<BoxCollider2D>();
|
|
playerStressController = GetComponent<PlayerStressController>();
|
|
playerPickupController = GetComponent<PlayerPickupController>();
|
|
animator = GetComponent<Animator>();
|
|
}
|
|
|
|
private void OnEnable()
|
|
{
|
|
if (inputActions == null)
|
|
{
|
|
Debug.LogError("PlayerPlatformerController missing InputActionAsset reference.", this);
|
|
return;
|
|
}
|
|
|
|
runtimeActions = Instantiate(inputActions);
|
|
if (!string.IsNullOrWhiteSpace(controlSchemeName))
|
|
{
|
|
runtimeActions.bindingMask = InputBinding.MaskByGroup(controlSchemeName);
|
|
}
|
|
deviceFilterEnabled = ShouldFilterDevices();
|
|
ApplyDeviceFilter();
|
|
InputSystem.onDeviceChange += HandleDeviceChange;
|
|
|
|
var map = runtimeActions.FindActionMap(actionMapName, true);
|
|
moveAction = map.FindAction(moveActionName, true);
|
|
jumpAction = map.FindAction(jumpActionName, true);
|
|
healAction = map.FindAction(healActionName, true);
|
|
|
|
moveAction.Enable();
|
|
jumpAction.Enable();
|
|
jumpAction.performed += OnJumpPerformed;
|
|
healAction.Enable();
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (jumpAction != null)
|
|
{
|
|
jumpAction.performed -= OnJumpPerformed;
|
|
jumpAction.Disable();
|
|
}
|
|
|
|
if (moveAction != null)
|
|
{
|
|
moveAction.Disable();
|
|
}
|
|
|
|
if (healAction != null)
|
|
{
|
|
healAction.Disable();
|
|
}
|
|
|
|
if (runtimeActions != null)
|
|
{
|
|
runtimeActions.Disable();
|
|
runtimeActions = null;
|
|
}
|
|
|
|
InputSystem.onDeviceChange -= HandleDeviceChange;
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if(IsGrounded())
|
|
{
|
|
animator.SetBool("isJumping", false);
|
|
}
|
|
|
|
if (healAction == null) return;
|
|
|
|
currentHealCoolDown++;
|
|
|
|
if (currentHealCoolDown >= healCoolDownTicks)
|
|
{
|
|
currentHealCoolDown = 0;
|
|
canHeal = true;
|
|
}
|
|
|
|
if (moveAction == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (playerStressController.isPanicking) return;
|
|
|
|
if (healAction.IsPressed())
|
|
{
|
|
animator.SetBool("isBreathing", true);
|
|
|
|
if (playerPickupController.heldObject != null)
|
|
{
|
|
playerPickupController.DropObject(false);
|
|
}
|
|
|
|
if (canHeal)
|
|
{
|
|
playerStressController.ReduceStress(3f);
|
|
canHeal = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
animator.SetBool("isBreathing", false);
|
|
}
|
|
|
|
var input = moveAction.ReadValue<Vector2>();
|
|
if (Mathf.Abs(input.x) > 0.01f)
|
|
{
|
|
LastMoveX = input.x;
|
|
}
|
|
var velocity = body.linearVelocity;
|
|
velocity.x = input.x * moveSpeed;
|
|
body.linearVelocity = velocity;
|
|
|
|
if(velocity.x != 0)
|
|
{
|
|
animator.SetBool("isRunning", true);
|
|
if (velocity.x > 0 && IsGrounded())
|
|
{
|
|
transform.localScale = new Vector3(.25f, .25f, .25f);
|
|
}
|
|
else
|
|
{
|
|
transform.localScale = new Vector3(-.25f, .25f, .25f);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
animator.SetBool("isRunning", false);
|
|
}
|
|
}
|
|
|
|
private void OnJumpPerformed(InputAction.CallbackContext context)
|
|
{
|
|
if (playerStressController.isPanicking || healAction.IsPressed()) return;
|
|
|
|
if (!IsGrounded())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var velocity = body.linearVelocity;
|
|
velocity.y = 0f;
|
|
body.linearVelocity = velocity;
|
|
body.AddForce(Vector2.up * jumpImpulse, ForceMode2D.Impulse);
|
|
|
|
animator.SetBool("isJumping", true);
|
|
}
|
|
|
|
private bool ShouldFilterDevices()
|
|
{
|
|
return string.Equals(controlSchemeName, "Gamepad", StringComparison.OrdinalIgnoreCase) || gamepadIndex >= 0;
|
|
}
|
|
|
|
private void ApplyDeviceFilter()
|
|
{
|
|
if (!deviceFilterEnabled || runtimeActions == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var targetIndex = ResolveGamepadIndex();
|
|
if (targetIndex < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Gamepad.all.Count <= targetIndex)
|
|
{
|
|
runtimeActions.devices = Array.Empty<InputDevice>();
|
|
return;
|
|
}
|
|
|
|
runtimeActions.devices = new InputDevice[] { Gamepad.all[targetIndex] };
|
|
}
|
|
|
|
private void HandleDeviceChange(InputDevice device, InputDeviceChange change)
|
|
{
|
|
if (!deviceFilterEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (change == InputDeviceChange.Added || change == InputDeviceChange.Removed || change == InputDeviceChange.Reconnected)
|
|
{
|
|
ApplyDeviceFilter();
|
|
}
|
|
}
|
|
|
|
private int ResolveGamepadIndex()
|
|
{
|
|
if (gamepadIndex >= 0)
|
|
{
|
|
return gamepadIndex;
|
|
}
|
|
|
|
if (string.IsNullOrWhiteSpace(actionMapName))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (string.Equals(actionMapName, "Player", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
var end = actionMapName.Length - 1;
|
|
var start = end;
|
|
while (start >= 0 && char.IsDigit(actionMapName[start]))
|
|
{
|
|
start--;
|
|
}
|
|
|
|
start++;
|
|
if (start > end)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (!int.TryParse(actionMapName.Substring(start, end - start + 1), out var parsed))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return parsed > 0 ? parsed - 1 : -1;
|
|
}
|
|
|
|
private bool IsGrounded()
|
|
{
|
|
var bounds = bodyCollider.bounds;
|
|
var checkSize = new Vector2(bounds.size.x * 0.9f, groundCheckDistance * 2f);
|
|
var checkCenter = new Vector2(bounds.center.x, bounds.min.y - groundCheckDistance);
|
|
var hits = Physics2D.OverlapBoxAll(checkCenter, checkSize, 0f, groundMask);
|
|
for (var i = 0; i < hits.Length; i++)
|
|
{
|
|
var hit = hits[i];
|
|
if (hit == null || hit == bodyCollider || hit.isTrigger)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (hit.GetComponent<PlayerPlatformerController>() != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (hit.GetComponent<TrickController>() != null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|