Skip to content

Instantly share code, notes, and snippets.

@mandarinx
Created March 19, 2020 21:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mandarinx/df5e9bd6e5a8a3badd81dd92bd83d938 to your computer and use it in GitHub Desktop.
Save mandarinx/df5e9bd6e5a8a3badd81dd92bd83d938 to your computer and use it in GitHub Desktop.
Explosion system
//#define LOG
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using AptGames;
using TerrorSquid.BulletSystem;
using UnityEngine;
using TerrorSquid.Entities;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using Debug = UnityEngine.Debug;
namespace TerrorSquid.ExplosionSystem {
public static class Explosions {
private static readonly Dictionary<int, int> current = new Dictionary<int, int>();
private static readonly Dictionary<int, int> lengths = new Dictionary<int, int>();
private static readonly Dictionary<int, int> offsetMap = new Dictionary<int, int>();
private static float[] radius;
private static Explosion[] instances;
private static bool[] active;
private static Transform[] transforms;
private static int[] explosionHashes;
private static int[] offsets;
private static int[] counts;
private static int totalBullets;
public static void Init(BulletPatternSequence sequence) {
totalBullets = sequence.GetTotalBullets();
instances = new Explosion[totalBullets];
transforms = new Transform[totalBullets];
radius = new float[totalBullets];
active = new bool[totalBullets];
}
public static int GetLength(int hash) {
return lengths[hash];
}
#if DEV
public static string GetStatus() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < explosionHashes.Length; ++i) {
int explHash = explosionHashes[i];
int cur = current[explHash];
sb.AppendLine($"ID {explHash} cur: {cur}");
}
return sb.ToString();
}
#endif
public static void Spawn(int explosionHash, Vector3 pos, Quaternion rotLocalSphere) {
int typeIndex = Array.IndexOf(explosionHashes, explosionHash);
#if DEV
if (typeIndex < 0) {
throw new Exception($"Cannot find explosion hash {explosionHash} in list of hashes");
}
#endif
#if DEV
if (typeIndex >= offsets.Length) {
throw new Exception($"Type index {typeIndex} for hash {explosionHash} is greater than the length of offsets array");
}
if (!current.ContainsKey(explosionHash)) {
throw new Exception($"Current dictionary does not contain a key for hash {explosionHash}");
}
#endif
int offset = offsets[typeIndex];
int cur = current[explosionHash];
int instanceIndex = offset + cur;
#if DEV
if (instanceIndex >= instances.Length || instanceIndex < 0) {
throw new Exception($"Instance index {instanceIndex} is out of range of instances array");
}
#endif
transforms[instanceIndex].SetPositionAndRotation(pos, rotLocalSphere);
instances[instanceIndex].Play();
active[instanceIndex] = true;
counts[typeIndex] = counts[typeIndex] + 1;
int len = lengths[explosionHash];
for (int i = cur + 1; i < len; ++i) {
if (!active[offset + i]) {
current[explosionHash] = i;
break;
}
}
Log($"Spawn"+
$", hash: {explosionHash}"+
$", typeIndex: {typeIndex}"+
$", offset: {offset}"+
$", instanceIndex: {instanceIndex}"+
$", current: {current[explosionHash]}"+
$", count: {counts[typeIndex]}");
}
public static void Pause() {
int len = explosionHashes.Length;
for (int h = 0; h < len; ++h) {
int offset = offsets[h];
int count = counts[h];
for (int i = offset; i < offset + count; ++i) {
instances[i].Pause();
}
}
}
public static void Resume() {
int len = explosionHashes.Length;
for (int h = 0; h < len; ++h) {
int offset = offsets[h];
int count = counts[h];
for (int i = offset; i < offset + count; ++i) {
instances[i].Resume();
}
}
}
private static void Despawn(int instanceIndex) {
int seqIndex = GetSequenceIndex(instanceIndex);
instances[instanceIndex].Stop();
transforms[instanceIndex].localPosition = Vector3.zero;
active[instanceIndex] = false;
int explosionHash = explosionHashes[seqIndex];
if (instanceIndex < current[explosionHash]) {
current[explosionHash] = instanceIndex;
}
counts[seqIndex] = counts[seqIndex] - 1;
Log($"Despawn"+
$", instanceIndex: {instanceIndex}"+
$", current: {current[explosionHash]}"+
$", count: {counts[seqIndex]}"+
$", active: {active[instanceIndex]}");
}
public static void Update(float dt) {
int len = explosionHashes.Length;
for (int h = 0; h < len; ++h) {
int hash = explosionHashes[h];
int offset = offsets[h];
int count = counts[h];
int cur = count;
for (int it = count; it > 0; --it) {
int i = it - 1;
int n = offset + i;
if (!instances[n].IsAlive()) {
Despawn(n);
cur = i;
}
}
current[hash] = cur;
}
}
private static int GetSequenceIndex(int instanceIndex) {
int index = 0;
for (int i = 0; i < offsets.Length - 1; ++i) {
if (offsets[i + 1] > instanceIndex) {
return index;
}
++index;
}
return index;
}
public static void DespawnAll() {
for (int i = 0; i < totalBullets; ++i) {
instances[i].Stop();
active[i] = false;
}
int hashes = explosionHashes.Length;
for (int h = 0; h < hashes; ++h) {
int hash = explosionHashes[h];
current[hash] = 0;
}
}
public static float GetRadiusByNameHash(int nameHash) {
return radius[Array.IndexOf(explosionHashes, nameHash)];
}
public static IEnumerator Load(LoadState loadState, BulletPatternSequence sequence) {
loadState.explosionsLoaded = 0;
loadState.explosionsToLoad = sequence.GetTotalBullets();
// Load assets by label
AsyncOperationHandle<IList<GameObject>> handle = Addressables.LoadAssetsAsync<GameObject>("explosion", null);
yield return handle;
Dictionary<int, GameObject> prefabs = new Dictionary<int, GameObject>();
for (int i = 0; i < handle.Result.Count; ++i) {
prefabs.Add(HashUtils.Simple(handle.Result[i].name), handle.Result[i]);
}
int seqLen = sequence.Count;
for (int i = 0; i < seqLen; ++i) {
PatternConfig cfg = sequence.Get(i);
int explosionHash = cfg.ExplosionHash;
current[explosionHash] = 0;
if (lengths.ContainsKey(explosionHash)) {
lengths[explosionHash] += cfg.MaxNum;
} else {
lengths[explosionHash] = cfg.MaxNum;
}
}
List<int> uniqueHashes = new List<int>();
for (int i = 0; i < seqLen; ++i) {
if (!uniqueHashes.Contains(sequence.Get(i).ExplosionHash)) {
uniqueHashes.Add(sequence.Get(i).ExplosionHash);
}
}
explosionHashes = uniqueHashes.ToArray();
offsets = new int[explosionHashes.Length];
counts = new int[explosionHashes.Length];
{
int offset = 0;
for (int i = 0; i < explosionHashes.Length; ++i) {
offsets[i] = offset;
offsetMap.Add(explosionHashes[i], offset);
offset += lengths[explosionHashes[i]];
}
}
Scene sceneExplosions = SceneManager.CreateScene("Explosions");
for (int i = 0; i < explosionHashes.Length; ++i) {
int explosionHash = explosionHashes[i];
int length = lengths[explosionHash];
int offset = offsets[i];
GameObject prefab = prefabs[explosionHash];
for (int e = 0; e < length; ++e) {
int n = offset + e;
instances[n] = GameObject.Instantiate(prefab).GetComponent<Explosion>();
instances[n].gameObject.name = $"{prefab.name}_{e:0000}";
transforms[n] = instances[n].transform;
radius[n] = instances[n].Radius;
SceneManager.MoveGameObjectToScene(instances[n].gameObject, sceneExplosions);
++loadState.explosionsLoaded;
if (n % CONST.INSTANTIATES_PER_FRAME == 0) {
yield return null;
}
}
}
}
[Conditional("LOG")]
private static void Log(object msg) {
Debug.Log(msg);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment