Files
ggj2026/Assets/Scripts/PlayerPickupController.cs
T

333 lines
9.6 KiB
C#

using System;
using UnityEngine;
using UnityEngine.InputSystem;
public class PlayerPickupController : MonoBehaviour
{
[SerializeField] private InputActionAsset inputActions;
[SerializeField] private string actionMapName = "Player";
[SerializeField] private string interactActionName = "Interact";
private InputActionAsset runtimeActions;
private InputAction interactAction;
[SerializeField] private string controlSchemeName = "";
[SerializeField] private int gamepadIndex = -1;
private bool deviceFilterEnabled;
[SerializeField] private float minThrowSpeed = 6f;
[SerializeField] private float maxThrowSpeed = 20f;
[SerializeField] private float throwChargeTime = 0.75f;
[SerializeField] private float throwUpwardForce = 0f;
[SerializeField] private float throwTriggerThreshold = 0.6f;
[SerializeField] private float dropClearance = 0.2f;
[Header("Setup")]
public Transform holdPoint; // Empty GameObject above head
public float pickupRange = 2.0f;
private GameObject heldObject;
private PlayerPlatformerController platformerController;
private bool isChargingThrow;
private float throwChargeStartTime;
private void OnEnable()
{
platformerController = GetComponent<PlayerPlatformerController>();
if (inputActions == null)
{
Debug.LogError("PlayerPickupController 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);
interactAction = map.FindAction(interactActionName, true);
if (interactAction != null)
{
interactAction.Enable();
interactAction.performed += OnInteractPerformed;
}
}
private void OnDisable()
{
if (interactAction != null)
{
interactAction.performed -= OnInteractPerformed;
interactAction.Disable();
}
if (runtimeActions != null)
{
runtimeActions.Disable();
runtimeActions = null;
}
InputSystem.onDeviceChange -= HandleDeviceChange;
platformerController = null;
}
private void OnInteractPerformed(InputAction.CallbackContext context)
{
Debug.Log("Interact action performed");
if(heldObject != null)
{
Debug.Log("Dropping held object");
DropObject(false);
return;
}
TryPickUp();
}
private void Update()
{
if (heldObject == null)
{
return;
}
if (!heldObject.TryGetComponent<TrickController>(out _))
{
return;
}
var gamepad = GetGamepad();
if (gamepad == null)
{
return;
}
var triggerValue = gamepad.rightTrigger.ReadValue();
if (triggerValue >= throwTriggerThreshold)
{
if (!isChargingThrow)
{
isChargingThrow = true;
throwChargeStartTime = Time.time;
}
}
else if (isChargingThrow)
{
DropObject(true);
}
}
void TryPickUp()
{
// Find all colliders within range
Collider2D[] colliders = Physics2D.OverlapCircleAll(transform.position, pickupRange);
Debug.Log(colliders.Length + " colliders found in range");
foreach (var col in colliders)
{
if (col.gameObject == gameObject)
{
continue;
}
var hasTrick = col.TryGetComponent<TrickController>(out var trick);
var hasPlayer = col.TryGetComponent<PlayerPlatformerController>(out var playerController);
if (!hasTrick && !hasPlayer && !col.CompareTag("Interactable"))
{
continue;
}
heldObject = col.gameObject;
// Parent the object to the HoldPoint and reset its local position
heldObject.transform.SetParent(holdPoint);
heldObject.transform.localPosition = Vector3.zero;
heldObject.transform.localRotation = Quaternion.identity;
// Disable physics so it doesn't fall or collide with the player
if (heldObject.TryGetComponent<Rigidbody2D>(out Rigidbody2D rb))
{
rb.bodyType = RigidbodyType2D.Kinematic;
rb.gravityScale = 0;
rb.mass = 0;
}
if (trick != null)
{
trick.SetCarried(true);
}
if (playerController != null)
{
playerController.enabled = false;
if (heldObject.TryGetComponent<PlayerPickupController>(out var heldPickup))
{
heldPickup.enabled = false;
}
if (heldObject.TryGetComponent<Rigidbody2D>(out var heldBody))
{
heldBody.linearVelocity = Vector2.zero;
heldBody.angularVelocity = 0f;
}
}
if (col != null) col.enabled = false;
break;
}
}
void DropObject(bool applyThrow)
{
// Re-enable physics
if (heldObject.TryGetComponent<Rigidbody2D>(out Rigidbody2D rb))
{
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 1;
rb.linearVelocity = Vector2.zero;
}
if (heldObject.TryGetComponent<TrickController>(out var trick))
{
trick.SetCarried(false);
if (applyThrow)
{
trick.OnThrown();
}
}
if (heldObject.TryGetComponent<PlayerPlatformerController>(out var playerController))
{
playerController.enabled = true;
if (heldObject.TryGetComponent<PlayerPickupController>(out var heldPickup))
{
heldPickup.enabled = true;
}
}
if (applyThrow && heldObject.TryGetComponent<Rigidbody2D>(out Rigidbody2D throwBody))
{
var direction = 1f;
if (platformerController != null && Mathf.Abs(platformerController.LastMoveX) > 0.01f)
{
direction = Mathf.Sign(platformerController.LastMoveX);
}
var charge01 = 1f;
if (isChargingThrow)
{
var heldTime = Time.time - throwChargeStartTime;
charge01 = Mathf.Clamp01(heldTime / Mathf.Max(0.01f, throwChargeTime));
}
var throwSpeed = Mathf.Lerp(minThrowSpeed, maxThrowSpeed, charge01);
throwBody.linearVelocity = new Vector2(direction * throwSpeed, 0f);
throwBody.angularVelocity = 0f;
}
heldObject.transform.position += Vector3.up * dropClearance;
Collider2D col = heldObject.GetComponent<Collider2D>();
if (col != null) col.enabled = true;
// Unparent
heldObject.transform.SetParent(null);
heldObject = null;
isChargingThrow = false;
}
private Gamepad GetGamepad()
{
if (runtimeActions == null)
{
return null;
}
var devices = runtimeActions.devices;
if (devices != null)
{
foreach (var device in devices)
{
if (device is Gamepad gamepad)
{
return gamepad;
}
}
}
return Gamepad.current;
}
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;
}
}