Working state for minigameobstacles

This commit is contained in:
2025-09-17 16:10:18 +02:00
committed by Michal Pikulski
parent 2ec5c3d855
commit 50070651c5
26 changed files with 4573 additions and 19 deletions

View File

@@ -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;
}
}