203 lines
5.4 KiB
C#
203 lines
5.4 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 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 Rigidbody2D body;
|
|
private BoxCollider2D bodyCollider;
|
|
private InputActionAsset runtimeActions;
|
|
private InputAction moveAction;
|
|
private InputAction jumpAction;
|
|
private bool deviceFilterEnabled;
|
|
|
|
private void Awake()
|
|
{
|
|
body = GetComponent<Rigidbody2D>();
|
|
bodyCollider = GetComponent<BoxCollider2D>();
|
|
}
|
|
|
|
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);
|
|
|
|
moveAction.Enable();
|
|
jumpAction.Enable();
|
|
jumpAction.performed += OnJumpPerformed;
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
if (jumpAction != null)
|
|
{
|
|
jumpAction.performed -= OnJumpPerformed;
|
|
jumpAction.Disable();
|
|
}
|
|
|
|
if (moveAction != null)
|
|
{
|
|
moveAction.Disable();
|
|
}
|
|
|
|
if (runtimeActions != null)
|
|
{
|
|
runtimeActions.Disable();
|
|
runtimeActions = null;
|
|
}
|
|
|
|
InputSystem.onDeviceChange -= HandleDeviceChange;
|
|
}
|
|
|
|
private void FixedUpdate()
|
|
{
|
|
if (moveAction == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var input = moveAction.ReadValue<Vector2>();
|
|
var velocity = body.linearVelocity;
|
|
velocity.x = input.x * moveSpeed;
|
|
body.linearVelocity = velocity;
|
|
}
|
|
|
|
private void OnJumpPerformed(InputAction.CallbackContext context)
|
|
{
|
|
if (!IsGrounded())
|
|
{
|
|
return;
|
|
}
|
|
|
|
var velocity = body.linearVelocity;
|
|
velocity.y = 0f;
|
|
body.linearVelocity = velocity;
|
|
body.AddForce(Vector2.up * jumpImpulse, ForceMode2D.Impulse);
|
|
}
|
|
|
|
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)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|