using System; using Unity.BossRoom.Gameplay.GameplayObjects.Character; using Unity.Netcode; using UnityEngine; using UnityEngine.Events; namespace Unity.BossRoom.Gameplay.GameplayObjects { public class TossedItem : NetworkBehaviour { [Header("Server")] [SerializeField] int m_DamagePoints; [SerializeField] float m_HitRadius = 5f; [SerializeField] float m_KnockbackSpeed; [SerializeField] float m_KnockbackDuration; [SerializeField] LayerMask m_LayerMask; bool m_Started; const int k_MaxCollisions = 16; Collider[] m_CollisionCache = new Collider[k_MaxCollisions]; [SerializeField] float m_DetonateAfterSeconds = 5f; float m_DetonateTimer; [SerializeField] float m_DestroyAfterSeconds = 6f; float m_DestroyTimer; bool m_Detonated; public UnityEvent detonatedCallback; [Header("Client")] [SerializeField] Transform m_TossedItemVisualTransform; const float k_DisplayHeight = 0.1f; readonly Quaternion k_TossAttackRadiusDisplayRotation = Quaternion.Euler(90f, 0f, 0f); [SerializeField] GameObject m_TossedObjectGraphics; [SerializeField] AudioSource m_FallingSound; public override void OnNetworkSpawn() { if (IsServer) { m_Started = true; m_Detonated = false; m_DetonateTimer = Time.fixedTime + m_DetonateAfterSeconds; m_DestroyTimer = Time.fixedTime + m_DestroyAfterSeconds; } if (IsClient) { m_TossedItemVisualTransform.gameObject.SetActive(true); m_TossedObjectGraphics.SetActive(true); m_FallingSound.Play(); } } public override void OnNetworkDespawn() { if (IsServer) { m_Started = false; m_Detonated = false; } if (IsClient) { m_TossedItemVisualTransform.gameObject.SetActive(false); } } void Detonate() { var hits = Physics.OverlapSphereNonAlloc(transform.position, m_HitRadius, m_CollisionCache, m_LayerMask); for (int i = 0; i < hits; i++) { if (m_CollisionCache[i].gameObject.TryGetComponent(out IDamageable damageReceiver)) { damageReceiver.ReceiveHP(null, -m_DamagePoints); var serverCharacter = m_CollisionCache[i].gameObject.GetComponentInParent(); if (serverCharacter) { serverCharacter.Movement.StartKnockback(transform.position, m_KnockbackSpeed, m_KnockbackDuration); } } } // send client RPC to detonate on clients ClientDetonateRpc(); m_Detonated = true; } [Rpc(SendTo.ClientsAndHost)] void ClientDetonateRpc() { detonatedCallback?.Invoke(); } void FixedUpdate() { if (IsServer) { if (!m_Started) { return; //don't do anything before OnNetworkSpawn has run. } if (!m_Detonated && m_DetonateTimer < Time.fixedTime) { Detonate(); } if (m_Detonated && m_DestroyTimer < Time.fixedTime) { // despawn after sending detonate RPC var networkObject = gameObject.GetComponent(); networkObject.Despawn(); } } } void LateUpdate() { if (IsClient) { var tossedItemPosition = transform.position; m_TossedItemVisualTransform.SetPositionAndRotation( new Vector3(tossedItemPosition.x, k_DisplayHeight, tossedItemPosition.z), k_TossAttackRadiusDisplayRotation); } } } }