enemy spawner
This commit is contained in:
206
Game/Scripts/Vfx/ProjectileImpactVfxService.cs
Normal file
206
Game/Scripts/Vfx/ProjectileImpactVfxService.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
using MegaKoop.Game.Networking;
|
||||
using Steamworks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MegaKoop.Game.Vfx
|
||||
{
|
||||
[DefaultExecutionOrder(-50)]
|
||||
public class ProjectileImpactVfxService : MonoBehaviour
|
||||
{
|
||||
private const float MinLifetime = 0.5f;
|
||||
|
||||
private static ProjectileImpactVfxService instance;
|
||||
|
||||
private SteamCoopNetworkManager networkManager;
|
||||
private bool isRegistered;
|
||||
private ProjectileImpactVfxSettings settings;
|
||||
|
||||
public static ProjectileImpactVfxService Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = FindObjectOfType<ProjectileImpactVfxService>();
|
||||
if (instance == null)
|
||||
{
|
||||
var go = new GameObject(nameof(ProjectileImpactVfxService));
|
||||
instance = go.AddComponent<ProjectileImpactVfxService>();
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void AutoCreate()
|
||||
{
|
||||
_ = Instance;
|
||||
}
|
||||
|
||||
public static void ReportImpact(ProjectileImpactKind kind, Vector3 position, Vector3 normal)
|
||||
{
|
||||
Instance.InternalReportImpact(kind, position, normal);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (instance != null && instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
instance = this;
|
||||
DontDestroyOnLoad(gameObject);
|
||||
LoadSettings();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
TryBindNetworkManager();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
TryBindNetworkManager();
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
UnregisterHandler();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (instance == this)
|
||||
{
|
||||
instance = null;
|
||||
}
|
||||
|
||||
UnregisterHandler();
|
||||
}
|
||||
|
||||
private void LoadSettings()
|
||||
{
|
||||
if (settings != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
settings = ProjectileImpactVfxSettings.LoadFromResources();
|
||||
if (settings == null)
|
||||
{
|
||||
Debug.LogWarning($"[ProjectileImpactVfxService] Could not locate Resources/{ProjectileImpactVfxSettings.ResourcePath}. Impact particles will be skipped.");
|
||||
}
|
||||
}
|
||||
|
||||
private void TryBindNetworkManager()
|
||||
{
|
||||
var current = SteamCoopNetworkManager.Instance;
|
||||
if (current != networkManager)
|
||||
{
|
||||
UnregisterHandler();
|
||||
networkManager = current;
|
||||
}
|
||||
|
||||
if (networkManager != null && !isRegistered)
|
||||
{
|
||||
networkManager.RegisterHandler(NetworkMessageType.ProjectileImpact, HandleImpactMessage);
|
||||
isRegistered = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void UnregisterHandler()
|
||||
{
|
||||
if (isRegistered && networkManager != null)
|
||||
{
|
||||
networkManager.UnregisterHandler(NetworkMessageType.ProjectileImpact, HandleImpactMessage);
|
||||
}
|
||||
|
||||
isRegistered = false;
|
||||
}
|
||||
|
||||
private void HandleImpactMessage(NetworkMessage message)
|
||||
{
|
||||
ProjectileImpactMessage impact = ProjectileImpactMessage.Deserialize(message.Payload);
|
||||
SpawnImpact(impact.Kind, impact.Position, impact.Normal);
|
||||
}
|
||||
|
||||
private void InternalReportImpact(ProjectileImpactKind kind, Vector3 position, Vector3 normal)
|
||||
{
|
||||
SpawnImpact(kind, position, normal);
|
||||
|
||||
TryBindNetworkManager();
|
||||
if (networkManager == null || !networkManager.IsHost)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var impactMessage = new ProjectileImpactMessage(kind, position, normal);
|
||||
byte[] payload = ProjectileImpactMessage.Serialize(impactMessage);
|
||||
networkManager.SendToAll(NetworkMessageType.ProjectileImpact, payload, EP2PSend.k_EP2PSendUnreliableNoDelay);
|
||||
}
|
||||
|
||||
private void SpawnImpact(ProjectileImpactKind kind, Vector3 position, Vector3 normal)
|
||||
{
|
||||
LoadSettings();
|
||||
if (settings == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject prefab = kind switch
|
||||
{
|
||||
ProjectileImpactKind.Hero => settings.HeroImpactPrefab,
|
||||
ProjectileImpactKind.Enemy => settings.EnemyImpactPrefab,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Quaternion rotation = normal.sqrMagnitude > 0.001f ? Quaternion.LookRotation(normal) : Quaternion.identity;
|
||||
GameObject instance = Instantiate(prefab, position, rotation);
|
||||
float lifetime = EstimateLifetime(instance);
|
||||
if (lifetime > 0f)
|
||||
{
|
||||
Destroy(instance, lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
private float EstimateLifetime(GameObject effectInstance)
|
||||
{
|
||||
float fallback = settings != null ? settings.FallbackLifetime : 4f;
|
||||
ParticleSystem[] particleSystems = effectInstance.GetComponentsInChildren<ParticleSystem>();
|
||||
if (particleSystems == null || particleSystems.Length == 0)
|
||||
{
|
||||
return Mathf.Max(MinLifetime, fallback);
|
||||
}
|
||||
|
||||
float maxLifetime = fallback;
|
||||
foreach (ParticleSystem system in particleSystems)
|
||||
{
|
||||
var main = system.main;
|
||||
if (main.loop)
|
||||
{
|
||||
maxLifetime = Mathf.Max(maxLifetime, fallback);
|
||||
continue;
|
||||
}
|
||||
|
||||
float candidate = main.duration + main.startLifetime.constantMax;
|
||||
if (candidate <= 0f)
|
||||
{
|
||||
candidate = fallback;
|
||||
}
|
||||
|
||||
maxLifetime = Mathf.Max(maxLifetime, candidate);
|
||||
}
|
||||
|
||||
return Mathf.Max(MinLifetime, maxLifetime);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user