199 lines
6.5 KiB
C#

using BulletHellTemplate.Core.Events;
using Cysharp.Threading.Tasks;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
namespace BulletHellTemplate
{
/// <summary>
/// This script allows rotation of a child object inside a containerPassItems when dragging or holding,
/// or triggers an animation on click without dragging.
/// Attach this script to an Image or Button (set to transparent).
/// </summary>
public class DragRotateAndHit : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
public static DragRotateAndHit Singleton;
[Header("Container Settings")]
[Tooltip("The containerPassItems that holds the child object to be rotated.")]
public Transform container;
[Tooltip("Rotation speed of the containerPassItems.")]
public float rotateSpeed = 4f;
[Tooltip("Audio clip to play when triggering a hit animation.")]
public AudioClip hitClip;
[Tooltip("Audio mixer tag to identify the audio group.")]
public string hitTag = "master";
private CharacterModel childCharacterModel;
private bool isDragging = false;
private bool isHolding = false;
private Vector2 startPointerPosition;
private Quaternion initialRotation;
private void Awake()
{
if (Singleton == null)
{
Singleton = this;
}
else
{
Destroy(gameObject);
}
}
private void Start()
{
SetupChild();
}
/// <summary>
/// Sets up the child object inside the containerPassItems and retrieves its Animator and/or CharacterModel.
/// </summary>
/// <summary>
/// Initializes references to the first child of the containerPassItems.
/// Tries to find Animator and CharacterModel in the child, and falls back to the containerPassItems if not found.
/// </summary>
private void SetupChild()
{
if (container == null)
{
Debug.LogError("No containerPassItems assigned. Please assign a containerPassItems in the inspector.");
return;
}
if (container.childCount > 0)
{
Transform child = container.GetChild(0);
childCharacterModel = child.GetComponent<CharacterModel>();
if (childCharacterModel == null)
childCharacterModel = container.GetComponent<CharacterModel>();
}
else
{
Debug.LogError("No child found in the containerPassItems.");
}
initialRotation = container.rotation;
}
/// <summary>
/// Called when the pointer is pressed down on the UI element.
/// </summary>
public void OnPointerDown(PointerEventData eventData)
{
startPointerPosition = eventData.position;
isDragging = false;
isHolding = true;
}
/// <summary>
/// Called when dragging the UI element. Rotates the containerPassItems based on horizontal drag movement.
/// </summary>
/// <param name="eventData">Pointer event data containing the drag information.</param>
public void OnDrag(PointerEventData eventData)
{
if (container == null || container.childCount == 0)
return;
Vector2 dragDirection = eventData.position - startPointerPosition;
if (dragDirection.x != 0)
{
float rotationAmount = -rotateSpeed * dragDirection.x * Time.deltaTime;
container.Rotate(Vector3.up, rotationAmount);
}
isDragging = true;
}
private void Update()
{
// If holding but not dragging, rotate the containerPassItems slowly
if (isHolding && !isDragging)
{
container.Rotate(Vector3.up, rotateSpeed * Time.deltaTime);
}
}
/// <summary>
/// Called when the pointer is released. If not dragging, triggers the hit animation.
/// </summary>
/// <param name="eventData">Pointer event data.</param>
public void OnPointerUp(PointerEventData eventData)
{
isHolding = false;
// If it was not dragging, trigger a "Hit" animation
if (!isDragging)
{
// Play audio if available
if (AudioManager.Singleton != null)
{
AudioManager.Singleton.PlayAudio(hitClip, hitTag);
}
// Check if the child has a CharacterModel with Playable animations
if (childCharacterModel != null)
{
EventBus.Publish(new AnimationOnReceiveDamageEvent(childCharacterModel));
}
else
{
Debug.LogWarning("No CharacterModel found to trigger the hit animation.");
}
}
}
/// <summary>
/// Resets the containerPassItems rotation to its original state.
/// </summary>
public void ResetRotation()
{
if (container != null)
{
container.rotation = initialRotation;
}
else
{
Debug.LogError("No containerPassItems assigned to reset rotation.");
}
}
/// <summary>
/// Updates the character after a new one is loaded into the containerPassItems.
/// </summary>
public void UpdateCharacter()
{
UpdateCharacterAsync().Forget();
}
/// <summary>
/// Waits a frame to ensure the new character is instantiated before setup.
/// Uses UniTask to avoid issues when changing scenes or exiting play mode.
/// </summary>
private async UniTaskVoid UpdateCharacterAsync()
{
var token = this.GetCancellationTokenOnDestroy();
await UniTask.Yield(PlayerLoopTiming.PostLateUpdate, token);
ResetRotation();
SetupChild();
if (childCharacterModel == null)
{
Debug.LogError("No CharacterModel found after character update. Check the containerPassItems's child.");
}
}
}
}