MiniGames/Assets/Scripts/CubeClash/CubeClash_ZibuController.cs

213 lines
6.7 KiB
C#

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[RequireComponent(typeof(Rigidbody))]
public class CubeClash_ZibuController : MonoBehaviour
{
public static List<CubeClash_ZibuController> AllZibus = new List<CubeClash_ZibuController>();
[Header("Control")]
public bool isPlayerControlled = true;
public float moveSpeed = 5f;
public Joystick joystick;
[Header("Bump (Physics)")]
[Tooltip("Impulse strength added in forward direction when bump starts.")]
public float dashImpulse = 12f;
[Tooltip("How long collisions count as 'bumping' (hit-active window).")]
public float hitWindow = 0.22f;
[Tooltip("Cooldown after bump sequence finishes.")]
public float bumpCooldown = 1.0f;
[Header("Momentum Transfer")]
[Tooltip("Scales impulse delivered to the other Zibu on hit.")]
public float momentumTransferScale = 0.9f;
[Tooltip("Extra kick added to the victim on hit.")]
public float bonusKnockImpulse = 2f;
[Tooltip("How much of our forward momentum we lose on hit (0..1).")]
public float selfMomentumLoss = 0.25f;
[Header("Hit Reaction")]
[Tooltip("Time (sec) the victim loses control after being pushed.")]
public float pushStunTime = 0.25f;
[Header("Animation")]
public Animator animator; // optional
// --- runtime
private Rigidbody rb;
private bool canBump = true;
[HideInInspector] public bool isBumping = false; // only during dash + hit window
[HideInInspector] public bool isHitActive = false;
private float stunUntilTime = -999f;
public bool ControlLocked => isBumping || Time.time < stunUntilTime;
void OnEnable()
{
if (!AllZibus.Contains(this)) AllZibus.Add(this);
}
void OnDisable()
{
AllZibus.Remove(this);
}
void Awake()
{
rb = GetComponent<Rigidbody>();
if (animator == null) animator = GetComponentInChildren<Animator>();
// Physics stability
rb.collisionDetectionMode = CollisionDetectionMode.Continuous;
rb.interpolation = RigidbodyInterpolation.Interpolate;
rb.solverIterations = Mathf.Max(rb.solverIterations, 12);
rb.solverVelocityIterations = Mathf.Max(rb.solverVelocityIterations, 12);
// freeze tilt rotations
rb.constraints |= RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ;
}
void Update()
{
if (isPlayerControlled && !ControlLocked)
HandleMovement();
UpdateAnimations();
FaceVelocity(); // always face movement
}
private void HandleMovement()
{
if (joystick == null) return;
Vector3 move = new Vector3(joystick.Horizontal, 0, joystick.Vertical);
if (move.sqrMagnitude > 0.01f)
{
Vector3 targetVel = move.normalized * moveSpeed;
rb.velocity = new Vector3(targetVel.x, rb.velocity.y, targetVel.z);
// smooth yaw-only rotation
Quaternion targetRot = Quaternion.LookRotation(new Vector3(move.x, 0, move.z), Vector3.up);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, Time.deltaTime * 10f);
}
else
{
rb.velocity = new Vector3(0f, rb.velocity.y, 0f);
}
}
private void FaceVelocity()
{
Vector3 v = rb.velocity;
v.y = 0;
if (v.sqrMagnitude > 0.05f) // ignore tiny jitter
{
Quaternion targetRot = Quaternion.LookRotation(v.normalized, Vector3.up);
transform.rotation = Quaternion.Slerp(transform.rotation, targetRot, Time.deltaTime * 10f);
}
}
private void UpdateAnimations()
{
if (!animator) return;
Vector3 horiz = new Vector3(rb.velocity.x, 0, rb.velocity.z);
animator.SetFloat("Speed", horiz.magnitude);
}
// Player UI
public void PerformBump()
{
if (!canBump || ControlLocked) return;
StartCoroutine(BumpSequence(transform.forward));
}
// AI
public void PerformBump(Vector3 direction)
{
if (!canBump || ControlLocked) return;
StartCoroutine(BumpSequence(direction));
}
private IEnumerator BumpSequence(Vector3 direction)
{
canBump = false;
isBumping = true;
// Clean start (optional): limit drift so dash reads crisp
Vector3 horiz = new Vector3(rb.velocity.x, 0f, rb.velocity.z);
rb.velocity = new Vector3(0f, rb.velocity.y, 0f) + horiz * 0.25f;
// --- Dash forward impulse (pure physics) ---
Vector3 fwd = new Vector3(direction.x, 0, direction.z);
if (fwd.sqrMagnitude < 0.001f) fwd = transform.forward;
fwd.Normalize();
rb.AddForce(fwd * dashImpulse, ForceMode.Impulse);
// Collisions are “hot” only during this window
isHitActive = true;
yield return new WaitForSeconds(hitWindow);
isHitActive = false;
// End bump; control resumes, velocity continues naturally
isBumping = false;
// Cooldown before next bump
yield return new WaitForSeconds(bumpCooldown);
canBump = true;
}
// External: short lock so physics can show the shove
public void ApplyPushStun(float seconds)
{
float until = Time.time + Mathf.Max(0f, seconds);
if (until > stunUntilTime) stunUntilTime = until;
}
private void OnCollisionEnter(Collision c) => HandleBumpCollision(c);
private void OnCollisionStay(Collision c) => HandleBumpCollision(c);
private void HandleBumpCollision(Collision c)
{
if (!isHitActive) return;
var other = c.collider.GetComponent<CubeClash_ZibuController>();
if (other == null) return;
// If both are in hit window, let raw physics resolve
if (other.isHitActive) return;
var otherRb = other.GetComponent<Rigidbody>();
if (otherRb == null) return;
// Push direction = our forward
Vector3 pushDir = transform.forward;
pushDir.y = 0;
pushDir.Normalize();
Vector3 relVel = rb.velocity - otherRb.velocity;
float approach = Vector3.Dot(relVel, pushDir);
if (approach <= 0f) return;
float impulseMag = (rb.mass * approach * momentumTransferScale) + bonusKnockImpulse;
Vector3 impulse = pushDir * impulseMag;
// pure shove, no torque
otherRb.AddForce(impulse, ForceMode.Impulse);
// Lock their movement briefly so they don't cancel the shove
other.ApplyPushStun(pushStunTime);
if (selfMomentumLoss > 0f)
{
Vector3 ourForward = Vector3.Project(rb.velocity, pushDir);
rb.AddForce(-ourForward * selfMomentumLoss * rb.mass, ForceMode.Impulse);
}
}
}