Working state for minigameobstacles
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for handling player collisions with world obstacles.
|
||||
/// Detects collisions between Player layer (7) and WorldObstacle layer (6).
|
||||
/// </summary>
|
||||
public abstract class PlayerCollisionBehavior : MonoBehaviour
|
||||
{
|
||||
[Header("Collision Settings")]
|
||||
[Tooltip("Duration in seconds of damage immunity after being hit")]
|
||||
[SerializeField] protected float damageImmunityDuration = 1.0f;
|
||||
|
||||
[Tooltip("Layer mask for obstacle detection - should match WorldObstacle layer")]
|
||||
[SerializeField] protected LayerMask obstacleLayerMask = 1 << 6; // WorldObstacle layer
|
||||
|
||||
[Header("Input Blocking")]
|
||||
[Tooltip("Whether to block player input during damage immunity period")]
|
||||
[SerializeField] protected bool blockInputDuringImmunity;
|
||||
|
||||
[Header("References")]
|
||||
[Tooltip("The player character GameObject (auto-assigned if empty)")]
|
||||
[SerializeField] protected GameObject playerCharacter;
|
||||
|
||||
[Tooltip("Reference to the PlayerController component (auto-assigned if empty)")]
|
||||
[SerializeField] protected PlayerController playerController;
|
||||
|
||||
// Events for damage state changes
|
||||
public static event Action OnDamageStart;
|
||||
public static event Action OnDamageEnd;
|
||||
|
||||
/// <summary>
|
||||
/// Public static method to trigger damage start event from external classes
|
||||
/// </summary>
|
||||
public static void TriggerDamageStart()
|
||||
{
|
||||
OnDamageStart?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public static method to trigger damage end event from external classes
|
||||
/// </summary>
|
||||
public static void TriggerDamageEnd()
|
||||
{
|
||||
OnDamageEnd?.Invoke();
|
||||
}
|
||||
|
||||
protected bool isImmune;
|
||||
protected float immunityTimer;
|
||||
protected Collider2D playerCollider;
|
||||
protected bool wasInputBlocked;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
// Auto-assign if not set in inspector
|
||||
if (playerCharacter == null)
|
||||
playerCharacter = gameObject;
|
||||
|
||||
if (playerController == null)
|
||||
playerController = GetComponent<PlayerController>();
|
||||
|
||||
// Look for collider on this GameObject first, then in children
|
||||
playerCollider = GetComponent<Collider2D>();
|
||||
if (playerCollider == null)
|
||||
{
|
||||
playerCollider = GetComponentInChildren<Collider2D>();
|
||||
if (playerCollider != null)
|
||||
{
|
||||
Debug.Log($"[{GetType().Name}] Found collider on child object: {playerCollider.gameObject.name}");
|
||||
}
|
||||
}
|
||||
|
||||
if (playerCollider == null)
|
||||
{
|
||||
Debug.LogError($"[{GetType().Name}] No Collider2D found on this GameObject or its children!");
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
// Handle immunity timer
|
||||
if (isImmune)
|
||||
{
|
||||
immunityTimer -= Time.deltaTime;
|
||||
if (immunityTimer <= 0f)
|
||||
{
|
||||
isImmune = false;
|
||||
OnImmunityEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for collisions if not immune
|
||||
if (!isImmune && playerCollider != null)
|
||||
{
|
||||
CheckForCollisions();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks for collisions with obstacle layer objects
|
||||
/// </summary>
|
||||
protected virtual void CheckForCollisions()
|
||||
{
|
||||
// Get all colliders overlapping with the player
|
||||
Collider2D[] overlapping = new Collider2D[10];
|
||||
ContactFilter2D filter = new ContactFilter2D();
|
||||
filter.SetLayerMask(obstacleLayerMask);
|
||||
filter.useTriggers = true;
|
||||
|
||||
int count = playerCollider.Overlap(filter, overlapping);
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
// Found collision, trigger response
|
||||
OnCollisionDetected(overlapping[0]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a collision with an obstacle is detected
|
||||
/// </summary>
|
||||
/// <param name="obstacle">The obstacle collider that was hit</param>
|
||||
protected virtual void OnCollisionDetected(Collider2D obstacle)
|
||||
{
|
||||
if (isImmune) return;
|
||||
|
||||
// Start immunity period
|
||||
isImmune = true;
|
||||
immunityTimer = damageImmunityDuration;
|
||||
|
||||
// Call the specific collision response
|
||||
HandleCollisionResponse(obstacle);
|
||||
|
||||
// Notify about immunity start
|
||||
OnImmunityStart();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to implement specific collision response behavior
|
||||
/// </summary>
|
||||
/// <param name="obstacle">The obstacle that was collided with</param>
|
||||
protected abstract void HandleCollisionResponse(Collider2D obstacle);
|
||||
|
||||
/// <summary>
|
||||
/// Called when damage immunity starts
|
||||
/// </summary>
|
||||
protected virtual void OnImmunityStart()
|
||||
{
|
||||
Debug.Log($"[{GetType().Name}] Damage immunity started for {damageImmunityDuration} seconds");
|
||||
|
||||
// Block input if specified
|
||||
if (blockInputDuringImmunity)
|
||||
{
|
||||
BlockPlayerInput();
|
||||
}
|
||||
|
||||
// Broadcast damage start event
|
||||
OnDamageStart?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when damage immunity ends
|
||||
/// </summary>
|
||||
protected virtual void OnImmunityEnd()
|
||||
{
|
||||
Debug.Log($"[{GetType().Name}] Damage immunity ended");
|
||||
|
||||
// Restore input if it was blocked
|
||||
if (wasInputBlocked)
|
||||
{
|
||||
RestorePlayerInput();
|
||||
}
|
||||
|
||||
// Broadcast damage end event
|
||||
OnDamageEnd?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restores player input after immunity
|
||||
/// </summary>
|
||||
protected virtual void RestorePlayerInput()
|
||||
{
|
||||
if (playerController != null && wasInputBlocked)
|
||||
{
|
||||
playerController.enabled = true;
|
||||
wasInputBlocked = false;
|
||||
|
||||
// Update the controller's target position to current position to prevent snapping
|
||||
UpdateControllerTarget();
|
||||
|
||||
Debug.Log($"[{GetType().Name}] Player input restored after immunity");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blocks player input during immunity
|
||||
/// </summary>
|
||||
protected virtual void BlockPlayerInput()
|
||||
{
|
||||
if (playerController != null && playerController.enabled)
|
||||
{
|
||||
playerController.enabled = false;
|
||||
wasInputBlocked = true;
|
||||
Debug.Log($"[{GetType().Name}] Player input blocked during immunity");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the PlayerController's internal target to match current position
|
||||
/// </summary>
|
||||
protected virtual void UpdateControllerTarget()
|
||||
{
|
||||
if (playerController != null && playerCharacter != null)
|
||||
{
|
||||
// Use reflection to update the private _targetFingerX field
|
||||
var targetField = typeof(PlayerController)
|
||||
.GetField("_targetFingerX", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (targetField != null)
|
||||
{
|
||||
targetField.SetValue(playerController, playerCharacter.transform.position.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public property to check if player is currently immune
|
||||
/// </summary>
|
||||
public bool IsImmune => isImmune;
|
||||
|
||||
/// <summary>
|
||||
/// Remaining immunity time
|
||||
/// </summary>
|
||||
public float RemainingImmunityTime => immunityTimer;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user