using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Serialization;
using UnityEngine.SceneManagement;
using TMPro;
using DamageNumbersPro.Internal;
#if ENABLE_INPUT_SYSTEM && DNP_NewInputSystem
using UnityEngine.InputSystem;
#endif
namespace DamageNumbersPro
{
public abstract class DamageNumber : MonoBehaviour
{
#region Main Settings
// Lifetime
[Tooltip("Damage number will not fade out on it's own.")]
public bool permanent = false;
[Tooltip("The lifetime after which this fades out.")]
public float lifetime = 2f;
[Tooltip("Ignores slow motion or game pause.")]
public bool unscaledTime = false;
// 3D Settings
public bool enable3DGame = false;
[Tooltip("Faces the camera at all times.")]
public bool faceCameraView = true;
[Tooltip("Uses LookAt(...) instead of the camera rotation.\nThis costs more performance but looks better in VR.\n\nNot recommended for spamming popups.")]
public bool lookAtCamera = false;
[Tooltip("Moves the number close to the camera and scales it down to make it look like it was visible through walls.")]
public bool renderThroughWalls = true;
[Tooltip("Keeps the screen-size consistent accross different distances.")]
public bool consistentScreenSize = false;
public DistanceScalingSettings distanceScalingSettings = new DistanceScalingSettings(0);
[Tooltip("Scales the popup with the camera's field of view to keep it's size consistent.")]
public bool scaleWithFov = false;
[Tooltip("The default field of view, where the popup will be at it's default scale.")]
public float defaultFov = 60f;
[Tooltip("The camera whose field of view the popup will react to.")]
public Camera fovCamera;
[Tooltip("Override the camera looked at and scaled for.\nIf this set to None the Main Camera will be used.")]
public Transform cameraOverride;
#endregion
#region Text Settings
// Number
public bool enableNumber = true;
[Tooltip("The number displayed in the text.\nCan be disabled if you only need text.")]
public float number = 1;
public TextSettings numberSettings = new TextSettings(0);
public DigitSettings digitSettings = new DigitSettings(0);
// Left Text
[FormerlySerializedAs("enablePrefix")]
public bool enableLeftText = false;
[Tooltip("Text displayed to the left of the number.")]
[FormerlySerializedAs("prefix")]
public string leftText = "";
[FormerlySerializedAs("prefixSettings")]
public TextSettings leftTextSettings = new TextSettings(0);
// Right Text
[FormerlySerializedAs("enableSuffix")]
public bool enableRightText = false;
[Tooltip("Text displayed to the right of the number.")]
[FormerlySerializedAs("suffix")]
public string rightText = "";
[FormerlySerializedAs("suffixSettings")]
public TextSettings rightTextSettings = new TextSettings(0);
// Top Text
public bool enableTopText = false;
[Tooltip("Text displayed above the number.")]
public string topText = "";
public TextSettings topTextSettings = new TextSettings(0f);
// Bottom Text
public bool enableBottomText = false;
[Tooltip("Text displayed below the number.")]
public string bottomText = "";
public TextSettings bottomTextSettings = new TextSettings(0f);
// Color by Number
public bool enableColorByNumber = false;
public ColorByNumberSettings colorByNumberSettings = new ColorByNumberSettings(0f);
#endregion
#region Fade Settings
// Fade In
public float durationFadeIn = 0.2f;
public bool enableOffsetFadeIn = true;
[Tooltip("TextA and TextB move together from this offset.")]
public Vector2 offsetFadeIn = new Vector2(0.5f, 0);
public bool enableScaleFadeIn = true;
[Tooltip("Scales in from this scale.")]
public Vector2 scaleFadeIn = new Vector2(2, 2);
public bool enableCrossScaleFadeIn = false;
[Tooltip("Scales TextA in from this scale and TextB from the inverse of this scale.")]
public Vector2 crossScaleFadeIn = new Vector2(1, 1.5f);
public bool enableShakeFadeIn = false;
[Tooltip("Shakes in from this offset.")]
public Vector2 shakeOffsetFadeIn = new Vector2(0, 1.5f);
[Tooltip("Shakes in at this frequency.")]
public float shakeFrequencyFadeIn = 4f;
// Fade Out
public float durationFadeOut = 0.2f;
public bool enableOffsetFadeOut = true;
[Tooltip("TextA and TextB move apart to this offset.")]
public Vector2 offsetFadeOut = new Vector2(0.5f, 0);
public bool enableScaleFadeOut = false;
[Tooltip("Scales out to this scale.")]
public Vector2 scaleFadeOut = new Vector2(2, 2);
public bool enableCrossScaleFadeOut = false;
[Tooltip("Scales TextA out to this scale and TextB to the inverse of this scale.")]
public Vector2 crossScaleFadeOut = new Vector2(1, 1.5f);
public bool enableShakeFadeOut = false;
[Tooltip("Shakes out to this offset.")]
public Vector2 shakeOffsetFadeOut = new Vector2(0, 1.5f);
[Tooltip("Shakes out at this frequency.")]
public float shakeFrequencyFadeOut = 4f;
#endregion
#region Movement Settings
// Lerping
public bool enableLerp = true;
public LerpSettings lerpSettings = new LerpSettings(0);
// Velocity
public bool enableVelocity = false;
public VelocitySettings velocitySettings = new VelocitySettings(0);
// Shaking
public bool enableShaking = false;
[Tooltip("Shake settings during idle.")]
public ShakeSettings shakeSettings = new ShakeSettings(new Vector2(0.005f, 0.005f));
// Following
public bool enableFollowing = false;
[Tooltip("Transform that will be followed.\nTries to maintain the position relative to the target.")]
public Transform followedTarget;
public FollowSettings followSettings = new FollowSettings(0);
#endregion
#region Rotation & Scale Settings
// Start Rotation
public bool enableStartRotation = false;
[Tooltip("The minimum z-angle for the random spawn rotation.")]
public float minRotation = -4f;
[Tooltip("The maximum z-angle for the random spawn rotation.")]
public float maxRotation = 4f;
public bool rotationRandomFlip = false;
// Rotate By Time
public bool enableRotateOverTime = false;
[Tooltip("The minimum rotation speed for the z-angle.")]
public float minRotationSpeed = -15f;
[Tooltip("The maximum rotation speed for the z-angle.")]
public float maxRotationSpeed = 15;
public bool rotationSpeedRandomFlip = false;
[Tooltip("Defines rotation speed over lifetime.")]
public AnimationCurve rotateOverTime = new AnimationCurve(new Keyframe[] { new Keyframe(0, 1), new Keyframe(0.4f, 1), new Keyframe(0.8f, 0), new Keyframe(1, 0) });
// Scale By Number
public bool enableScaleByNumber = false;
public ScaleByNumberSettings scaleByNumberSettings = new ScaleByNumberSettings(0);
// Scale By Time
public bool enableScaleOverTime = false;
[Tooltip("Will scale over it's lifetime using this curve.")]
public AnimationCurve scaleOverTime = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1, 0.7f));
// Scale By Orthographic Size
public bool enableOrthographicScaling = false;
[Tooltip("The base orthographic size of the camera, where the popup will use it's default scale.")]
public float defaultOrthographicSize = 5f;
[Tooltip("Popups wont get larger than this value to prevent overlapping, when zooming out.")]
public float maxOrthographicSize = 1.5f;
[Tooltip("The camera whose orthographic size will be used to resize the popup. Uses the Main Camera by default.")]
public Camera orthographicCamera;
#endregion
#region Spam Control Settings
[Tooltip("The group of numbers which will affect each other using the features bellow.")]
public string spamGroup = "";
// Combination
public bool enableCombination = false;
public CombinationSettings combinationSettings = new CombinationSettings(0);
// Destruction
public bool enableDestruction = false;
public DestructionSettings destructionSettings = new DestructionSettings(0);
// Collision
public bool enableCollision = false;
public CollisionSettings collisionSettings = new CollisionSettings(0);
// Push
public bool enablePush = false;
public PushSettings pushSettings = new PushSettings(0);
#endregion
#region Performance Settings
// Update Delay
public float updateDelay = 0.0125f;
// Pooling
public bool enablePooling = false;
[Tooltip("Maximum of damage numbers stored in pool.")]
public int poolSize = 50;
[Tooltip("Pooled damage numbers are not destroyed on load.\nThis option will fade them out on load.\nSo you don't see popups from the previous scene.")]
public bool disableOnSceneLoad = true;
#endregion
#region Editor Variables
///
/// Please ignore this variable, it's used by the editor.
///
public string editorLastFont;
#endregion
// References
TextMeshPro textMeshPro;
MeshRenderer textMeshRenderer;
MeshRenderer meshRendererA;
MeshRenderer meshRendererB;
MeshFilter meshFilterA;
MeshFilter meshFilterB;
protected Transform transformA;
protected Transform transformB;
List> subMeshRenderers;
List> subMeshFilters;
protected List meshs;
protected List colors;
protected List alphas;
// Fading
protected float currentFade;
protected float startTime;
float startLifeTime;
protected float currentLifetime;
float fadeInSpeed;
float fadeOutSpeed;
protected float baseAlpha;
Vector2 currentScaleInOffset;
Vector2 currentScaleOutOffset;
// Position
public Vector3 position;
Vector3 finalPosition;
protected Vector3 remainingOffset;
protected Vector2 currentVelocity;
// Scaling
protected Vector3 originalScale;
float numberScale;
float combinationScale;
float destructionScale;
float renderThroughWallsScale = 0.1f;
float lastScaleFactor = 1f;
bool firstFrameScale;
// Rotation
float currentRotationSpeed;
float currentRotation;
// Following
Vector3 lastTargetPosition;
Vector3 targetOffset;
float currentFollowSpeed;
// Spam Control
static Dictionary> spamGroupDictionary;
bool removedFromDictionary;
// Combination
DamageNumber myAbsorber;
bool givenNumber;
float absorbStartTime;
Vector3 absorbStartPosition;
// 3D
Transform targetCamera;
Camera targetFovCamera;
float simulatedScale;
float lastFOV;
// Orthographic Sclaing
Camera targetOrthographicCamera;
// Destruction
bool isDestroyed;
float destructionStartTime;
// Collision & Push
bool collided;
bool pushed;
// Pooling
DamageNumber originalPrefab;
public static Transform poolParent;
static Dictionary> pools;
public static HashSet activeInstances;
int poolingID;
bool performRestart;
bool destroyAfterSpawning;
// Fallback font fix
static Dictionary fallbackDictionary;
// Custom Events
protected bool isFadingOut;
void Start()
{
// Once
GetReferencesIfNecessary();
if(enablePooling && disableOnSceneLoad)
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
// Repeated for Pooling
Restart();
}
void Update()
{
// For Pooling
if (performRestart)
{
Restart();
performRestart = false;
}
}
void LateUpdate()
{
if (!performRestart)
{
UpdateScaleAnd3D();
}
OnLateUpdate();
}
///
/// This is called by DNPUpdater for improved performance.
/// You can ignore this function.
///
public void UpdateDamageNumber(float delta, float time)
{
// Check activity
if(isActiveAndEnabled == false)
{
startTime += delta;
startLifeTime += delta;
absorbStartTime += delta;
destructionStartTime += delta;
return;
}
// Vectors
if(DNPUpdater.vectorsNeedUpdate)
{
DNPUpdater.UpdateVectors(transform);
}
// Fading
if (IsAlive(time))
{
HandleFadeIn(delta);
}
else
{
HandleFadeOut(delta);
}
// Custom Event
InternalUpdate(delta);
// Movement
if (enableLerp)
{
HandleLerp(delta);
}
if (enableVelocity)
{
HandleVelocity(delta);
}
if (enableFollowing)
{
HandleFollowing(delta);
}
// Rotation
if (enableRotateOverTime)
{
HandleRotateOverTime(delta, time);
UpdateRotationZ();
}
// Combination
if (enableCombination)
{
HandleCombination(delta, time);
}
// Destruction
if (enableDestruction)
{
HandleDestruction(time);
}
// Offset
finalPosition = position;
if (enableShaking)
{
finalPosition = ApplyShake(finalPosition, shakeSettings, time);
}
// Apply Transform
SetFinalPosition(finalPosition);
}
#region Spawn Functions
///
/// Spawns a new popup and handles pooling.
///
/// The spawned popup, which can be modified at runtime.
public DamageNumber Spawn()
{
DamageNumber newDN = default;
int instanceID = GetInstanceID();
// Check Pool
if (enablePooling && PoolAvailable(instanceID))
{
// Get from Pool
foreach (DamageNumber dn in pools[instanceID])
{
newDN = dn; // This is the only way I can get a unknown element from a hashset, using a single loop iteration
break;
}
pools[instanceID].Remove(newDN);
}
else
{
// Create New
GameObject newGO = Instantiate(gameObject);
newDN = newGO.GetComponent();
if (enablePooling)
{
newDN.originalPrefab = this;
}
}
newDN.gameObject.SetActive(true); // Active Gameobject
newDN.InternalOnPreSpawn();
if (enablePooling)
{
newDN.SetPoolingID(instanceID);
newDN.destroyAfterSpawning = false;
}
return newDN;
}
///
/// Spawns a new popup and handles pooling.
///
/// The worldspace position of this popup.
/// The spawned popup, which can be modified at runtime.
public DamageNumber Spawn(Vector3 newPosition)
{
DamageNumber newDN = Spawn();
// Position
newDN.SetPosition(newPosition);
return newDN;
}
///
/// Spawns a new popup and handles pooling.
/// Also sets the popup's number.
///
/// The worldspace position of this popup.
/// The displayed number of this popup.
/// The spawned popup, which can be modified at runtime.
public DamageNumber Spawn(Vector3 newPosition, float newNumber)
{
DamageNumber newDN = Spawn(newPosition);
// Number
newDN.enableNumber = true;
newDN.number = newNumber;
return newDN;
}
///
/// Spawns a new popup and handles pooling.
/// Also sets the popup's number and makes it follow a transform.
///
/// The worldspace position of this popup.
/// The displayed number of this popup.
/// The transform, which this popup should follow.
/// The spawned popup, which can be modified at runtime.
public DamageNumber Spawn(Vector3 newPosition, float newNumber, Transform followedTransform)
{
DamageNumber newDN = Spawn(newPosition, newNumber);
// Following
newDN.SetFollowedTarget(followedTransform);
return newDN;
}
///
/// Spawns a new popup and handles pooling.
/// Also makes the popup follow a transform.
///
/// The worldspace position of this popup.
/// The transform, which this popup should follow.
/// The spawned popup, which can be modified at runtime.
public DamageNumber Spawn(Vector3 newPosition, Transform followedTransform)
{
DamageNumber newDN = Spawn(newPosition);
// Following
newDN.SetFollowedTarget(followedTransform);
return newDN;
}
///
/// Spawns a new popup and handles pooling.
/// Also sets the popup's text and disables the number.
///
/// The worldspace position of this popup.
/// The text displayed by this popup.
/// The spawned popup, which can be modified at runtime.
public DamageNumber Spawn(Vector3 newPosition, string newText)
{
DamageNumber newDN = Spawn(newPosition);
// Disable Number
newDN.enableNumber = false;
// Text
newDN.enableLeftText = true;
newDN.leftText = newText;
return newDN;
}
///
/// Spawns a new popup and handles pooling.
/// Also sets the popup's text and makes it follow a transform.
/// Disables the number.
///
/// The worldspace position of this popup.
/// The text displayed by this popup.
/// The transform, which this popup should follow.
/// The spawned popup, which can be modified at runtime.
public DamageNumber Spawn(Vector3 newPosition, string newText, Transform followedTransform)
{
DamageNumber newDN = Spawn(newPosition, newText);
// Following
newDN.SetFollowedTarget(followedTransform);
return newDN;
}
#endregion
#region GUI Spawn Functions
///
/// Spawns a new GUI popup and handles pooling.
/// Use this function for DamageNumberGUI only.
///
/// The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.
/// The anchored position relative to rectParent.
/// The spawned popup, which can be modified at runtime.
public DamageNumber SpawnGUI(RectTransform rectParent, Vector2 anchoredPosition)
{
DamageNumber newDN = Spawn();
// Position
newDN.SetAnchoredPosition(rectParent, anchoredPosition);
return newDN;
}
///
/// Spawns a new GUI popup and handles pooling.
/// Use this function for DamageNumberGUI only.
///
/// The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.
/// The RectTransform, which this popup's anchored position is relative to.
/// This popup's anchored position relative to rectPosition.
/// The spawned popup, which can be modified at runtime.
public DamageNumber SpawnGUI(RectTransform rectParent, RectTransform rectPosition, Vector2 anchoredPosition)
{
DamageNumber newDN = Spawn();
// Position
newDN.SetAnchoredPosition(rectParent, rectPosition, anchoredPosition);
return newDN;
}
///
/// Spawns a new GUI popup and handles pooling.
/// Use this function for DamageNumberGUI only.
///
/// The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.
/// The anchored position relative to rectParent.
/// The displayed number of this popup.
/// The spawned popup, which can be modified at runtime.
public DamageNumber SpawnGUI(RectTransform rectParent, Vector2 anchoredPosition, float newNumber)
{
DamageNumber newDN = SpawnGUI(rectParent, anchoredPosition);
// Number
newDN.enableNumber = true;
newDN.number = newNumber;
return newDN;
}
///
/// Spawns a new GUI popup and handles pooling.
/// Use this function for DamageNumberGUI only.
///
/// The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.
/// The RectTransform, which this popup's anchored position is relative to.
/// This popup's anchored position relative to rectPosition.
/// The displayed number of this popup.
/// The spawned popup, which can be modified at runtime.
public DamageNumber SpawnGUI(RectTransform rectParent, RectTransform rectPosition, Vector2 anchoredPosition, float newNumber)
{
DamageNumber newDN = SpawnGUI(rectParent, rectPosition, anchoredPosition);
// Number
newDN.enableNumber = true;
newDN.number = newNumber;
return newDN;
}
///
/// Spawns a new GUI popup and handles pooling.
/// Use this function for DamageNumberGUI only.
///
/// The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.
/// The anchored position relative to rectParent.
/// The text displayed by this popup.
/// The spawned popup, which can be modified at runtime.
public DamageNumber SpawnGUI(RectTransform rectParent, Vector2 anchoredPosition, string newText)
{
DamageNumber newDN = SpawnGUI(rectParent, anchoredPosition);
// Disable Number
newDN.enableNumber = false;
// Text
newDN.enableLeftText = true;
newDN.leftText = newText;
return newDN;
}
///
/// Spawns a new GUI popup and handles pooling.
/// Use this function for DamageNumberGUI only.
///
/// The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.
/// The RectTransform, which this popup's anchored position is relative to.
/// This popup's anchored position relative to rectPosition.
/// The text displayed by this popup.
/// The spawned popup, which can be modified at runtime.
public DamageNumber SpawnGUI(RectTransform rectParent, RectTransform rectPosition, Vector2 anchoredPosition, string newText)
{
DamageNumber newDN = SpawnGUI(rectParent, rectPosition, anchoredPosition);
// Disable Number
newDN.enableNumber = false;
// Text
newDN.enableLeftText = true;
newDN.leftText = newText;
return newDN;
}
#endregion
#region Removed Functions
/*
public DamageNumber Spawn(Vector3 newPosition, float newNumber, Color newColor)
{
DamageNumber newDN = Spawn(newPosition, newNumber);
// Position
newDN.SetPosition(newPosition);
// Number
newDN.number = newNumber;
// Color
newDN.SetColor(newColor);
return newDN;
}
public DamageNumber Spawn(Vector3 newPosition, string newLeftText, Color newColor)
{
DamageNumber newDN = Spawn();
// Position
newDN.SetPosition(newPosition);
// Number
newDN.enableLeftText = true;
newDN.leftText = newLeftText;
// Color
newDN.SetColor(newColor);
return newDN;
}
public DamageNumber Spawn(Vector3 newPosition, float newNumber, Transform followedTransform, Color newColor)
{
DamageNumber newDN = Spawn();
// Position
newDN.SetPosition(newPosition);
// Text
newDN.number = newNumber;
// Following
newDN.SetFollowedTarget(followedTransform);
// Color
newDN.SetColor(newColor);
return newDN;
}
public DamageNumber Spawn(Vector3 newPosition, string newLeftText, Transform followedTransform, Color newColor)
{
DamageNumber newDN = Spawn();
// Position
newDN.SetPosition(newPosition);
// Text
newDN.enableLeftText = true;
newDN.leftText = newLeftText;
// Following
newDN.SetFollowedTarget(followedTransform);
// Color
newDN.SetColor(newColor);
return newDN;
}
public DamageNumber Spawn(Transform rectParent, Vector2 anchoredPosition)
{
DamageNumber newDN = Spawn();
// Position
newDN.SetAnchoredPosition(rectParent, anchoredPosition);
return newDN;
}
public DamageNumber Spawn(Transform rectParent, Vector2 anchoredPosition, float number)
{
DamageNumber newDN = Spawn();
// Number
newDN.number = number;
// Position
newDN.SetAnchoredPosition(rectParent, anchoredPosition);
return newDN;
}
public DamageNumber Spawn(Transform rectParent, Transform rectPosition, Vector2 anchoredPosition, float number)
{
DamageNumber newDN = Spawn();
// Number
newDN.number = number;
// Position
newDN.SetAnchoredPosition(rectParent, rectPosition, anchoredPosition);
return newDN;
}
public DamageNumber Spawn(Transform rectParent, Transform rectPosition, Vector2 anchoredPosition)
{
DamageNumber newDN = Spawn();
// Position
newDN.SetAnchoredPosition(rectParent, rectPosition, anchoredPosition);
return newDN;
}
*/
#endregion
#region Public Functions
///
/// Makes the damage number follow a transform.
/// Can also modify the spamGroup so that only damage numbers following this taget interact with each other.
///
public void SetFollowedTarget(Transform followedTransform, bool modifySpamGroup = true)
{
// Following
enableFollowing = true;
followedTarget = followedTransform;
// Spam Group
if (modifySpamGroup)
{
spamGroup += followedTransform.GetInstanceID();
}
}
public void SetColor(Color newColor)
{
// References
GetReferencesIfNecessary();
// Set Color
foreach (TMP_Text tmp in GetTextMeshs())
{
tmp.color = newColor;
}
}
public void SetGradientColor(VertexGradient newGradient)
{
// References
GetReferencesIfNecessary();
// Set Gradient
foreach (TMP_Text tmp in GetTextMeshs())
{
tmp.enableVertexGradient = true;
tmp.colorGradient = newGradient;
}
}
public void SetRandomColor(Color from, Color to)
{
SetColor(Color.Lerp(from, to, Random.value));
}
public void SetRandomColor(Gradient gradient)
{
SetColor(gradient.Evaluate(Random.value));
}
public void SetGradientColor(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight)
{
VertexGradient newGradient = new VertexGradient();
newGradient.topLeft = topLeft;
newGradient.topRight = topRight;
newGradient.bottomLeft = bottomLeft;
newGradient.bottomRight = bottomRight;
SetGradientColor(newGradient);
}
public void SetFontMaterial(TMP_FontAsset font)
{
// References
GetReferencesIfNecessary();
// Set Font
foreach (TMP_Text tmp in GetTextMeshs())
{
tmp.font = font;
}
}
public TMP_FontAsset GetFontMaterial()
{
// References
GetReferencesIfNecessary();
// Get Font
foreach (TMP_Text tmp in GetTextMeshs())
{
if (tmp.font != null)
{
return tmp.font;
}
}
return null;
}
public void SetScale(float newScale)
{
originalScale = transform.localScale = new Vector3(newScale, newScale, newScale);
}
public virtual Vector3 GetUpVector()
{
return DNPUpdater.upVector;
}
public virtual Vector3 GetRightVector()
{
return DNPUpdater.rightVector;
}
public virtual Vector3 GetFreshUpVector()
{
return transform.up;
}
public virtual Vector3 GetFreshRightVector()
{
return transform.right;
}
#endregion
#region Utility Functions
public virtual void GetReferences()
{
baseAlpha = 1f;
textMeshPro = transform.Find("TMP").GetComponent();
textMeshRenderer = textMeshPro.GetComponent();
transformA = transform.Find("MeshA");
transformB = transform.Find("MeshB");
meshRendererA = transformA.GetComponent();
meshRendererB = transformB.GetComponent();
meshFilterA = transformA.GetComponent();
meshFilterB = transformB.GetComponent();
subMeshRenderers = new List>();
subMeshFilters = new List>();
Transform parentA = meshRendererA.transform;
Transform parentB = meshRendererB.transform;
for (int n = 0; n < parentA.childCount; n++)
{
Transform childA = parentA.GetChild(n);
Transform childB = parentB.GetChild(n);
subMeshRenderers.Add(new System.Tuple(childA.GetComponent(), childB.GetComponent()));
subMeshFilters.Add(new System.Tuple(childA.GetComponent(), childB.GetComponent()));
}
}
public virtual void GetReferencesIfNecessary()
{
if (textMeshPro == null || subMeshRenderers == null)
{
GetReferences();
}
}
///
/// Starts fading out this damage number.
/// Use this to fade out damage numbers early.
///
public void FadeOut()
{
permanent = false;
startLifeTime = -1000;
}
///
/// Restarts the fade-in animation.
/// Could be used for fixed gui texts with permanent lifetime.
///
public void FadeIn()
{
currentFade = 0;
}
///
/// Returns 1 text mesh pro component on the mesh version.
/// Returns 2 text mesh pro components on the gui version.
///
///
public virtual TMP_Text[] GetTextMeshs()
{
return new TMP_Text[] { textMeshPro };
}
///
/// Returns the text mesh pro component.
/// Use this to change text mesh pro settings at runtime.
///
///
public virtual TMP_Text GetTextMesh()
{
return textMeshPro;
}
public virtual Material[] GetSharedMaterials()
{
return textMeshRenderer.sharedMaterials;
}
public virtual Material[] GetMaterials()
{
return textMeshRenderer.materials;
}
public virtual Material GetSharedMaterial()
{
return textMeshRenderer.sharedMaterial;
}
public virtual Material GetMaterial()
{
return textMeshRenderer.material;
}
///
/// Input Time.time or Time.unscaledTime (depending on the time setting in main settings).
/// Returns if damage number is still alive.
///
///
///
public virtual bool IsAlive(float time)
{
if (permanent)
{
return true;
}
return time - startLifeTime < currentLifetime;
}
///
/// Use this function to manually destroy a damage number.
/// This will also handle pooling.
///
public void DestroyDNP()
{
// Event
OnDespawn?.Invoke();
// Pooling / Destroying
if (enablePooling && originalPrefab != null)
{
if (pools == null)
{
pools = new Dictionary>();
}
if (!pools.ContainsKey(poolingID))
{
pools.Add(poolingID, new HashSet());
}
// Static Reference
if (activeInstances != null && activeInstances.Contains(this))
{
activeInstances.Remove(this);
}
// Updater
DNPUpdater.UnregisterPopup(unscaledTime, updateDelay, this);
// Remove from dictionaries
RemoveFromDictionary();
// Pooling
if (pools[poolingID].Count < poolSize)
{
PreparePooling();
}
else
{
Destroy(gameObject); // Not enough pool space
}
}
else
{
Destroy(gameObject);
}
}
public virtual void CheckAndEnable3D()
{
// Dimension Check
Camera camera = Camera.main;
if (camera == null)
{
camera = Camera.current;
}
if (camera != null)
{
if (!camera.orthographic)
{
enable3DGame = true;
}
}
}
///
/// Returns true if this is a Mesh or false if this is a GUI Component.
///
///
public virtual bool IsMesh()
{
return true;
}
public static GameObject NewMesh(string tmName, Transform parent)
{
// GameObject
GameObject newTM = new GameObject();
newTM.name = tmName;
newTM.layer = parent.gameObject.layer;
// Mesh
MeshRenderer mr = newTM.AddComponent();
newTM.AddComponent();
mr.receiveShadows = false;
mr.allowOcclusionWhenDynamic = false;
mr.shadowCastingMode = ShadowCastingMode.Off;
mr.lightProbeUsage = LightProbeUsage.Off;
mr.reflectionProbeUsage = ReflectionProbeUsage.Off;
// Transform
Transform transform = newTM.transform;
transform.SetParent(parent, true);
transform.localPosition = Vector3.zero;
transform.localScale = Vector3.one;
transform.localEulerAngles = Vector3.zero;
return newTM;
}
#endregion
#region Pooling
///
/// Use this function ONCE PER PREFAB to prewarm it's pool at the start of your game.
/// It will generate enough damage numbers to fill the pool size.
///
public void PrewarmPool()
{
if (enablePooling)
{
int instanceId = GetInstanceID();
// Initialize Dictionary
if (pools == null)
{
pools = new Dictionary>();
}
// Initialize Pool
if (!pools.ContainsKey(instanceId))
{
pools.Add(instanceId, new HashSet());
}
// Fill Pool
int amount = poolSize - pools[instanceId].Count;
if(amount > poolSize * 0.5f)
{
for (int n = 0; n < amount; n++)
{
DamageNumber dn = Spawn(new Vector3(-9999, -9999, 0));
dn.destroyAfterSpawning = true;
}
}
}
}
///
/// Clears all pooled popups.
///
/// The type of pooled popups to clear. All by default.
public static void ClearPooled(DNPType type = DNPType.All)
{
// Check if pools exist
if(pools != null)
{
// Iterate through pools
foreach (KeyValuePair> entry in pools)
{
// Check if pool contains popups
if (entry.Value != null)
{
// Iterate through the pool's popups
foreach (DamageNumber popup in entry.Value)
{
// Check type
if (type == DNPType.All || (popup.IsMesh() == (type == DNPType.Mesh)))
{
// Destroy pooled popup
Destroy(popup.gameObject);
}
}
}
}
}
}
///
/// Destroys all currently active popups.
///
/// The type of popup to destroy. All by default.
/// Whether destroyed popups are allowed to pool.
/// Whether permanent popups should be destroyed.
public static void ClearActive(DNPType type = DNPType.All, bool allowPooling = true, bool ignorePermanent = true)
{
if (allowPooling)
{
// Get all active popups
List popups = new List();
foreach (DamageNumber dn in activeInstances)
{
if (dn != null)
{
popups.Add(dn);
}
}
// Destroy all active popups with potential pooling
foreach(DamageNumber popup in popups)
{
// Check permanent
if (!ignorePermanent || !popup.permanent)
{
// Check type
if (type == DNPType.All || (popup.IsMesh() == (type == DNPType.Mesh)))
{
popup.DestroyDNP();
}
}
}
}
else
{
foreach (DamageNumber popup in activeInstances)
{
if (popup != null)
{
// Check permanent
if (!ignorePermanent || !popup.permanent)
{
// Check type
if (type == DNPType.All || (popup.IsMesh() == (type == DNPType.Mesh)))
{
Destroy(popup.gameObject);
}
}
}
}
}
}
///
/// Fades out all currently active popups.
///
/// The type of the popup to fade out. All by default.
/// Whether permanent popups should be faded out.
public static void FadeOutActive(DNPType type = DNPType.All, bool ignorePermanent = true)
{
foreach(DamageNumber popup in activeInstances)
{
if (popup != null)
{
// Check permanent
if (!ignorePermanent || !popup.permanent)
{
// Check type
if (type == DNPType.All || (popup.IsMesh() == (type == DNPType.Mesh)))
{
popup.FadeOut();
}
}
}
}
}
protected void Restart()
{
// Static Reference
if(activeInstances == null)
{
activeInstances = new HashSet();
}
if (activeInstances.Contains(this) == false)
{
activeInstances.Add(this);
}
// Updater
DNPUpdater.RegisterPopup(unscaledTime, updateDelay, this);
// Get Scale
if (originalScale.x < 0.02f)
{
originalScale = transform.localScale;
}
// Fix Fading Scale
transformA.localScale = transformB.localScale = Vector3.one;
// Event
OnSpawn?.Invoke();
// Custom Event
InternalOnSpawn();
#region Fallback Fix
if (IsMesh())
{
// Create fallback dictionary
if (fallbackDictionary == null)
{
fallbackDictionary = new Dictionary();
}
// Get font material
TMP_FontAsset fontAsset = GetFontMaterial();
// Check if in dictionary
if (!fallbackDictionary.ContainsKey(fontAsset) && fontAsset != null)
{
bool usesFallbackFonts = fontAsset.fallbackFontAssetTable != null && fontAsset.fallbackFontAssetTable.Count > 0;
if (fontAsset.isMultiAtlasTexturesEnabled || usesFallbackFonts)
{
// New tmp for fallback assets
GameObject fallbackAsset = Instantiate(textMeshPro.gameObject);
fallbackAsset.transform.localScale = Vector3.zero;
fallbackAsset.SetActive(true);
fallbackAsset.hideFlags = HideFlags.HideAndDontSave;
DontDestroyOnLoad(fallbackAsset);
// Create base string containing a single character of the base font
string textString = System.Char.ConvertFromUtf32((int) fontAsset.characterTable[0].unicode);
// Add all characters to support multi-atlas fonts
if (fontAsset.isMultiAtlasTexturesEnabled)
{
foreach (TMP_Character character in fontAsset.characterTable)
{
if(character != null && character.unicode > 0)
{
textString += System.Char.ConvertFromUtf32((int)character.unicode);
}
}
}
// Create a new string containing various unicode characters of the fallback fonts
if (usesFallbackFonts)
{
for (int f = 0; f < fontAsset.fallbackFontAssetTable.Count; f++)
{
TMP_FontAsset fallbackFont = fontAsset.fallbackFontAssetTable[f];
if (fallbackFont != null && fallbackFont.characterTable != null)
{
bool success = false;
foreach (TMP_Character fallbackCharacter in fallbackFont.characterTable)
{
if (fallbackCharacter != null)
{
if (AddFallbackCharacterToString(ref textString, fallbackCharacter.unicode, fontAsset, f))
{
success = true;
break;
}
}
}
if (!success && fallbackFont.atlasPopulationMode == AtlasPopulationMode.Dynamic)
{
for (int unicode = 0; unicode < 40959; unicode++)
{
if (fallbackFont.TryAddCharacters(new uint[] { (uint) unicode }))
{
if (AddFallbackCharacterToString(ref textString, (uint)unicode, fontAsset, f))
{
break;
}
}
}
}
}
}
}
// Assign text and add to dictionary
fallbackAsset.GetComponent().text = textString;
fallbackDictionary.Add(fontAsset, fallbackAsset);
}
else
{
fallbackDictionary.Add(fontAsset, null);
}
}
}
#endregion
// Called right after spawn
float time = unscaledTime ? Time.unscaledTime : Time.time;
Initialize(time);
// Spam Control
TriggerSpamControl();
// Scale to Zero
firstFrameScale = true;
if(destroyAfterSpawning)
{
destroyAfterSpawning = false;
startLifeTime = -100;
}
}
bool AddFallbackCharacterToString(ref string textString, uint unicode, TMP_FontAsset mainFontAsset, int fallbackIndex)
{
// The unicode 0 can cause issues
if(unicode < 1)
{
return false;
}
if (mainFontAsset.characterLookupTable.ContainsKey(unicode) || (mainFontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic && mainFontAsset.TryAddCharacters(new uint[] { unicode })))
{
// Character already in main font
}
else
{
bool addCharacter = true;
for (int pF = 0; pF < fallbackIndex; pF++)
{
TMP_FontAsset previousFallbackFont = mainFontAsset.fallbackFontAssetTable[pF];
if (previousFallbackFont != null)
{
if (previousFallbackFont.characterLookupTable.ContainsKey(unicode) || (previousFallbackFont.atlasPopulationMode == AtlasPopulationMode.Dynamic && previousFallbackFont.TryAddCharacters(new uint[] { unicode })))
{
// Character already in a higher priority fallback font
addCharacter = false;
break;
}
}
}
if (addCharacter)
{
textString += System.Char.ConvertFromUtf32((int) unicode);
return true;
}
}
return false;
}
void Initialize(float time)
{
numberScale = destructionScale = combinationScale = currentFollowSpeed = 1f;
baseAlpha = 1f;
finalPosition = position = GetPosition();
startLifeTime = startTime = time;
currentLifetime = lifetime;
isFadingOut = false;
// 3D Game
if (enable3DGame)
{
if (cameraOverride != null)
{
targetCamera = cameraOverride;
}
else if (Camera.main != null)
{
targetCamera = Camera.main.transform;
}
else if (Camera.current != null)
{
targetCamera = Camera.current.transform;
}
// Scale with FOV
if (scaleWithFov)
{
if (fovCamera != null)
{
targetFovCamera = fovCamera;
}
else if (Camera.main != null)
{
targetFovCamera = Camera.main;
}
else if (Camera.current != null)
{
targetFovCamera = Camera.current;
}
}
}
// Orthographic Scaling
if (enableOrthographicScaling)
{
if (orthographicCamera != null)
{
targetOrthographicCamera = orthographicCamera;
}
else if (Camera.main != null)
{
targetOrthographicCamera = Camera.main;
}
else if (Camera.current != null)
{
targetOrthographicCamera = Camera.current;
}
}
// Scale
UpdateScaleAnd3D(true);
// Rotation
if (enableRotateOverTime)
{
currentRotationSpeed = Random.Range(minRotationSpeed, maxRotationSpeed);
if(rotationSpeedRandomFlip && Random.value < 0.5f)
{
currentRotationSpeed *= -1;
}
}
// Spam Groups
AddToDictionary();
// Lerp
if (enableLerp)
{
float xOffset = Random.Range(lerpSettings.minX, lerpSettings.maxX) * GetPositionFactor();
if(lerpSettings.randomFlip && Random.value < 0.5f)
{
xOffset = -xOffset;
}
remainingOffset = GetFreshRightVector() * xOffset + GetFreshUpVector() * (Random.Range(lerpSettings.minY, lerpSettings.maxY) * GetPositionFactor());
}
// Velocity
if (enableVelocity)
{
currentVelocity = new Vector2(Random.Range(velocitySettings.minX, velocitySettings.maxX), Random.Range(velocitySettings.minY, velocitySettings.maxY)) * GetPositionFactor();
if(velocitySettings.randomFlip && Random.value < 0.5f)
{
currentVelocity.x = -currentVelocity.x;
}
}
// Start Rotation
if (enableStartRotation)
{
currentRotation = Random.Range(minRotation, maxRotation);
if (rotationRandomFlip && Random.value < 0.5f)
{
currentRotation *= -1;
}
}
else
{
currentRotation = 0;
}
// Fading
currentFade = durationFadeIn > 0f ? 0f : 1f;
// Fade In
fadeInSpeed = 1f / Mathf.Max(0.0001f, durationFadeIn);
if (enableCrossScaleFadeIn)
{
currentScaleInOffset = crossScaleFadeIn;
if (currentScaleInOffset.x == 0) currentScaleInOffset.x += 0.001f;
if (currentScaleInOffset.y == 0) currentScaleInOffset.y += 0.001f;
}
// Fade Out
fadeOutSpeed = 1f / Mathf.Max(0.0001f, durationFadeOut);
if (enableCrossScaleFadeOut)
{
currentScaleOutOffset = crossScaleFadeOut;
if (currentScaleOutOffset.x == 0) currentScaleOutOffset.x += 0.001f;
if (currentScaleOutOffset.y == 0) currentScaleOutOffset.y += 0.001f;
}
lastTargetPosition = Vector3.zero;
// Update Text
UpdateText();
// Update Rotation
UpdateRotationZ();
}
void PreparePooling()
{
// Add to Pool
pools[poolingID].Add(this);
// Disable GameObject
gameObject.SetActive(false);
// Queue Restart
performRestart = true;
// Reset Runtime Variables
transform.localScale = originalScale;
lastTargetPosition = targetOffset = Vector3.zero;
// Clear Combination Targets
myAbsorber = null;
// Reset some Setting Variables
permanent = originalPrefab.permanent;
spamGroup = originalPrefab.spamGroup;
leftText = originalPrefab.leftText;
rightText = originalPrefab.rightText;
followedTarget = originalPrefab.followedTarget;
enableCollision = originalPrefab.enableCollision;
enablePush = originalPrefab.enablePush;
}
bool PoolAvailable(int id)
{
if (pools != null && pools.ContainsKey(id))
{
if (pools[id].Count > 0)
{
return true;
}
}
return false;
}
void SetPoolingID(int id)
{
poolingID = id;
// Initiate Dictionaries
if (pools == null)
{
pools = new Dictionary>();
}
// Initiate Pool Parent
if (poolParent == null)
{
GameObject poolGameobject = new GameObject("Damage Number Pool");
DontDestroyOnLoad(poolGameobject);
poolParent = poolGameobject.transform;
poolParent.localScale = Vector3.one;
poolParent.eulerAngles = poolParent.position = Vector3.zero;
}
// Parent
transform.SetParent(poolParent, true);
}
#endregion
#region Text
public void UpdateText()
{
// Number
string numberText = "";
if (enableNumber)
{
string numberString;
bool shortened;
if (digitSettings.decimals <= 0)
{
numberString = ProcessIntegers(Mathf.Round(number).ToString("F0"), out shortened);
}
else
{
// Digits
string allDigits = (Mathf.Abs(number) * Mathf.Pow(10, digitSettings.decimals)).ToString("F0");
bool hasMinus = number < 0;
// Add zeros to the left to fix numbers less than 1
int usedDecimals = digitSettings.decimals;
int currentLength = allDigits.Length;
if(currentLength < usedDecimals)
{
for(int i = 0; i < usedDecimals - currentLength; i++)
{
allDigits = "0" + allDigits;
}
}
while (digitSettings.hideZeros && allDigits.EndsWith("0") && usedDecimals > 0)
{
allDigits = allDigits.Substring(0, allDigits.Length - 1);
usedDecimals--;
}
string integers = allDigits.Substring(0, Mathf.Max(0, allDigits.Length - usedDecimals));
integers = ProcessIntegers(integers, out shortened);
if (integers == "")
{
integers = "0";
}
int digitLength = allDigits.Length;
while (digitLength < usedDecimals)
{
if(digitSettings.hideZeros)
{
usedDecimals--;
}
else
{
allDigits += "0";
digitLength = allDigits.Length;
}
}
string decimals = allDigits.Substring(allDigits.Length - usedDecimals);
if (usedDecimals > 0 && !shortened)
{
numberString = integers + digitSettings.decimalChar + decimals;
}
else
{
numberString = integers;
}
if (hasMinus)
{
numberString = "-" + numberString;
}
}
numberText = ApplyTextSettings(numberString, numberSettings);
if (enableScaleByNumber)
{
numberScale = scaleByNumberSettings.fromScale + (scaleByNumberSettings.toScale - scaleByNumberSettings.fromScale) * Mathf.Clamp01((number - scaleByNumberSettings.fromNumber) / (scaleByNumberSettings.toNumber - scaleByNumberSettings.fromNumber));
}
if(enableColorByNumber)
{
SetColor(colorByNumberSettings.colorGradient.Evaluate(Mathf.Clamp01((number - colorByNumberSettings.fromNumber) / (colorByNumberSettings.toNumber - colorByNumberSettings.fromNumber))));
}
}
// Prefix
string prefixText = "";
if (enableTopText)
{
prefixText += ApplyTextSettings(topText, topTextSettings) + System.Environment.NewLine;
}
if (enableLeftText)
{
prefixText += ApplyTextSettings(leftText, leftTextSettings);
}
// Suffix
string suffixText = "";
if (enableRightText)
{
suffixText += ApplyTextSettings(rightText, rightTextSettings);
}
if (enableBottomText)
{
suffixText += System.Environment.NewLine + ApplyTextSettings(bottomText, bottomTextSettings);
}
GetReferencesIfNecessary();
// Scale Fix
Vector3 currentLocalScale = transform.localScale;
if(!enable3DGame || !renderThroughWalls)
{
renderThroughWallsScale = 1f;
}
if (lastScaleFactor < 1)
{
lastScaleFactor = 1f;
}
float minScale = renderThroughWallsScale * lastScaleFactor;
if (currentLocalScale.x < minScale)
{
transform.localScale = new Vector3(minScale, minScale, minScale);
}
// Update Text
SetTextString(prefixText + numberText + suffixText);
transform.localScale = currentLocalScale;
// Get Colors
colors = new List();
alphas = new List();
for (int n = 0; n < meshs.Count; n++)
{
Mesh mesh = meshs[n];
if(mesh != null)
{
Color[] color = mesh.colors;
float[] alpha = new float[color.Length];
for (int c = 0; c < color.Length; c++)
{
alpha[c] = color[c].a;
}
alphas.Add(alpha);
colors.Add(color);
}
else
{
colors.Add(new Color[0]);
alphas.Add(new float[0]);
}
}
// Finish
UpdateAlpha(currentFade);
// Event
OnUpdateText?.Invoke();
}
protected virtual void SetTextString(string fullString)
{
// Generate Mesh
textMeshPro.gameObject.SetActive(true);
textMeshPro.text = fullString;
textMeshPro.ForceMeshUpdate();
// Clear Meshs
ClearMeshs();
meshs = new List();
meshs.Add(Instantiate(textMeshPro.mesh));
meshFilterA.mesh = meshFilterB.mesh = meshs[0];
meshRendererA.sharedMaterials = meshRendererB.sharedMaterials = textMeshRenderer.sharedMaterials;
// Submeshes
int usedSubMeshes = 0;
for (int c = 0; c < textMeshPro.transform.childCount; c++)
{
MeshRenderer subMeshRenderer = textMeshPro.transform.GetChild(c).GetComponent();
if (subMeshRenderer != null)
{
MeshFilter subMeshFilter = subMeshRenderer.GetComponent();
// Create new Submesh
if (subMeshRenderers.Count <= c)
{
GameObject subMeshA = NewMesh("Sub", meshRendererA.transform);
GameObject subMeshB = NewMesh("Sub", meshRendererB.transform);
subMeshRenderers.Add(new System.Tuple(subMeshA.GetComponent(), subMeshB.GetComponent()));
subMeshFilters.Add(new System.Tuple(subMeshA.GetComponent(), subMeshB.GetComponent()));
}
// Apply
System.Tuple subRenderers = subMeshRenderers[c];
System.Tuple subFilters = subMeshFilters[c];
subRenderers.Item1.sharedMaterials = subRenderers.Item2.sharedMaterials = subMeshRenderer.sharedMaterials;
Mesh newSubMesh = Instantiate(subMeshFilter.sharedMesh);
subFilters.Item1.mesh = subFilters.Item2.mesh = newSubMesh;
meshs.Add(newSubMesh);
// Enable
subRenderers.Item1.transform.localScale = subRenderers.Item2.transform.localScale = Vector3.one;
usedSubMeshes++;
}
}
// Hide Unused Meshs
for (int n = usedSubMeshes; n < subMeshRenderers.Count; n++)
{
subMeshRenderers[n].Item1.transform.localScale = subMeshRenderers[n].Item2.transform.localScale = Vector3.zero;
}
// Disable TMP
textMeshPro.gameObject.SetActive(false);
}
string ProcessIntegers(string integers, out bool shortened)
{
shortened = false;
// Short Suffix
if (digitSettings.suffixShorten)
{
int currentSuffix = -1;
int integerLength = integers.Length;
while (integerLength > digitSettings.maxDigits && currentSuffix < digitSettings.suffixes.Count - 1 && integerLength - digitSettings.suffixDigits[currentSuffix + 1] > 0)
{
currentSuffix++;
integerLength -= digitSettings.suffixDigits[currentSuffix];
}
if (currentSuffix >= 0)
{
// Get shortened integers
string shortenedIntegers = integers.Substring(0, integerLength);
if (digitSettings.suffixDecimals > 0)
{
// Get decimals after the shortened integers
string decimals = integers.Substring(integerLength, digitSettings.suffixDecimals);
// Hide zeros
while(digitSettings.suffixHideZeros && decimals.EndsWith("0"))
{
decimals = decimals.Substring(0, decimals.Length - 1);
}
// Combine
if(decimals.Length > 0)
{
integers = shortenedIntegers + digitSettings.suffixDecimalChar + decimals + digitSettings.suffixes[currentSuffix];
}
else
{
integers = shortenedIntegers + digitSettings.suffixes[currentSuffix];
}
}
else
{
integers = shortenedIntegers + digitSettings.suffixes[currentSuffix];
}
shortened = true;
return integers;
}
}
// Dots
if (digitSettings.dotSeparation && digitSettings.dotDistance > 0)
{
char[] chars = integers.ToCharArray();
integers = "";
for (int n = chars.Length - 1; n > -1; n--)
{
integers = chars[n] + integers;
if ((chars.Length - n) % digitSettings.dotDistance == 0 && n > 0)
{
integers = digitSettings.dotChar + integers;
}
}
}
return integers;
}
string ApplyTextSettings(string text, TextSettings settings)
{
string newString = text;
if (text == "")
{
return "";
}
// Formatting
if (settings.bold)
{
newString = "" + newString + "";
}
if (settings.italic)
{
newString = "" + newString + "";
}
if (settings.underline)
{
newString = "" + newString + "";
}
if (settings.strike)
{
newString = "" + newString + "";
}
// Custom Color
if (settings.customColor)
{
newString = "" + newString + "";
}
if (settings.mark)
{
newString = "" + newString + "";
}
if (settings.alpha < 1)
{
newString = "" + newString + "";
}
// Change Size
if (settings.size > 0)
{
newString = "" + newString + "";
}
else if (settings.size < 0)
{
newString = "" + newString + "";
}
// Character Spacing
if (settings.characterSpacing != 0)
{
newString = "" + newString + "";
}
// Spacing
if (settings.horizontal > 0)
{
string space = "";
newString = space + newString + space;
}
if (settings.vertical != 0)
{
newString = "" + newString + "";
}
// Return
return newString;
}
private void ClearMeshs()
{
if (meshs != null)
{
if (Application.isPlaying)
{
foreach (Mesh mesh in meshs)
{
Destroy(mesh);
}
}
else
{
foreach (Mesh mesh in meshs)
{
DestroyImmediate(mesh);
}
}
}
}
#endregion
#region Fading
void HandleFadeIn(float delta)
{
if (currentFade < 1)
{
currentFade = Mathf.Min(1, currentFade + delta * fadeInSpeed);
UpdateFade(enableOffsetFadeIn, offsetFadeIn, enableCrossScaleFadeIn, currentScaleInOffset, enableScaleFadeIn, scaleFadeIn, enableShakeFadeIn, shakeOffsetFadeIn, shakeFrequencyFadeIn);
}
}
void HandleFadeOut(float delta)
{
// Event
if (isFadingOut == false)
{
isFadingOut = true;
OnFadeOut?.Invoke();
}
// Update fade value
currentFade = Mathf.Max(0, currentFade - delta * fadeOutSpeed);
UpdateFade(enableOffsetFadeOut, offsetFadeOut, enableCrossScaleFadeOut, currentScaleOutOffset, enableScaleFadeOut, scaleFadeOut, enableShakeFadeOut, shakeOffsetFadeOut, shakeFrequencyFadeOut);
// Remove from dictionaries
RemoveFromDictionary();
if (currentFade <= 0)
{
DestroyDNP();
}
}
void UpdateFade(bool enablePositionOffset, Vector2 positionOffset, bool enableScaleOffset, Vector2 scaleOffset, bool enableScale, Vector2 scale, bool enableShake, Vector2 shakeOffset, float shakeFrequency)
{
Vector2 basePosition = Vector2.zero;
float inverseFade = currentFade - 1;
if (enableShake)
{
basePosition = shakeOffset * Mathf.Sin(inverseFade * shakeFrequency) * inverseFade;
}
// Position Offset
if (enablePositionOffset)
{
Vector2 posOffset = positionOffset * inverseFade;
SetLocalPositionA(basePosition + posOffset);
SetLocalPositionB(basePosition - posOffset);
}
else
{
SetLocalPositionA(basePosition);
SetLocalPositionB(basePosition);
}
// Scale & Scale Offset
if (enableScaleOffset)
{
if (enableScale)
{
Vector3 scaleA = Vector2.Lerp(scaleOffset * scale, Vector2.one, currentFade);
scaleA.z = 1;
Vector3 scaleB = Vector2.Lerp(new Vector3(1f / scaleOffset.x, 1f / scaleOffset.y, 1) * scale, Vector2.one, currentFade);
scaleB.z = 1;
transformA.localScale = scaleA;
transformB.localScale = scaleB;
}
else
{
Vector3 scaleA = Vector2.Lerp(scaleOffset, Vector2.one, currentFade);
scaleA.z = 1;
Vector3 scaleB = Vector2.Lerp(new Vector3(1f / scaleOffset.x, 1f / scaleOffset.y, 1), Vector2.one, currentFade);
scaleB.z = 1;
transformA.localScale = scaleA;
transformB.localScale = scaleB;
}
}
else if (enableScale)
{
Vector3 newScale = Vector2.Lerp(scale, Vector2.one, currentFade);
newScale.z = 1;
transformA.localScale = transformB.localScale = newScale;
}
// Alpha
UpdateAlpha(currentFade);
}
public void UpdateAlpha(float progress)
{
float alphaFactor = progress * progress * baseAlpha * baseAlpha;
if(meshs != null)
{
for (int n = 0; n < meshs.Count; n++)
{
if (colors[n] != null && meshs[n] != null)
{
Color[] color = colors[n];
float[] alpha = alphas[n];
for (int c = 0; c < color.Length; c++)
{
color[c].a = alphaFactor * alpha[c];
}
meshs[n].colors = color;
}
}
}
// Event
OnUpdateFade?.Invoke(progress);
// Abstract
InternalUpdateFade(progress);
}
#endregion
#region Movement
void HandleFollowing(float deltaTime)
{
// No Target
if (followedTarget == null)
{
lastTargetPosition = Vector3.zero;
return;
}
// Get Position
Vector3 targetPosition = GetOtherPosition(followedTarget);
// Get Offset
if (lastTargetPosition != Vector3.zero)
{
targetOffset += targetPosition - lastTargetPosition;
}
lastTargetPosition = targetPosition;
// Apply Drag
if (followSettings.drag > 0 && currentFollowSpeed > 0)
{
currentFollowSpeed -= followSettings.drag * deltaTime;
if (currentFollowSpeed < 0)
{
currentFollowSpeed = 0;
}
}
// Move to Target
Vector3 oldOffset = targetOffset;
targetOffset = Vector3.Lerp(targetOffset, Vector3.zero, deltaTime * followSettings.speed * currentFollowSpeed);
position += oldOffset - targetOffset;
}
void HandleLerp(float deltaTime)
{
float deltaFactor = Mathf.Min(1, deltaTime * lerpSettings.speed);
Vector3 deltaOffset = remainingOffset * deltaFactor;
remainingOffset -= deltaOffset;
position += deltaOffset;
}
void HandleVelocity(float deltaTime)
{
if (velocitySettings.dragX > 0)
{
currentVelocity.x = Mathf.Lerp(currentVelocity.x, 0, deltaTime * velocitySettings.dragX);
}
if (velocitySettings.dragY > 0)
{
currentVelocity.y = Mathf.Lerp(currentVelocity.y, 0, deltaTime * velocitySettings.dragY);
}
currentVelocity.y -= velocitySettings.gravity * deltaTime * GetPositionFactor();
position += (GetUpVector() * currentVelocity.y + GetRightVector() * currentVelocity.x) * deltaTime;
}
Vector3 ApplyShake(Vector3 vector, ShakeSettings shakeSettings, float time)
{
float currentTime = time - startTime;
float currentFactor = Mathf.Sin(shakeSettings.frequency * currentTime) * GetPositionFactor();
if (shakeSettings.offset.y != 0)
{
vector += GetUpVector() * currentFactor * shakeSettings.offset.y;
}
if (shakeSettings.offset.x != 0)
{
vector += GetRightVector() * currentFactor * shakeSettings.offset.x;
}
return vector;
}
public Vector3 GetTargetPosition()
{
return position + remainingOffset;
}
public virtual Vector3 GetPosition()
{
return transform.position;
}
protected virtual void SetLocalPositionA(Vector3 localPosition)
{
transformA.localPosition = localPosition;
}
protected virtual void SetLocalPositionB(Vector3 localPosition)
{
transformB.localPosition = localPosition;
}
public virtual void SetPosition(Vector3 newPosition)
{
position = transform.position = newPosition;
}
protected virtual Vector3 GetOtherPosition(Transform other)
{
return other.position;
}
///
/// Updates position without changing the position variable.
/// Used for temporary position changes like shaking movement.
///
protected virtual void SetFinalPosition(Vector3 newPosition)
{
transform.position = newPosition;
}
///
/// This function is for the GUI version of damage numbers pro.
/// It will position the damage number at your mouse position.
/// Leave 'canvasCamera' as 'null' if your canvas render mode is "Screen Space - Overlay"e
///
public virtual void SetToMousePosition(RectTransform rectParent, Camera canvasCamera)
{
Vector2 mousePosition = Vector3.zero;
#if ENABLE_INPUT_SYSTEM && DNP_NewInputSystem
if(Mouse.current != null) {
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectParent, Mouse.current.position.ReadValue(), canvasCamera, out mousePosition);
}
#else
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectParent, Input.mousePosition, canvasCamera, out mousePosition);
#endif
SetAnchoredPosition(rectParent, mousePosition);
}
///
/// Use this function after you spawn a GUI version of damage numbers pro.
///
public virtual void SetAnchoredPosition(Transform rectParent, Vector2 anchoredPosition)
{
// Old Transform
Vector3 oldScale = transform.localScale;
Vector3 oldRotation = transform.eulerAngles;
// Set Parent and Position
transform.SetParent(rectParent, false);
transform.position = anchoredPosition;
// New Transform
transform.localScale = oldScale;
transform.eulerAngles = oldRotation;
}
///
/// Use this function after you spawn a GUI version of damage numbers pro.
///
public virtual void SetAnchoredPosition(Transform rectParent, Transform rectPosition, Vector2 relativeAnchoredPosition)
{
// Old Transform
Vector3 oldScale = transform.localScale;
Vector3 oldRotation = transform.eulerAngles;
// Set Parent and Position
transform.SetParent(rectParent, false);
transform.position = rectPosition.position + (Vector3)relativeAnchoredPosition;
// New Transform
transform.localScale = oldScale;
transform.eulerAngles = oldRotation;
}
protected virtual float GetPositionFactor()
{
return 1f;
}
#endregion
#region Spam Control
///
/// Use this function to change the spam group of the popup.
/// Using the public spamGroup variable will also work within the spawn frame.
///
public void SetSpamGroup(string newSpamGroup)
{
RemoveFromDictionary();
spamGroup = newSpamGroup;
AddToDictionary();
}
///
/// Use this function to manually trigger spam control features like Combination, Destruction, Collision and Push.
///
public void TriggerSpamControl()
{
if (spamGroup != "")
{
float time = unscaledTime ? Time.unscaledTime : Time.time;
TryCombination(time);
TryDestruction(time);
TryCollision();
TryPush();
}
}
void AddToDictionary()
{
if (spamGroup != "")
{
// Create Dictionary
if (spamGroupDictionary == null)
{
spamGroupDictionary = new Dictionary>();
}
// Create HashSet
if (spamGroupDictionary.ContainsKey(spamGroup) == false)
{
spamGroupDictionary.Add(spamGroup, new HashSet());
}
// Add to HashSet
if (spamGroupDictionary[spamGroup].Contains(this) == false)
{
spamGroupDictionary[spamGroup].Add(this);
}
removedFromDictionary = false;
}
else
{
removedFromDictionary = true;
}
}
void RemoveFromDictionary()
{
if (!removedFromDictionary && spamGroup != "")
{
if (spamGroupDictionary != null && spamGroupDictionary.ContainsKey(spamGroup))
{
if (spamGroupDictionary[spamGroup].Contains(this))
{
spamGroupDictionary[spamGroup].Remove(this);
removedFromDictionary = true;
}
}
}
}
#endregion
#region Combination
void HandleCombination(float delta, float time)
{
if (myAbsorber != null)
{
if(myAbsorber.myAbsorber != null)
{
myAbsorber = myAbsorber.myAbsorber;
}
if (time - startTime < combinationSettings.spawnDelay)
{
absorbStartPosition = position;
absorbStartTime = time;
return;
}
// Reset Lifetime
startLifeTime = time;
// Combination Progress
float combinationProgress = combinationSettings.absorbDuration > 0 ? (time - absorbStartTime) / combinationSettings.absorbDuration : 1f;
// Move
if (combinationSettings.moveToAbsorber)
{
position = Vector3.Lerp(absorbStartPosition, myAbsorber.position, combinationProgress);
}
// Scale
combinationScale = combinationSettings.scaleCurve.Evaluate(combinationProgress);
// Alpha
baseAlpha = 1f * combinationSettings.alphaCurve.Evaluate(combinationProgress);
UpdateAlpha(currentFade);
if(combinationSettings.instantGain && combinationProgress > 0)
{
GiveNumber(time);
}
if (combinationProgress >= 1)
{
GiveNumber(time);
DestroyDNP();
}
}
}
void TryCombination(float time)
{
if (enableCombination == false) return; // No Combination
myAbsorber = null;
// Combination Methods
switch (combinationSettings.method)
{
case (CombinationMethod.ABSORB_NEW):
float oldestStartTime = time + 0.5f;
DamageNumber oldestNumber = null;
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
if (otherNumber != this && otherNumber.enableCombination && otherNumber.myAbsorber == null && otherNumber.startTime <= oldestStartTime)
{
if (Vector3.Distance(otherNumber.GetTargetPosition(), GetTargetPosition()) < combinationSettings.maxDistance * GetPositionFactor())
{
oldestStartTime = otherNumber.startTime;
oldestNumber = otherNumber;
}
}
}
if (oldestNumber != null)
{
GetAbsorbed(oldestNumber, time);
}
break;
case (CombinationMethod.REPLACE_OLD):
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
if (otherNumber != this && otherNumber.enableCombination)
{
if (Vector3.Distance(otherNumber.position, position) < combinationSettings.maxDistance * GetPositionFactor())
{
if (otherNumber.myAbsorber == null)
{
otherNumber.startTime = time - 0.01f;
}
otherNumber.GetAbsorbed(this, time);
}
}
}
break;
case (CombinationMethod.IS_ALWAYS_ABSORBER):
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
if (otherNumber != this && otherNumber.enableCombination && otherNumber.combinationSettings.method == CombinationMethod.IS_ALWAYS_VICTIM)
{
if (Vector3.Distance(otherNumber.position, position) < combinationSettings.maxDistance * GetPositionFactor())
{
if (otherNumber.myAbsorber == null)
{
otherNumber.startTime = time - 0.01f;
}
otherNumber.GetAbsorbed(this, time);
}
}
}
break;
case (CombinationMethod.IS_ALWAYS_VICTIM):
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
if (otherNumber != this && otherNumber.enableCombination && otherNumber.myAbsorber == null && otherNumber.combinationSettings.method == CombinationMethod.IS_ALWAYS_ABSORBER)
{
if (Vector3.Distance(otherNumber.GetTargetPosition(), GetTargetPosition()) < combinationSettings.maxDistance * GetPositionFactor())
{
GetAbsorbed(otherNumber, time);
break;
}
}
}
break;
}
}
void GetAbsorbed(DamageNumber otherNumber, float time)
{
if (myAbsorber != null)
{
return;
}
// Disable Other Features
otherNumber.enablePush = otherNumber.enableCollision = false;
// Set Absorber
myAbsorber = otherNumber;
// Initialize Variables
absorbStartPosition = position;
absorbStartTime = time;
givenNumber = false;
// Reset Lifetime
myAbsorber.startLifeTime = time;
// Spawn in Absorber
if (combinationSettings.teleportToAbsorber)
{
position = otherNumber.position;
}
}
void GiveNumber(float time)
{
if (!givenNumber)
{
givenNumber = true;
myAbsorber.number += number;
if (myAbsorber.myAbsorber == null)
{
myAbsorber.combinationScale = combinationSettings.absorberScaleFactor;
myAbsorber.startTime = time;
myAbsorber.currentLifetime = myAbsorber.lifetime + combinationSettings.bonusLifetime;
}
myAbsorber.UpdateText();
// Event
myAbsorber.OnAbsorb?.Invoke(number, myAbsorber.number);
}
}
#endregion
#region Destruction
void HandleDestruction(float time)
{
if (enableDestruction && isDestroyed)
{
if (time - startTime < destructionSettings.spawnDelay)
{
destructionStartTime = time;
return;
}
float destructionProgress = destructionSettings.duration > 0 ? (time - destructionStartTime) / destructionSettings.duration : 1f;
if (destructionProgress >= 1)
{
DestroyDNP();
}
else
{
baseAlpha = 1f * destructionSettings.alphaCurve.Evaluate(destructionProgress);
UpdateAlpha(currentFade);
destructionScale = destructionSettings.scaleCurve.Evaluate(destructionProgress);
}
}
}
void TryDestruction(float time)
{
if (enableDestruction)
{
isDestroyed = false;
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
if (otherNumber.isDestroyed == false && otherNumber != this && otherNumber.enableDestruction)
{
if (Vector3.Distance(otherNumber.GetTargetPosition(), GetTargetPosition()) < destructionSettings.maxDistance * GetPositionFactor())
{
otherNumber.isDestroyed = true;
otherNumber.destructionStartTime = time;
}
}
}
}
}
#endregion
#region Collision
void TryCollision()
{
if (enableCollision)
{
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
otherNumber.collided = false;
}
TryCollision(GetTargetPosition());
}
}
void TryCollision(Vector3 sourcePosition)
{
if (enableCollision)
{
collided = true;
Vector3 myTargetPosition = GetTargetPosition();
float radius = collisionSettings.radius * simulatedScale * GetPositionFactor();
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
if (otherNumber.enableCollision && otherNumber != this)
{
Vector3 otherTargetPosition = otherNumber.GetTargetPosition();
Vector3 offset = otherTargetPosition - myTargetPosition;
float distance = offset.magnitude;
if (distance < radius)
{
Vector3 offsetOrigin = otherTargetPosition - sourcePosition + offset + collisionSettings.desiredDirection * GetPositionFactor();
if (offsetOrigin == Vector3.zero)
{
offsetOrigin = new Vector3(Random.value - 0.5f, Random.value - 0.5f, Random.value - 0.5f);
}
otherNumber.remainingOffset += offsetOrigin.normalized * (radius - distance) * collisionSettings.pushFactor;
if(!otherNumber.collided)
{
otherNumber.TryCollision(sourcePosition);
}
}
}
}
}
}
#endregion
#region Push
void TryPush()
{
if (enablePush)
{
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
otherNumber.pushed = false;
}
TryPush(GetTargetPosition());
}
}
void TryPush(Vector3 sourcePosition)
{
if (enablePush)
{
pushed = true;
Vector3 myTargetPosition = GetTargetPosition();
float radius = pushSettings.radius * simulatedScale * GetPositionFactor();
DamageNumber bestPushTarget = null;
float pushDirection = pushSettings.pushOffset > 0 ? 1 : -1;
float heightCap = myTargetPosition.y + 1000 * pushDirection;
foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
{
if (otherNumber.enablePush && !otherNumber.pushed)
{
Vector3 targetPosition = otherNumber.GetTargetPosition();
if (targetPosition.y * pushDirection < heightCap* pushDirection && Vector3.Distance(myTargetPosition, targetPosition) < radius)
{
heightCap = targetPosition.y;
bestPushTarget = otherNumber;
}
}
}
if (bestPushTarget != null)
{
float heightDifference = (heightCap - myTargetPosition.y);
float pushDistance = (pushSettings.pushOffset * GetPositionFactor() - heightDifference);
bestPushTarget.remainingOffset.y += pushDirection > 0 ? Mathf.Max(pushDistance, 0) : Mathf.Min(pushDistance, 0);
bestPushTarget.TryPush(sourcePosition);
}
}
}
#endregion
#region Scale and Rotation
protected virtual void UpdateRotationZ()
{
SetRotationZ(meshRendererA.transform);
SetRotationZ(meshRendererB.transform);
}
protected void SetRotationZ(Transform transformTarget)
{
Vector3 localRotation = transformTarget.localEulerAngles;
localRotation.z = currentRotation;
transformTarget.localEulerAngles = localRotation;
}
void HandleRotateOverTime(float delta, float time)
{
currentRotation += currentRotationSpeed * delta * rotateOverTime.Evaluate((time - startTime) / currentLifetime);
}
void UpdateScaleAnd3D(bool beforeMeshBuild = false)
{
Vector3 appliedScale = originalScale;
lastScaleFactor = 1f;
// Scale Down from Combination
if (enableCombination)
{
combinationScale = Mathf.Lerp(combinationScale, 1f, Time.deltaTime * combinationSettings.absorberScaleFade);
lastScaleFactor *= combinationScale;
}
// Scale by Number Size
if (enableScaleByNumber)
{
lastScaleFactor *= numberScale;
}
// Scale over Lifetime
if (enableScaleOverTime)
{
float time = unscaledTime ? Time.unscaledTime : Time.time;
appliedScale *= scaleOverTime.Evaluate((time - startTime) / (currentLifetime + durationFadeOut));
}
// Destruction Scale
if (enableDestruction)
{
appliedScale *= destructionScale;
}
// Orthographic Scale
if (enableOrthographicScaling && !beforeMeshBuild)
{
appliedScale *= Mathf.Min(maxOrthographicSize, targetOrthographicCamera.orthographicSize / defaultOrthographicSize);
}
// Perspective
#region Perspective
if (enable3DGame && targetCamera != null)
{
// Face Camera
if(faceCameraView)
{
if(lookAtCamera)
{
transform.LookAt(transform.position + (transform.position - targetCamera.position));
}
else
{
transform.rotation = targetCamera.rotation;
}
}
// Initialize Offset
Vector3 offset = default;
float distance = default;
// Consistent Screen Size
if (consistentScreenSize)
{
// Calculate Offset
offset = finalPosition - targetCamera.position;
distance = Mathf.Max(0.004f, offset.magnitude);
// Calculate Scale
lastScaleFactor *= distance / distanceScalingSettings.baseDistance;
if (distance < distanceScalingSettings.closeDistance)
{
lastScaleFactor *= distanceScalingSettings.closeScale;
}
else if (distance > distanceScalingSettings.farDistance)
{
lastScaleFactor *= distanceScalingSettings.farScale;
}
else
{
lastScaleFactor *= distanceScalingSettings.farScale + (distanceScalingSettings.closeScale - distanceScalingSettings.farScale) * Mathf.Clamp01(1 - (distance - distanceScalingSettings.closeScale) / Mathf.Max(0.01f, distanceScalingSettings.farDistance - distanceScalingSettings.closeScale));
}
}
// FOV Scaling
if (scaleWithFov && !beforeMeshBuild)
{
lastScaleFactor *= targetFovCamera.fieldOfView / defaultFov;
}
// Apply scale
appliedScale *= lastScaleFactor;
simulatedScale = appliedScale.x;
// Render Through Walls
if (renderThroughWalls)
{
float near = 0.3f;
if (Camera.main != null)
{
near = Camera.main.nearClipPlane;
}
// Move close to camera
if (!consistentScreenSize)
{
offset = finalPosition - targetCamera.position;
distance = Mathf.Max(0.004f, offset.magnitude);
}
near += 0.0005f * distance + 0.02f + near * 0.02f * Vector3.Angle(offset, targetCamera.forward);
transform.position = offset.normalized * near + targetCamera.position;
// Adjust Scale
renderThroughWallsScale = near / distance;
appliedScale *= renderThroughWallsScale;
}
}
else
{
appliedScale *= lastScaleFactor;
simulatedScale = appliedScale.x;
}
#endregion
// Apply
transform.localScale = appliedScale;
// First Frame Fix
if (firstFrameScale)
{
if(durationFadeIn > 0)
{
transform.localScale = Vector3.zero;
}
firstFrameScale = false;
}
}
#endregion
#region C# Events
///
/// This event is called during fade in and fade out.
/// The float value of the event determines the fade progress (0 = invisible, 1 = fully faded in).
///
public event System.Action OnUpdateFade;
///
/// This event is called when the text content of the damage number has been updated.
///
public event System.Action OnUpdateText;
///
/// This event is called when the damage number starts fading out.
///
public event System.Action OnFadeOut;
///
/// This event is called when the damage number starts fading in.
///
public event System.Action OnSpawn;
///
/// This event is called when the damage number has fully faded out.
///
public event System.Action OnDespawn;
///
/// Called when a damage number absorbs another damage number.
/// The first float value is the absorbed number and the second float value is the new sum.
///
public event System.Action OnAbsorb;
#endregion
#region Internal Events
///
/// This function is called while fading in or out.
///
/// The current fade and alpha factor meaning 0 = 0% and 1 = 100% faded in.
protected virtual void InternalUpdateFade(float currentFade)
{
}
///
/// This function is called on every update interval.
/// Frequency can be configured under performance settings.
///
protected virtual void InternalUpdate(float deltaTime)
{
}
///
/// This function is called whenever a damage number is spawned.
///
protected virtual void InternalOnSpawn()
{
}
///
/// Called every late update.
///
protected virtual void OnLateUpdate()
{
}
///
/// This event was created to fix a GUI specific issue.
///
protected virtual void InternalOnPreSpawn()
{
}
#endregion
#region Unity Events
void OnDestroy()
{
// Static Reference
if (activeInstances != null && activeInstances.Contains(this))
{
activeInstances.Remove(this);
}
// Updater
DNPUpdater.UnregisterPopup(unscaledTime, updateDelay, this);
// Remove from dictionaries
RemoveFromDictionary();
// Clear Mesh
ClearMeshs();
// Remove from Pool
if (enablePooling && pools != null)
{
if (pools.ContainsKey(poolingID))
{
if (pools[poolingID].Contains(this))
{
pools[poolingID].Remove(this);
}
}
}
if (enablePooling && disableOnSceneLoad)
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
}
void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
if(currentFade > 0)
{
DestroyDNP();
}
}
void Reset()
{
if (!Application.isPlaying)
{
CheckAndEnable3D();
}
}
#endregion
}
}