DamageNumber.cs 109 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.Rendering;
  5. using UnityEngine.Serialization;
  6. using UnityEngine.SceneManagement;
  7. using TMPro;
  8. using DamageNumbersPro.Internal;
  9. #if ENABLE_INPUT_SYSTEM && DNP_NewInputSystem
  10. using UnityEngine.InputSystem;
  11. #endif
  12. namespace DamageNumbersPro
  13. {
  14. public abstract class DamageNumber : MonoBehaviour
  15. {
  16. #region Main Settings
  17. // Lifetime
  18. [Tooltip("Damage number will not fade out on it's own.")]
  19. public bool permanent = false;
  20. [Tooltip("The lifetime after which this fades out.")]
  21. public float lifetime = 2f;
  22. [Tooltip("Ignores slow motion or game pause.")]
  23. public bool unscaledTime = false;
  24. // 3D Settings
  25. public bool enable3DGame = false;
  26. [Tooltip("Faces the camera at all times.")]
  27. public bool faceCameraView = true;
  28. [Tooltip("Uses LookAt(...) instead of the camera rotation.\nThis costs more performance but looks better in VR.\n\nNot recommended for spamming popups.")]
  29. public bool lookAtCamera = false;
  30. [Tooltip("Moves the number close to the camera and scales it down to make it look like it was visible through walls.")]
  31. public bool renderThroughWalls = true;
  32. [Tooltip("Keeps the screen-size consistent accross different distances.")]
  33. public bool consistentScreenSize = false;
  34. public DistanceScalingSettings distanceScalingSettings = new DistanceScalingSettings(0);
  35. [Tooltip("Scales the popup with the camera's field of view to keep it's size consistent.")]
  36. public bool scaleWithFov = false;
  37. [Tooltip("The default field of view, where the popup will be at it's default scale.")]
  38. public float defaultFov = 60f;
  39. [Tooltip("The camera whose field of view the popup will react to.")]
  40. public Camera fovCamera;
  41. [Tooltip("Override the camera looked at and scaled for.\nIf this set to None the Main Camera will be used.")]
  42. public Transform cameraOverride;
  43. #endregion
  44. #region Text Settings
  45. // Number
  46. public bool enableNumber = true;
  47. [Tooltip("The number displayed in the text.\nCan be disabled if you only need text.")]
  48. public float number = 1;
  49. public TextSettings numberSettings = new TextSettings(0);
  50. public DigitSettings digitSettings = new DigitSettings(0);
  51. // Left Text
  52. [FormerlySerializedAs("enablePrefix")]
  53. public bool enableLeftText = false;
  54. [Tooltip("Text displayed to the left of the number.")]
  55. [FormerlySerializedAs("prefix")]
  56. public string leftText = "";
  57. [FormerlySerializedAs("prefixSettings")]
  58. public TextSettings leftTextSettings = new TextSettings(0);
  59. // Right Text
  60. [FormerlySerializedAs("enableSuffix")]
  61. public bool enableRightText = false;
  62. [Tooltip("Text displayed to the right of the number.")]
  63. [FormerlySerializedAs("suffix")]
  64. public string rightText = "";
  65. [FormerlySerializedAs("suffixSettings")]
  66. public TextSettings rightTextSettings = new TextSettings(0);
  67. // Top Text
  68. public bool enableTopText = false;
  69. [Tooltip("Text displayed above the number.")]
  70. public string topText = "";
  71. public TextSettings topTextSettings = new TextSettings(0f);
  72. // Bottom Text
  73. public bool enableBottomText = false;
  74. [Tooltip("Text displayed below the number.")]
  75. public string bottomText = "";
  76. public TextSettings bottomTextSettings = new TextSettings(0f);
  77. // Color by Number
  78. public bool enableColorByNumber = false;
  79. public ColorByNumberSettings colorByNumberSettings = new ColorByNumberSettings(0f);
  80. #endregion
  81. #region Fade Settings
  82. // Fade In
  83. public float durationFadeIn = 0.2f;
  84. public bool enableOffsetFadeIn = true;
  85. [Tooltip("TextA and TextB move together from this offset.")]
  86. public Vector2 offsetFadeIn = new Vector2(0.5f, 0);
  87. public bool enableScaleFadeIn = true;
  88. [Tooltip("Scales in from this scale.")]
  89. public Vector2 scaleFadeIn = new Vector2(2, 2);
  90. public bool enableCrossScaleFadeIn = false;
  91. [Tooltip("Scales TextA in from this scale and TextB from the inverse of this scale.")]
  92. public Vector2 crossScaleFadeIn = new Vector2(1, 1.5f);
  93. public bool enableShakeFadeIn = false;
  94. [Tooltip("Shakes in from this offset.")]
  95. public Vector2 shakeOffsetFadeIn = new Vector2(0, 1.5f);
  96. [Tooltip("Shakes in at this frequency.")]
  97. public float shakeFrequencyFadeIn = 4f;
  98. // Fade Out
  99. public float durationFadeOut = 0.2f;
  100. public bool enableOffsetFadeOut = true;
  101. [Tooltip("TextA and TextB move apart to this offset.")]
  102. public Vector2 offsetFadeOut = new Vector2(0.5f, 0);
  103. public bool enableScaleFadeOut = false;
  104. [Tooltip("Scales out to this scale.")]
  105. public Vector2 scaleFadeOut = new Vector2(2, 2);
  106. public bool enableCrossScaleFadeOut = false;
  107. [Tooltip("Scales TextA out to this scale and TextB to the inverse of this scale.")]
  108. public Vector2 crossScaleFadeOut = new Vector2(1, 1.5f);
  109. public bool enableShakeFadeOut = false;
  110. [Tooltip("Shakes out to this offset.")]
  111. public Vector2 shakeOffsetFadeOut = new Vector2(0, 1.5f);
  112. [Tooltip("Shakes out at this frequency.")]
  113. public float shakeFrequencyFadeOut = 4f;
  114. #endregion
  115. #region Movement Settings
  116. // Lerping
  117. public bool enableLerp = true;
  118. public LerpSettings lerpSettings = new LerpSettings(0);
  119. // Velocity
  120. public bool enableVelocity = false;
  121. public VelocitySettings velocitySettings = new VelocitySettings(0);
  122. // Shaking
  123. public bool enableShaking = false;
  124. [Tooltip("Shake settings during idle.")]
  125. public ShakeSettings shakeSettings = new ShakeSettings(new Vector2(0.005f, 0.005f));
  126. // Following
  127. public bool enableFollowing = false;
  128. [Tooltip("Transform that will be followed.\nTries to maintain the position relative to the target.")]
  129. public Transform followedTarget;
  130. public FollowSettings followSettings = new FollowSettings(0);
  131. #endregion
  132. #region Rotation & Scale Settings
  133. // Start Rotation
  134. public bool enableStartRotation = false;
  135. [Tooltip("The minimum z-angle for the random spawn rotation.")]
  136. public float minRotation = -4f;
  137. [Tooltip("The maximum z-angle for the random spawn rotation.")]
  138. public float maxRotation = 4f;
  139. public bool rotationRandomFlip = false;
  140. // Rotate By Time
  141. public bool enableRotateOverTime = false;
  142. [Tooltip("The minimum rotation speed for the z-angle.")]
  143. public float minRotationSpeed = -15f;
  144. [Tooltip("The maximum rotation speed for the z-angle.")]
  145. public float maxRotationSpeed = 15;
  146. public bool rotationSpeedRandomFlip = false;
  147. [Tooltip("Defines rotation speed over lifetime.")]
  148. 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) });
  149. // Scale By Number
  150. public bool enableScaleByNumber = false;
  151. public ScaleByNumberSettings scaleByNumberSettings = new ScaleByNumberSettings(0);
  152. // Scale By Time
  153. public bool enableScaleOverTime = false;
  154. [Tooltip("Will scale over it's lifetime using this curve.")]
  155. public AnimationCurve scaleOverTime = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1, 0.7f));
  156. // Scale By Orthographic Size
  157. public bool enableOrthographicScaling = false;
  158. [Tooltip("The base orthographic size of the camera, where the popup will use it's default scale.")]
  159. public float defaultOrthographicSize = 5f;
  160. [Tooltip("Popups wont get larger than this value to prevent overlapping, when zooming out.")]
  161. public float maxOrthographicSize = 1.5f;
  162. [Tooltip("The camera whose orthographic size will be used to resize the popup. Uses the Main Camera by default.")]
  163. public Camera orthographicCamera;
  164. #endregion
  165. #region Spam Control Settings
  166. [Tooltip("The group of numbers which will affect each other using the features bellow.")]
  167. public string spamGroup = "";
  168. // Combination
  169. public bool enableCombination = false;
  170. public CombinationSettings combinationSettings = new CombinationSettings(0);
  171. // Destruction
  172. public bool enableDestruction = false;
  173. public DestructionSettings destructionSettings = new DestructionSettings(0);
  174. // Collision
  175. public bool enableCollision = false;
  176. public CollisionSettings collisionSettings = new CollisionSettings(0);
  177. // Push
  178. public bool enablePush = false;
  179. public PushSettings pushSettings = new PushSettings(0);
  180. #endregion
  181. #region Performance Settings
  182. // Update Delay
  183. public float updateDelay = 0.0125f;
  184. // Pooling
  185. public bool enablePooling = false;
  186. [Tooltip("Maximum of damage numbers stored in pool.")]
  187. public int poolSize = 50;
  188. [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.")]
  189. public bool disableOnSceneLoad = true;
  190. #endregion
  191. #region Editor Variables
  192. /// <summary>
  193. /// Please ignore this variable, it's used by the editor.
  194. /// </summary>
  195. public string editorLastFont;
  196. #endregion
  197. // References
  198. TextMeshPro textMeshPro;
  199. MeshRenderer textMeshRenderer;
  200. MeshRenderer meshRendererA;
  201. MeshRenderer meshRendererB;
  202. MeshFilter meshFilterA;
  203. MeshFilter meshFilterB;
  204. protected Transform transformA;
  205. protected Transform transformB;
  206. List<System.Tuple<MeshRenderer, MeshRenderer>> subMeshRenderers;
  207. List<System.Tuple<MeshFilter, MeshFilter>> subMeshFilters;
  208. protected List<Mesh> meshs;
  209. protected List<Color[]> colors;
  210. protected List<float[]> alphas;
  211. // Fading
  212. protected float currentFade;
  213. protected float startTime;
  214. float startLifeTime;
  215. protected float currentLifetime;
  216. float fadeInSpeed;
  217. float fadeOutSpeed;
  218. protected float baseAlpha;
  219. Vector2 currentScaleInOffset;
  220. Vector2 currentScaleOutOffset;
  221. // Position
  222. public Vector3 position;
  223. Vector3 finalPosition;
  224. protected Vector3 remainingOffset;
  225. protected Vector2 currentVelocity;
  226. // Scaling
  227. protected Vector3 originalScale;
  228. float numberScale;
  229. float combinationScale;
  230. float destructionScale;
  231. float renderThroughWallsScale = 0.1f;
  232. float lastScaleFactor = 1f;
  233. bool firstFrameScale;
  234. // Rotation
  235. float currentRotationSpeed;
  236. float currentRotation;
  237. // Following
  238. Vector3 lastTargetPosition;
  239. Vector3 targetOffset;
  240. float currentFollowSpeed;
  241. // Spam Control
  242. static Dictionary<string, HashSet<DamageNumber>> spamGroupDictionary;
  243. bool removedFromDictionary;
  244. // Combination
  245. DamageNumber myAbsorber;
  246. bool givenNumber;
  247. float absorbStartTime;
  248. Vector3 absorbStartPosition;
  249. // 3D
  250. Transform targetCamera;
  251. Camera targetFovCamera;
  252. float simulatedScale;
  253. float lastFOV;
  254. // Orthographic Sclaing
  255. Camera targetOrthographicCamera;
  256. // Destruction
  257. bool isDestroyed;
  258. float destructionStartTime;
  259. // Collision & Push
  260. bool collided;
  261. bool pushed;
  262. // Pooling
  263. DamageNumber originalPrefab;
  264. public static Transform poolParent;
  265. static Dictionary<int, HashSet<DamageNumber>> pools;
  266. public static HashSet<DamageNumber> activeInstances;
  267. int poolingID;
  268. bool performRestart;
  269. bool destroyAfterSpawning;
  270. // Fallback font fix
  271. static Dictionary<TMP_FontAsset, GameObject> fallbackDictionary;
  272. // Custom Events
  273. protected bool isFadingOut;
  274. void Start()
  275. {
  276. // Once
  277. GetReferencesIfNecessary();
  278. if(enablePooling && disableOnSceneLoad)
  279. {
  280. SceneManager.sceneLoaded += OnSceneLoaded;
  281. }
  282. // Repeated for Pooling
  283. Restart();
  284. }
  285. void Update()
  286. {
  287. // For Pooling
  288. if (performRestart)
  289. {
  290. Restart();
  291. performRestart = false;
  292. }
  293. }
  294. void LateUpdate()
  295. {
  296. if (!performRestart)
  297. {
  298. UpdateScaleAnd3D();
  299. }
  300. OnLateUpdate();
  301. }
  302. /// <summary>
  303. /// This is called by DNPUpdater for improved performance.
  304. /// You can ignore this function.
  305. /// </summary>
  306. public void UpdateDamageNumber(float delta, float time)
  307. {
  308. // Check activity
  309. if(isActiveAndEnabled == false)
  310. {
  311. startTime += delta;
  312. startLifeTime += delta;
  313. absorbStartTime += delta;
  314. destructionStartTime += delta;
  315. return;
  316. }
  317. // Vectors
  318. if(DNPUpdater.vectorsNeedUpdate)
  319. {
  320. DNPUpdater.UpdateVectors(transform);
  321. }
  322. // Fading
  323. if (IsAlive(time))
  324. {
  325. HandleFadeIn(delta);
  326. }
  327. else
  328. {
  329. HandleFadeOut(delta);
  330. }
  331. // Custom Event
  332. InternalUpdate(delta);
  333. // Movement
  334. if (enableLerp)
  335. {
  336. HandleLerp(delta);
  337. }
  338. if (enableVelocity)
  339. {
  340. HandleVelocity(delta);
  341. }
  342. if (enableFollowing)
  343. {
  344. HandleFollowing(delta);
  345. }
  346. // Rotation
  347. if (enableRotateOverTime)
  348. {
  349. HandleRotateOverTime(delta, time);
  350. UpdateRotationZ();
  351. }
  352. // Combination
  353. if (enableCombination)
  354. {
  355. HandleCombination(delta, time);
  356. }
  357. // Destruction
  358. if (enableDestruction)
  359. {
  360. HandleDestruction(time);
  361. }
  362. // Offset
  363. finalPosition = position;
  364. if (enableShaking)
  365. {
  366. finalPosition = ApplyShake(finalPosition, shakeSettings, time);
  367. }
  368. // Apply Transform
  369. SetFinalPosition(finalPosition);
  370. }
  371. #region Spawn Functions
  372. /// <summary>
  373. /// Spawns a new popup and handles pooling.
  374. /// </summary>
  375. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  376. public DamageNumber Spawn()
  377. {
  378. DamageNumber newDN = default;
  379. int instanceID = GetInstanceID();
  380. // Check Pool
  381. if (enablePooling && PoolAvailable(instanceID))
  382. {
  383. // Get from Pool
  384. foreach (DamageNumber dn in pools[instanceID])
  385. {
  386. newDN = dn; // This is the only way I can get a unknown element from a hashset, using a single loop iteration
  387. break;
  388. }
  389. pools[instanceID].Remove(newDN);
  390. }
  391. else
  392. {
  393. // Create New
  394. GameObject newGO = Instantiate<GameObject>(gameObject);
  395. newDN = newGO.GetComponent<DamageNumber>();
  396. if (enablePooling)
  397. {
  398. newDN.originalPrefab = this;
  399. }
  400. }
  401. newDN.gameObject.SetActive(true); // Active Gameobject
  402. newDN.InternalOnPreSpawn();
  403. if (enablePooling)
  404. {
  405. newDN.SetPoolingID(instanceID);
  406. newDN.destroyAfterSpawning = false;
  407. }
  408. return newDN;
  409. }
  410. /// <summary>
  411. /// Spawns a new popup and handles pooling.
  412. /// </summary>
  413. /// <param name="newPosition">The worldspace position of this popup.</param>
  414. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  415. public DamageNumber Spawn(Vector3 newPosition)
  416. {
  417. DamageNumber newDN = Spawn();
  418. // Position
  419. newDN.SetPosition(newPosition);
  420. return newDN;
  421. }
  422. /// <summary>
  423. /// Spawns a new popup and handles pooling.
  424. /// Also sets the popup's number.
  425. /// </summary>
  426. /// <param name="newPosition">The worldspace position of this popup.</param>
  427. /// <param name="newNumber">The displayed number of this popup.</param>
  428. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  429. public DamageNumber Spawn(Vector3 newPosition, float newNumber)
  430. {
  431. DamageNumber newDN = Spawn(newPosition);
  432. // Number
  433. newDN.enableNumber = true;
  434. newDN.number = newNumber;
  435. return newDN;
  436. }
  437. /// <summary>
  438. /// Spawns a new popup and handles pooling.
  439. /// Also sets the popup's number and makes it follow a transform.
  440. /// </summary>
  441. /// <param name="newPosition">The worldspace position of this popup.</param>
  442. /// <param name="newNumber">The displayed number of this popup.</param>
  443. /// <param name="followedTransform">The transform, which this popup should follow.</param>
  444. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  445. public DamageNumber Spawn(Vector3 newPosition, float newNumber, Transform followedTransform)
  446. {
  447. DamageNumber newDN = Spawn(newPosition, newNumber);
  448. // Following
  449. newDN.SetFollowedTarget(followedTransform);
  450. return newDN;
  451. }
  452. /// <summary>
  453. /// Spawns a new popup and handles pooling.
  454. /// Also makes the popup follow a transform.
  455. /// </summary>
  456. /// <param name="newPosition">The worldspace position of this popup.</param>
  457. /// <param name="followedTransform">The transform, which this popup should follow.</param>
  458. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  459. public DamageNumber Spawn(Vector3 newPosition, Transform followedTransform)
  460. {
  461. DamageNumber newDN = Spawn(newPosition);
  462. // Following
  463. newDN.SetFollowedTarget(followedTransform);
  464. return newDN;
  465. }
  466. /// <summary>
  467. /// Spawns a new popup and handles pooling.
  468. /// Also sets the popup's text and disables the number.
  469. /// </summary>
  470. /// <param name="newPosition">The worldspace position of this popup.</param>
  471. /// <param name="newText">The text displayed by this popup.</param>
  472. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  473. public DamageNumber Spawn(Vector3 newPosition, string newText)
  474. {
  475. DamageNumber newDN = Spawn(newPosition);
  476. // Disable Number
  477. newDN.enableNumber = false;
  478. // Text
  479. newDN.enableLeftText = true;
  480. newDN.leftText = newText;
  481. return newDN;
  482. }
  483. /// <summary>
  484. /// Spawns a new popup and handles pooling.
  485. /// Also sets the popup's text and makes it follow a transform.
  486. /// Disables the number.
  487. /// </summary>
  488. /// <param name="newPosition">The worldspace position of this popup.</param>
  489. /// <param name="newText">The text displayed by this popup.</param>
  490. /// <param name="followedTransform">The transform, which this popup should follow.</param>
  491. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  492. public DamageNumber Spawn(Vector3 newPosition, string newText, Transform followedTransform)
  493. {
  494. DamageNumber newDN = Spawn(newPosition, newText);
  495. // Following
  496. newDN.SetFollowedTarget(followedTransform);
  497. return newDN;
  498. }
  499. #endregion
  500. #region GUI Spawn Functions
  501. /// <summary>
  502. /// Spawns a new GUI popup and handles pooling.
  503. /// Use this function for DamageNumberGUI only.
  504. /// </summary>
  505. /// <param name="rectParent">The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.</param>
  506. /// <param name="anchoredPosition">The anchored position relative to rectParent.</param>
  507. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  508. public DamageNumber SpawnGUI(RectTransform rectParent, Vector2 anchoredPosition)
  509. {
  510. DamageNumber newDN = Spawn();
  511. // Position
  512. newDN.SetAnchoredPosition(rectParent, anchoredPosition);
  513. return newDN;
  514. }
  515. /// <summary>
  516. /// Spawns a new GUI popup and handles pooling.
  517. /// Use this function for DamageNumberGUI only.
  518. /// </summary>
  519. /// <param name="rectParent">The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.</param>
  520. /// <param name="rectPosition">The RectTransform, which this popup's anchored position is relative to.</param>
  521. /// <param name="anchoredPosition">This popup's anchored position relative to rectPosition.</param>
  522. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  523. public DamageNumber SpawnGUI(RectTransform rectParent, RectTransform rectPosition, Vector2 anchoredPosition)
  524. {
  525. DamageNumber newDN = Spawn();
  526. // Position
  527. newDN.SetAnchoredPosition(rectParent, rectPosition, anchoredPosition);
  528. return newDN;
  529. }
  530. /// <summary>
  531. /// Spawns a new GUI popup and handles pooling.
  532. /// Use this function for DamageNumberGUI only.
  533. /// </summary>
  534. /// <param name="rectParent">The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.</param>
  535. /// <param name="anchoredPosition">The anchored position relative to rectParent.</param>
  536. /// <param name="newNumber">The displayed number of this popup.</param>
  537. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  538. public DamageNumber SpawnGUI(RectTransform rectParent, Vector2 anchoredPosition, float newNumber)
  539. {
  540. DamageNumber newDN = SpawnGUI(rectParent, anchoredPosition);
  541. // Number
  542. newDN.enableNumber = true;
  543. newDN.number = newNumber;
  544. return newDN;
  545. }
  546. /// <summary>
  547. /// Spawns a new GUI popup and handles pooling.
  548. /// Use this function for DamageNumberGUI only.
  549. /// </summary>
  550. /// <param name="rectParent">The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.</param>
  551. /// <param name="rectPosition">The RectTransform, which this popup's anchored position is relative to.</param>
  552. /// <param name="anchoredPosition">This popup's anchored position relative to rectPosition.</param>
  553. /// <param name="newNumber">The displayed number of this popup.</param>
  554. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  555. public DamageNumber SpawnGUI(RectTransform rectParent, RectTransform rectPosition, Vector2 anchoredPosition, float newNumber)
  556. {
  557. DamageNumber newDN = SpawnGUI(rectParent, rectPosition, anchoredPosition);
  558. // Number
  559. newDN.enableNumber = true;
  560. newDN.number = newNumber;
  561. return newDN;
  562. }
  563. /// <summary>
  564. /// Spawns a new GUI popup and handles pooling.
  565. /// Use this function for DamageNumberGUI only.
  566. /// </summary>
  567. /// <param name="rectParent">The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.</param>
  568. /// <param name="anchoredPosition">The anchored position relative to rectParent.</param>
  569. /// <param name="newText">The text displayed by this popup.</param>
  570. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  571. public DamageNumber SpawnGUI(RectTransform rectParent, Vector2 anchoredPosition, string newText)
  572. {
  573. DamageNumber newDN = SpawnGUI(rectParent, anchoredPosition);
  574. // Disable Number
  575. newDN.enableNumber = false;
  576. // Text
  577. newDN.enableLeftText = true;
  578. newDN.leftText = newText;
  579. return newDN;
  580. }
  581. /// <summary>
  582. /// Spawns a new GUI popup and handles pooling.
  583. /// Use this function for DamageNumberGUI only.
  584. /// </summary>
  585. /// <param name="rectParent">The RectTransform, which this popup should be parented to. Spam Control features like Combination only work under the same parent.</param>
  586. /// <param name="rectPosition">The RectTransform, which this popup's anchored position is relative to.</param>
  587. /// <param name="anchoredPosition">This popup's anchored position relative to rectPosition.</param>
  588. /// <param name="newText">The text displayed by this popup.</param>
  589. /// <returns>The spawned popup, which can be modified at runtime.</returns>
  590. public DamageNumber SpawnGUI(RectTransform rectParent, RectTransform rectPosition, Vector2 anchoredPosition, string newText)
  591. {
  592. DamageNumber newDN = SpawnGUI(rectParent, rectPosition, anchoredPosition);
  593. // Disable Number
  594. newDN.enableNumber = false;
  595. // Text
  596. newDN.enableLeftText = true;
  597. newDN.leftText = newText;
  598. return newDN;
  599. }
  600. #endregion
  601. #region Removed Functions
  602. /*
  603. public DamageNumber Spawn(Vector3 newPosition, float newNumber, Color newColor)
  604. {
  605. DamageNumber newDN = Spawn(newPosition, newNumber);
  606. // Position
  607. newDN.SetPosition(newPosition);
  608. // Number
  609. newDN.number = newNumber;
  610. // Color
  611. newDN.SetColor(newColor);
  612. return newDN;
  613. }
  614. public DamageNumber Spawn(Vector3 newPosition, string newLeftText, Color newColor)
  615. {
  616. DamageNumber newDN = Spawn();
  617. // Position
  618. newDN.SetPosition(newPosition);
  619. // Number
  620. newDN.enableLeftText = true;
  621. newDN.leftText = newLeftText;
  622. // Color
  623. newDN.SetColor(newColor);
  624. return newDN;
  625. }
  626. public DamageNumber Spawn(Vector3 newPosition, float newNumber, Transform followedTransform, Color newColor)
  627. {
  628. DamageNumber newDN = Spawn();
  629. // Position
  630. newDN.SetPosition(newPosition);
  631. // Text
  632. newDN.number = newNumber;
  633. // Following
  634. newDN.SetFollowedTarget(followedTransform);
  635. // Color
  636. newDN.SetColor(newColor);
  637. return newDN;
  638. }
  639. public DamageNumber Spawn(Vector3 newPosition, string newLeftText, Transform followedTransform, Color newColor)
  640. {
  641. DamageNumber newDN = Spawn();
  642. // Position
  643. newDN.SetPosition(newPosition);
  644. // Text
  645. newDN.enableLeftText = true;
  646. newDN.leftText = newLeftText;
  647. // Following
  648. newDN.SetFollowedTarget(followedTransform);
  649. // Color
  650. newDN.SetColor(newColor);
  651. return newDN;
  652. }
  653. public DamageNumber Spawn(Transform rectParent, Vector2 anchoredPosition)
  654. {
  655. DamageNumber newDN = Spawn();
  656. // Position
  657. newDN.SetAnchoredPosition(rectParent, anchoredPosition);
  658. return newDN;
  659. }
  660. public DamageNumber Spawn(Transform rectParent, Vector2 anchoredPosition, float number)
  661. {
  662. DamageNumber newDN = Spawn();
  663. // Number
  664. newDN.number = number;
  665. // Position
  666. newDN.SetAnchoredPosition(rectParent, anchoredPosition);
  667. return newDN;
  668. }
  669. public DamageNumber Spawn(Transform rectParent, Transform rectPosition, Vector2 anchoredPosition, float number)
  670. {
  671. DamageNumber newDN = Spawn();
  672. // Number
  673. newDN.number = number;
  674. // Position
  675. newDN.SetAnchoredPosition(rectParent, rectPosition, anchoredPosition);
  676. return newDN;
  677. }
  678. public DamageNumber Spawn(Transform rectParent, Transform rectPosition, Vector2 anchoredPosition)
  679. {
  680. DamageNumber newDN = Spawn();
  681. // Position
  682. newDN.SetAnchoredPosition(rectParent, rectPosition, anchoredPosition);
  683. return newDN;
  684. }
  685. */
  686. #endregion
  687. #region Public Functions
  688. /// <summary>
  689. /// Makes the damage number follow a transform.
  690. /// Can also modify the spamGroup so that only damage numbers following this taget interact with each other.
  691. /// </summary>
  692. public void SetFollowedTarget(Transform followedTransform, bool modifySpamGroup = true)
  693. {
  694. // Following
  695. enableFollowing = true;
  696. followedTarget = followedTransform;
  697. // Spam Group
  698. if (modifySpamGroup)
  699. {
  700. spamGroup += followedTransform.GetInstanceID();
  701. }
  702. }
  703. public void SetColor(Color newColor)
  704. {
  705. // References
  706. GetReferencesIfNecessary();
  707. // Set Color
  708. foreach (TMP_Text tmp in GetTextMeshs())
  709. {
  710. tmp.color = newColor;
  711. }
  712. }
  713. public void SetGradientColor(VertexGradient newGradient)
  714. {
  715. // References
  716. GetReferencesIfNecessary();
  717. // Set Gradient
  718. foreach (TMP_Text tmp in GetTextMeshs())
  719. {
  720. tmp.enableVertexGradient = true;
  721. tmp.colorGradient = newGradient;
  722. }
  723. }
  724. public void SetRandomColor(Color from, Color to)
  725. {
  726. SetColor(Color.Lerp(from, to, Random.value));
  727. }
  728. public void SetRandomColor(Gradient gradient)
  729. {
  730. SetColor(gradient.Evaluate(Random.value));
  731. }
  732. public void SetGradientColor(Color topLeft, Color topRight, Color bottomLeft, Color bottomRight)
  733. {
  734. VertexGradient newGradient = new VertexGradient();
  735. newGradient.topLeft = topLeft;
  736. newGradient.topRight = topRight;
  737. newGradient.bottomLeft = bottomLeft;
  738. newGradient.bottomRight = bottomRight;
  739. SetGradientColor(newGradient);
  740. }
  741. public void SetFontMaterial(TMP_FontAsset font)
  742. {
  743. // References
  744. GetReferencesIfNecessary();
  745. // Set Font
  746. foreach (TMP_Text tmp in GetTextMeshs())
  747. {
  748. tmp.font = font;
  749. }
  750. }
  751. public TMP_FontAsset GetFontMaterial()
  752. {
  753. // References
  754. GetReferencesIfNecessary();
  755. // Get Font
  756. foreach (TMP_Text tmp in GetTextMeshs())
  757. {
  758. if (tmp.font != null)
  759. {
  760. return tmp.font;
  761. }
  762. }
  763. return null;
  764. }
  765. public void SetScale(float newScale)
  766. {
  767. originalScale = transform.localScale = new Vector3(newScale, newScale, newScale);
  768. }
  769. public virtual Vector3 GetUpVector()
  770. {
  771. return DNPUpdater.upVector;
  772. }
  773. public virtual Vector3 GetRightVector()
  774. {
  775. return DNPUpdater.rightVector;
  776. }
  777. public virtual Vector3 GetFreshUpVector()
  778. {
  779. return transform.up;
  780. }
  781. public virtual Vector3 GetFreshRightVector()
  782. {
  783. return transform.right;
  784. }
  785. #endregion
  786. #region Utility Functions
  787. public virtual void GetReferences()
  788. {
  789. baseAlpha = 1f;
  790. textMeshPro = transform.Find("TMP").GetComponent<TextMeshPro>();
  791. textMeshRenderer = textMeshPro.GetComponent<MeshRenderer>();
  792. transformA = transform.Find("MeshA");
  793. transformB = transform.Find("MeshB");
  794. meshRendererA = transformA.GetComponent<MeshRenderer>();
  795. meshRendererB = transformB.GetComponent<MeshRenderer>();
  796. meshFilterA = transformA.GetComponent<MeshFilter>();
  797. meshFilterB = transformB.GetComponent<MeshFilter>();
  798. subMeshRenderers = new List<System.Tuple<MeshRenderer, MeshRenderer>>();
  799. subMeshFilters = new List<System.Tuple<MeshFilter, MeshFilter>>();
  800. Transform parentA = meshRendererA.transform;
  801. Transform parentB = meshRendererB.transform;
  802. for (int n = 0; n < parentA.childCount; n++)
  803. {
  804. Transform childA = parentA.GetChild(n);
  805. Transform childB = parentB.GetChild(n);
  806. subMeshRenderers.Add(new System.Tuple<MeshRenderer, MeshRenderer>(childA.GetComponent<MeshRenderer>(), childB.GetComponent<MeshRenderer>()));
  807. subMeshFilters.Add(new System.Tuple<MeshFilter, MeshFilter>(childA.GetComponent<MeshFilter>(), childB.GetComponent<MeshFilter>()));
  808. }
  809. }
  810. public virtual void GetReferencesIfNecessary()
  811. {
  812. if (textMeshPro == null || subMeshRenderers == null)
  813. {
  814. GetReferences();
  815. }
  816. }
  817. /// <summary>
  818. /// Starts fading out this damage number.
  819. /// Use this to fade out damage numbers early.
  820. /// </summary>
  821. public void FadeOut()
  822. {
  823. permanent = false;
  824. startLifeTime = -1000;
  825. }
  826. /// <summary>
  827. /// Restarts the fade-in animation.
  828. /// Could be used for fixed gui texts with permanent lifetime.
  829. /// </summary>
  830. public void FadeIn()
  831. {
  832. currentFade = 0;
  833. }
  834. /// <summary>
  835. /// Returns 1 text mesh pro component on the mesh version.
  836. /// Returns 2 text mesh pro components on the gui version.
  837. /// </summary>
  838. /// <returns></returns>
  839. public virtual TMP_Text[] GetTextMeshs()
  840. {
  841. return new TMP_Text[] { textMeshPro };
  842. }
  843. /// <summary>
  844. /// Returns the text mesh pro component.
  845. /// Use this to change text mesh pro settings at runtime.
  846. /// </summary>
  847. /// <returns></returns>
  848. public virtual TMP_Text GetTextMesh()
  849. {
  850. return textMeshPro;
  851. }
  852. public virtual Material[] GetSharedMaterials()
  853. {
  854. return textMeshRenderer.sharedMaterials;
  855. }
  856. public virtual Material[] GetMaterials()
  857. {
  858. return textMeshRenderer.materials;
  859. }
  860. public virtual Material GetSharedMaterial()
  861. {
  862. return textMeshRenderer.sharedMaterial;
  863. }
  864. public virtual Material GetMaterial()
  865. {
  866. return textMeshRenderer.material;
  867. }
  868. /// <summary>
  869. /// Input Time.time or Time.unscaledTime (depending on the time setting in main settings).
  870. /// Returns if damage number is still alive.
  871. /// </summary>
  872. /// <param name="time"></param>
  873. /// <returns></returns>
  874. public virtual bool IsAlive(float time)
  875. {
  876. if (permanent)
  877. {
  878. return true;
  879. }
  880. return time - startLifeTime < currentLifetime;
  881. }
  882. /// <summary>
  883. /// Use this function to manually destroy a damage number.
  884. /// This will also handle pooling.
  885. /// </summary>
  886. public void DestroyDNP()
  887. {
  888. // Event
  889. OnDespawn?.Invoke();
  890. // Pooling / Destroying
  891. if (enablePooling && originalPrefab != null)
  892. {
  893. if (pools == null)
  894. {
  895. pools = new Dictionary<int, HashSet<DamageNumber>>();
  896. }
  897. if (!pools.ContainsKey(poolingID))
  898. {
  899. pools.Add(poolingID, new HashSet<DamageNumber>());
  900. }
  901. // Static Reference
  902. if (activeInstances != null && activeInstances.Contains(this))
  903. {
  904. activeInstances.Remove(this);
  905. }
  906. // Updater
  907. DNPUpdater.UnregisterPopup(unscaledTime, updateDelay, this);
  908. // Remove from dictionaries
  909. RemoveFromDictionary();
  910. // Pooling
  911. if (pools[poolingID].Count < poolSize)
  912. {
  913. PreparePooling();
  914. }
  915. else
  916. {
  917. Destroy(gameObject); // Not enough pool space
  918. }
  919. }
  920. else
  921. {
  922. Destroy(gameObject);
  923. }
  924. }
  925. public virtual void CheckAndEnable3D()
  926. {
  927. // Dimension Check
  928. Camera camera = Camera.main;
  929. if (camera == null)
  930. {
  931. camera = Camera.current;
  932. }
  933. if (camera != null)
  934. {
  935. if (!camera.orthographic)
  936. {
  937. enable3DGame = true;
  938. }
  939. }
  940. }
  941. /// <summary>
  942. /// Returns true if this is a Mesh or false if this is a GUI Component.
  943. /// </summary>
  944. /// <returns></returns>
  945. public virtual bool IsMesh()
  946. {
  947. return true;
  948. }
  949. public static GameObject NewMesh(string tmName, Transform parent)
  950. {
  951. // GameObject
  952. GameObject newTM = new GameObject();
  953. newTM.name = tmName;
  954. newTM.layer = parent.gameObject.layer;
  955. // Mesh
  956. MeshRenderer mr = newTM.AddComponent<MeshRenderer>();
  957. newTM.AddComponent<MeshFilter>();
  958. mr.receiveShadows = false;
  959. mr.allowOcclusionWhenDynamic = false;
  960. mr.shadowCastingMode = ShadowCastingMode.Off;
  961. mr.lightProbeUsage = LightProbeUsage.Off;
  962. mr.reflectionProbeUsage = ReflectionProbeUsage.Off;
  963. // Transform
  964. Transform transform = newTM.transform;
  965. transform.SetParent(parent, true);
  966. transform.localPosition = Vector3.zero;
  967. transform.localScale = Vector3.one;
  968. transform.localEulerAngles = Vector3.zero;
  969. return newTM;
  970. }
  971. #endregion
  972. #region Pooling
  973. /// <summary>
  974. /// Use this function ONCE PER PREFAB to prewarm it's pool at the start of your game.
  975. /// It will generate enough damage numbers to fill the pool size.
  976. /// </summary>
  977. public void PrewarmPool()
  978. {
  979. if (enablePooling)
  980. {
  981. int instanceId = GetInstanceID();
  982. // Initialize Dictionary
  983. if (pools == null)
  984. {
  985. pools = new Dictionary<int, HashSet<DamageNumber>>();
  986. }
  987. // Initialize Pool
  988. if (!pools.ContainsKey(instanceId))
  989. {
  990. pools.Add(instanceId, new HashSet<DamageNumber>());
  991. }
  992. // Fill Pool
  993. int amount = poolSize - pools[instanceId].Count;
  994. if(amount > poolSize * 0.5f)
  995. {
  996. for (int n = 0; n < amount; n++)
  997. {
  998. DamageNumber dn = Spawn(new Vector3(-9999, -9999, 0));
  999. dn.destroyAfterSpawning = true;
  1000. }
  1001. }
  1002. }
  1003. }
  1004. /// <summary>
  1005. /// Clears all pooled popups.
  1006. /// </summary>
  1007. /// <param name="type">The type of pooled popups to clear. All by default.</param>
  1008. public static void ClearPooled(DNPType type = DNPType.All)
  1009. {
  1010. // Check if pools exist
  1011. if(pools != null)
  1012. {
  1013. // Iterate through pools
  1014. foreach (KeyValuePair<int, HashSet<DamageNumber>> entry in pools)
  1015. {
  1016. // Check if pool contains popups
  1017. if (entry.Value != null)
  1018. {
  1019. // Iterate through the pool's popups
  1020. foreach (DamageNumber popup in entry.Value)
  1021. {
  1022. // Check type
  1023. if (type == DNPType.All || (popup.IsMesh() == (type == DNPType.Mesh)))
  1024. {
  1025. // Destroy pooled popup
  1026. Destroy(popup.gameObject);
  1027. }
  1028. }
  1029. }
  1030. }
  1031. }
  1032. }
  1033. /// <summary>
  1034. /// Destroys all currently active popups.
  1035. /// </summary>
  1036. /// <param name="type">The type of popup to destroy. All by default.</param>
  1037. /// <param name="allowPooling">Whether destroyed popups are allowed to pool.</param>
  1038. /// <param name="ignorePermanent">Whether permanent popups should be destroyed.</param>
  1039. public static void ClearActive(DNPType type = DNPType.All, bool allowPooling = true, bool ignorePermanent = true)
  1040. {
  1041. if (allowPooling)
  1042. {
  1043. // Get all active popups
  1044. List<DamageNumber> popups = new List<DamageNumber>();
  1045. foreach (DamageNumber dn in activeInstances)
  1046. {
  1047. if (dn != null)
  1048. {
  1049. popups.Add(dn);
  1050. }
  1051. }
  1052. // Destroy all active popups with potential pooling
  1053. foreach(DamageNumber popup in popups)
  1054. {
  1055. // Check permanent
  1056. if (!ignorePermanent || !popup.permanent)
  1057. {
  1058. // Check type
  1059. if (type == DNPType.All || (popup.IsMesh() == (type == DNPType.Mesh)))
  1060. {
  1061. popup.DestroyDNP();
  1062. }
  1063. }
  1064. }
  1065. }
  1066. else
  1067. {
  1068. foreach (DamageNumber popup in activeInstances)
  1069. {
  1070. if (popup != null)
  1071. {
  1072. // Check permanent
  1073. if (!ignorePermanent || !popup.permanent)
  1074. {
  1075. // Check type
  1076. if (type == DNPType.All || (popup.IsMesh() == (type == DNPType.Mesh)))
  1077. {
  1078. Destroy(popup.gameObject);
  1079. }
  1080. }
  1081. }
  1082. }
  1083. }
  1084. }
  1085. /// <summary>
  1086. /// Fades out all currently active popups.
  1087. /// </summary>
  1088. /// <param name="type">The type of the popup to fade out. All by default.</param>
  1089. /// <param name="ignorePermanent">Whether permanent popups should be faded out.</param>
  1090. public static void FadeOutActive(DNPType type = DNPType.All, bool ignorePermanent = true)
  1091. {
  1092. foreach(DamageNumber popup in activeInstances)
  1093. {
  1094. if (popup != null)
  1095. {
  1096. // Check permanent
  1097. if (!ignorePermanent || !popup.permanent)
  1098. {
  1099. // Check type
  1100. if (type == DNPType.All || (popup.IsMesh() == (type == DNPType.Mesh)))
  1101. {
  1102. popup.FadeOut();
  1103. }
  1104. }
  1105. }
  1106. }
  1107. }
  1108. protected void Restart()
  1109. {
  1110. // Static Reference
  1111. if(activeInstances == null)
  1112. {
  1113. activeInstances = new HashSet<DamageNumber>();
  1114. }
  1115. if (activeInstances.Contains(this) == false)
  1116. {
  1117. activeInstances.Add(this);
  1118. }
  1119. // Updater
  1120. DNPUpdater.RegisterPopup(unscaledTime, updateDelay, this);
  1121. // Get Scale
  1122. if (originalScale.x < 0.02f)
  1123. {
  1124. originalScale = transform.localScale;
  1125. }
  1126. // Fix Fading Scale
  1127. transformA.localScale = transformB.localScale = Vector3.one;
  1128. // Event
  1129. OnSpawn?.Invoke();
  1130. // Custom Event
  1131. InternalOnSpawn();
  1132. #region Fallback Fix
  1133. if (IsMesh())
  1134. {
  1135. // Create fallback dictionary
  1136. if (fallbackDictionary == null)
  1137. {
  1138. fallbackDictionary = new Dictionary<TMP_FontAsset, GameObject>();
  1139. }
  1140. // Get font material
  1141. TMP_FontAsset fontAsset = GetFontMaterial();
  1142. // Check if in dictionary
  1143. if (!fallbackDictionary.ContainsKey(fontAsset) && fontAsset != null)
  1144. {
  1145. bool usesFallbackFonts = fontAsset.fallbackFontAssetTable != null && fontAsset.fallbackFontAssetTable.Count > 0;
  1146. if (fontAsset.isMultiAtlasTexturesEnabled || usesFallbackFonts)
  1147. {
  1148. // New tmp for fallback assets
  1149. GameObject fallbackAsset = Instantiate<GameObject>(textMeshPro.gameObject);
  1150. fallbackAsset.transform.localScale = Vector3.zero;
  1151. fallbackAsset.SetActive(true);
  1152. fallbackAsset.hideFlags = HideFlags.HideAndDontSave;
  1153. DontDestroyOnLoad(fallbackAsset);
  1154. // Create base string containing a single character of the base font
  1155. string textString = System.Char.ConvertFromUtf32((int) fontAsset.characterTable[0].unicode);
  1156. // Add all characters to support multi-atlas fonts
  1157. if (fontAsset.isMultiAtlasTexturesEnabled)
  1158. {
  1159. foreach (TMP_Character character in fontAsset.characterTable)
  1160. {
  1161. if(character != null && character.unicode > 0)
  1162. {
  1163. textString += System.Char.ConvertFromUtf32((int)character.unicode);
  1164. }
  1165. }
  1166. }
  1167. // Create a new string containing various unicode characters of the fallback fonts
  1168. if (usesFallbackFonts)
  1169. {
  1170. for (int f = 0; f < fontAsset.fallbackFontAssetTable.Count; f++)
  1171. {
  1172. TMP_FontAsset fallbackFont = fontAsset.fallbackFontAssetTable[f];
  1173. if (fallbackFont != null && fallbackFont.characterTable != null)
  1174. {
  1175. bool success = false;
  1176. foreach (TMP_Character fallbackCharacter in fallbackFont.characterTable)
  1177. {
  1178. if (fallbackCharacter != null)
  1179. {
  1180. if (AddFallbackCharacterToString(ref textString, fallbackCharacter.unicode, fontAsset, f))
  1181. {
  1182. success = true;
  1183. break;
  1184. }
  1185. }
  1186. }
  1187. if (!success && fallbackFont.atlasPopulationMode == AtlasPopulationMode.Dynamic)
  1188. {
  1189. for (int unicode = 0; unicode < 40959; unicode++)
  1190. {
  1191. if (fallbackFont.TryAddCharacters(new uint[] { (uint) unicode }))
  1192. {
  1193. if (AddFallbackCharacterToString(ref textString, (uint)unicode, fontAsset, f))
  1194. {
  1195. break;
  1196. }
  1197. }
  1198. }
  1199. }
  1200. }
  1201. }
  1202. }
  1203. // Assign text and add to dictionary
  1204. fallbackAsset.GetComponent<TextMeshPro>().text = textString;
  1205. fallbackDictionary.Add(fontAsset, fallbackAsset);
  1206. }
  1207. else
  1208. {
  1209. fallbackDictionary.Add(fontAsset, null);
  1210. }
  1211. }
  1212. }
  1213. #endregion
  1214. // Called right after spawn
  1215. float time = unscaledTime ? Time.unscaledTime : Time.time;
  1216. Initialize(time);
  1217. // Spam Control
  1218. TriggerSpamControl();
  1219. // Scale to Zero
  1220. firstFrameScale = true;
  1221. if(destroyAfterSpawning)
  1222. {
  1223. destroyAfterSpawning = false;
  1224. startLifeTime = -100;
  1225. }
  1226. }
  1227. bool AddFallbackCharacterToString(ref string textString, uint unicode, TMP_FontAsset mainFontAsset, int fallbackIndex)
  1228. {
  1229. // The unicode 0 can cause issues
  1230. if(unicode < 1)
  1231. {
  1232. return false;
  1233. }
  1234. if (mainFontAsset.characterLookupTable.ContainsKey(unicode) || (mainFontAsset.atlasPopulationMode == AtlasPopulationMode.Dynamic && mainFontAsset.TryAddCharacters(new uint[] { unicode })))
  1235. {
  1236. // Character already in main font
  1237. }
  1238. else
  1239. {
  1240. bool addCharacter = true;
  1241. for (int pF = 0; pF < fallbackIndex; pF++)
  1242. {
  1243. TMP_FontAsset previousFallbackFont = mainFontAsset.fallbackFontAssetTable[pF];
  1244. if (previousFallbackFont != null)
  1245. {
  1246. if (previousFallbackFont.characterLookupTable.ContainsKey(unicode) || (previousFallbackFont.atlasPopulationMode == AtlasPopulationMode.Dynamic && previousFallbackFont.TryAddCharacters(new uint[] { unicode })))
  1247. {
  1248. // Character already in a higher priority fallback font
  1249. addCharacter = false;
  1250. break;
  1251. }
  1252. }
  1253. }
  1254. if (addCharacter)
  1255. {
  1256. textString += System.Char.ConvertFromUtf32((int) unicode);
  1257. return true;
  1258. }
  1259. }
  1260. return false;
  1261. }
  1262. void Initialize(float time)
  1263. {
  1264. numberScale = destructionScale = combinationScale = currentFollowSpeed = 1f;
  1265. baseAlpha = 1f;
  1266. finalPosition = position = GetPosition();
  1267. startLifeTime = startTime = time;
  1268. currentLifetime = lifetime;
  1269. isFadingOut = false;
  1270. // 3D Game
  1271. if (enable3DGame)
  1272. {
  1273. if (cameraOverride != null)
  1274. {
  1275. targetCamera = cameraOverride;
  1276. }
  1277. else if (Camera.main != null)
  1278. {
  1279. targetCamera = Camera.main.transform;
  1280. }
  1281. else if (Camera.current != null)
  1282. {
  1283. targetCamera = Camera.current.transform;
  1284. }
  1285. // Scale with FOV
  1286. if (scaleWithFov)
  1287. {
  1288. if (fovCamera != null)
  1289. {
  1290. targetFovCamera = fovCamera;
  1291. }
  1292. else if (Camera.main != null)
  1293. {
  1294. targetFovCamera = Camera.main;
  1295. }
  1296. else if (Camera.current != null)
  1297. {
  1298. targetFovCamera = Camera.current;
  1299. }
  1300. }
  1301. }
  1302. // Orthographic Scaling
  1303. if (enableOrthographicScaling)
  1304. {
  1305. if (orthographicCamera != null)
  1306. {
  1307. targetOrthographicCamera = orthographicCamera;
  1308. }
  1309. else if (Camera.main != null)
  1310. {
  1311. targetOrthographicCamera = Camera.main;
  1312. }
  1313. else if (Camera.current != null)
  1314. {
  1315. targetOrthographicCamera = Camera.current;
  1316. }
  1317. }
  1318. // Scale
  1319. UpdateScaleAnd3D(true);
  1320. // Rotation
  1321. if (enableRotateOverTime)
  1322. {
  1323. currentRotationSpeed = Random.Range(minRotationSpeed, maxRotationSpeed);
  1324. if(rotationSpeedRandomFlip && Random.value < 0.5f)
  1325. {
  1326. currentRotationSpeed *= -1;
  1327. }
  1328. }
  1329. // Spam Groups
  1330. AddToDictionary();
  1331. // Lerp
  1332. if (enableLerp)
  1333. {
  1334. float xOffset = Random.Range(lerpSettings.minX, lerpSettings.maxX) * GetPositionFactor();
  1335. if(lerpSettings.randomFlip && Random.value < 0.5f)
  1336. {
  1337. xOffset = -xOffset;
  1338. }
  1339. remainingOffset = GetFreshRightVector() * xOffset + GetFreshUpVector() * (Random.Range(lerpSettings.minY, lerpSettings.maxY) * GetPositionFactor());
  1340. }
  1341. // Velocity
  1342. if (enableVelocity)
  1343. {
  1344. currentVelocity = new Vector2(Random.Range(velocitySettings.minX, velocitySettings.maxX), Random.Range(velocitySettings.minY, velocitySettings.maxY)) * GetPositionFactor();
  1345. if(velocitySettings.randomFlip && Random.value < 0.5f)
  1346. {
  1347. currentVelocity.x = -currentVelocity.x;
  1348. }
  1349. }
  1350. // Start Rotation
  1351. if (enableStartRotation)
  1352. {
  1353. currentRotation = Random.Range(minRotation, maxRotation);
  1354. if (rotationRandomFlip && Random.value < 0.5f)
  1355. {
  1356. currentRotation *= -1;
  1357. }
  1358. }
  1359. else
  1360. {
  1361. currentRotation = 0;
  1362. }
  1363. // Fading
  1364. currentFade = durationFadeIn > 0f ? 0f : 1f;
  1365. // Fade In
  1366. fadeInSpeed = 1f / Mathf.Max(0.0001f, durationFadeIn);
  1367. if (enableCrossScaleFadeIn)
  1368. {
  1369. currentScaleInOffset = crossScaleFadeIn;
  1370. if (currentScaleInOffset.x == 0) currentScaleInOffset.x += 0.001f;
  1371. if (currentScaleInOffset.y == 0) currentScaleInOffset.y += 0.001f;
  1372. }
  1373. // Fade Out
  1374. fadeOutSpeed = 1f / Mathf.Max(0.0001f, durationFadeOut);
  1375. if (enableCrossScaleFadeOut)
  1376. {
  1377. currentScaleOutOffset = crossScaleFadeOut;
  1378. if (currentScaleOutOffset.x == 0) currentScaleOutOffset.x += 0.001f;
  1379. if (currentScaleOutOffset.y == 0) currentScaleOutOffset.y += 0.001f;
  1380. }
  1381. lastTargetPosition = Vector3.zero;
  1382. // Update Text
  1383. UpdateText();
  1384. // Update Rotation
  1385. UpdateRotationZ();
  1386. }
  1387. void PreparePooling()
  1388. {
  1389. // Add to Pool
  1390. pools[poolingID].Add(this);
  1391. // Disable GameObject
  1392. gameObject.SetActive(false);
  1393. // Queue Restart
  1394. performRestart = true;
  1395. // Reset Runtime Variables
  1396. transform.localScale = originalScale;
  1397. lastTargetPosition = targetOffset = Vector3.zero;
  1398. // Clear Combination Targets
  1399. myAbsorber = null;
  1400. // Reset some Setting Variables
  1401. permanent = originalPrefab.permanent;
  1402. spamGroup = originalPrefab.spamGroup;
  1403. leftText = originalPrefab.leftText;
  1404. rightText = originalPrefab.rightText;
  1405. followedTarget = originalPrefab.followedTarget;
  1406. enableCollision = originalPrefab.enableCollision;
  1407. enablePush = originalPrefab.enablePush;
  1408. }
  1409. bool PoolAvailable(int id)
  1410. {
  1411. if (pools != null && pools.ContainsKey(id))
  1412. {
  1413. if (pools[id].Count > 0)
  1414. {
  1415. return true;
  1416. }
  1417. }
  1418. return false;
  1419. }
  1420. void SetPoolingID(int id)
  1421. {
  1422. poolingID = id;
  1423. // Initiate Dictionaries
  1424. if (pools == null)
  1425. {
  1426. pools = new Dictionary<int, HashSet<DamageNumber>>();
  1427. }
  1428. // Initiate Pool Parent
  1429. if (poolParent == null)
  1430. {
  1431. GameObject poolGameobject = new GameObject("Damage Number Pool");
  1432. DontDestroyOnLoad(poolGameobject);
  1433. poolParent = poolGameobject.transform;
  1434. poolParent.localScale = Vector3.one;
  1435. poolParent.eulerAngles = poolParent.position = Vector3.zero;
  1436. }
  1437. // Parent
  1438. transform.SetParent(poolParent, true);
  1439. }
  1440. #endregion
  1441. #region Text
  1442. public void UpdateText()
  1443. {
  1444. // Number
  1445. string numberText = "";
  1446. if (enableNumber)
  1447. {
  1448. string numberString;
  1449. bool shortened;
  1450. if (digitSettings.decimals <= 0)
  1451. {
  1452. numberString = ProcessIntegers(Mathf.Round(number).ToString("F0"), out shortened);
  1453. }
  1454. else
  1455. {
  1456. // Digits
  1457. string allDigits = (Mathf.Abs(number) * Mathf.Pow(10, digitSettings.decimals)).ToString("F0");
  1458. bool hasMinus = number < 0;
  1459. // Add zeros to the left to fix numbers less than 1
  1460. int usedDecimals = digitSettings.decimals;
  1461. int currentLength = allDigits.Length;
  1462. if(currentLength < usedDecimals)
  1463. {
  1464. for(int i = 0; i < usedDecimals - currentLength; i++)
  1465. {
  1466. allDigits = "0" + allDigits;
  1467. }
  1468. }
  1469. while (digitSettings.hideZeros && allDigits.EndsWith("0") && usedDecimals > 0)
  1470. {
  1471. allDigits = allDigits.Substring(0, allDigits.Length - 1);
  1472. usedDecimals--;
  1473. }
  1474. string integers = allDigits.Substring(0, Mathf.Max(0, allDigits.Length - usedDecimals));
  1475. integers = ProcessIntegers(integers, out shortened);
  1476. if (integers == "")
  1477. {
  1478. integers = "0";
  1479. }
  1480. int digitLength = allDigits.Length;
  1481. while (digitLength < usedDecimals)
  1482. {
  1483. if(digitSettings.hideZeros)
  1484. {
  1485. usedDecimals--;
  1486. }
  1487. else
  1488. {
  1489. allDigits += "0";
  1490. digitLength = allDigits.Length;
  1491. }
  1492. }
  1493. string decimals = allDigits.Substring(allDigits.Length - usedDecimals);
  1494. if (usedDecimals > 0 && !shortened)
  1495. {
  1496. numberString = integers + digitSettings.decimalChar + decimals;
  1497. }
  1498. else
  1499. {
  1500. numberString = integers;
  1501. }
  1502. if (hasMinus)
  1503. {
  1504. numberString = "-" + numberString;
  1505. }
  1506. }
  1507. numberText = ApplyTextSettings(numberString, numberSettings);
  1508. if (enableScaleByNumber)
  1509. {
  1510. numberScale = scaleByNumberSettings.fromScale + (scaleByNumberSettings.toScale - scaleByNumberSettings.fromScale) * Mathf.Clamp01((number - scaleByNumberSettings.fromNumber) / (scaleByNumberSettings.toNumber - scaleByNumberSettings.fromNumber));
  1511. }
  1512. if(enableColorByNumber)
  1513. {
  1514. SetColor(colorByNumberSettings.colorGradient.Evaluate(Mathf.Clamp01((number - colorByNumberSettings.fromNumber) / (colorByNumberSettings.toNumber - colorByNumberSettings.fromNumber))));
  1515. }
  1516. }
  1517. // Prefix
  1518. string prefixText = "";
  1519. if (enableTopText)
  1520. {
  1521. prefixText += ApplyTextSettings(topText, topTextSettings) + System.Environment.NewLine;
  1522. }
  1523. if (enableLeftText)
  1524. {
  1525. prefixText += ApplyTextSettings(leftText, leftTextSettings);
  1526. }
  1527. // Suffix
  1528. string suffixText = "";
  1529. if (enableRightText)
  1530. {
  1531. suffixText += ApplyTextSettings(rightText, rightTextSettings);
  1532. }
  1533. if (enableBottomText)
  1534. {
  1535. suffixText += System.Environment.NewLine + ApplyTextSettings(bottomText, bottomTextSettings);
  1536. }
  1537. GetReferencesIfNecessary();
  1538. // Scale Fix
  1539. Vector3 currentLocalScale = transform.localScale;
  1540. if(!enable3DGame || !renderThroughWalls)
  1541. {
  1542. renderThroughWallsScale = 1f;
  1543. }
  1544. if (lastScaleFactor < 1)
  1545. {
  1546. lastScaleFactor = 1f;
  1547. }
  1548. float minScale = renderThroughWallsScale * lastScaleFactor;
  1549. if (currentLocalScale.x < minScale)
  1550. {
  1551. transform.localScale = new Vector3(minScale, minScale, minScale);
  1552. }
  1553. // Update Text
  1554. SetTextString(prefixText + numberText + suffixText);
  1555. transform.localScale = currentLocalScale;
  1556. // Get Colors
  1557. colors = new List<Color[]>();
  1558. alphas = new List<float[]>();
  1559. for (int n = 0; n < meshs.Count; n++)
  1560. {
  1561. Mesh mesh = meshs[n];
  1562. if(mesh != null)
  1563. {
  1564. Color[] color = mesh.colors;
  1565. float[] alpha = new float[color.Length];
  1566. for (int c = 0; c < color.Length; c++)
  1567. {
  1568. alpha[c] = color[c].a;
  1569. }
  1570. alphas.Add(alpha);
  1571. colors.Add(color);
  1572. }
  1573. else
  1574. {
  1575. colors.Add(new Color[0]);
  1576. alphas.Add(new float[0]);
  1577. }
  1578. }
  1579. // Finish
  1580. UpdateAlpha(currentFade);
  1581. // Event
  1582. OnUpdateText?.Invoke();
  1583. }
  1584. protected virtual void SetTextString(string fullString)
  1585. {
  1586. // Generate Mesh
  1587. textMeshPro.gameObject.SetActive(true);
  1588. textMeshPro.text = fullString;
  1589. textMeshPro.ForceMeshUpdate();
  1590. // Clear Meshs
  1591. ClearMeshs();
  1592. meshs = new List<Mesh>();
  1593. meshs.Add(Instantiate<Mesh>(textMeshPro.mesh));
  1594. meshFilterA.mesh = meshFilterB.mesh = meshs[0];
  1595. meshRendererA.sharedMaterials = meshRendererB.sharedMaterials = textMeshRenderer.sharedMaterials;
  1596. // Submeshes
  1597. int usedSubMeshes = 0;
  1598. for (int c = 0; c < textMeshPro.transform.childCount; c++)
  1599. {
  1600. MeshRenderer subMeshRenderer = textMeshPro.transform.GetChild(c).GetComponent<MeshRenderer>();
  1601. if (subMeshRenderer != null)
  1602. {
  1603. MeshFilter subMeshFilter = subMeshRenderer.GetComponent<MeshFilter>();
  1604. // Create new Submesh
  1605. if (subMeshRenderers.Count <= c)
  1606. {
  1607. GameObject subMeshA = NewMesh("Sub", meshRendererA.transform);
  1608. GameObject subMeshB = NewMesh("Sub", meshRendererB.transform);
  1609. subMeshRenderers.Add(new System.Tuple<MeshRenderer, MeshRenderer>(subMeshA.GetComponent<MeshRenderer>(), subMeshB.GetComponent<MeshRenderer>()));
  1610. subMeshFilters.Add(new System.Tuple<MeshFilter, MeshFilter>(subMeshA.GetComponent<MeshFilter>(), subMeshB.GetComponent<MeshFilter>()));
  1611. }
  1612. // Apply
  1613. System.Tuple<MeshRenderer, MeshRenderer> subRenderers = subMeshRenderers[c];
  1614. System.Tuple<MeshFilter, MeshFilter> subFilters = subMeshFilters[c];
  1615. subRenderers.Item1.sharedMaterials = subRenderers.Item2.sharedMaterials = subMeshRenderer.sharedMaterials;
  1616. Mesh newSubMesh = Instantiate<Mesh>(subMeshFilter.sharedMesh);
  1617. subFilters.Item1.mesh = subFilters.Item2.mesh = newSubMesh;
  1618. meshs.Add(newSubMesh);
  1619. // Enable
  1620. subRenderers.Item1.transform.localScale = subRenderers.Item2.transform.localScale = Vector3.one;
  1621. usedSubMeshes++;
  1622. }
  1623. }
  1624. // Hide Unused Meshs
  1625. for (int n = usedSubMeshes; n < subMeshRenderers.Count; n++)
  1626. {
  1627. subMeshRenderers[n].Item1.transform.localScale = subMeshRenderers[n].Item2.transform.localScale = Vector3.zero;
  1628. }
  1629. // Disable TMP
  1630. textMeshPro.gameObject.SetActive(false);
  1631. }
  1632. string ProcessIntegers(string integers, out bool shortened)
  1633. {
  1634. shortened = false;
  1635. // Short Suffix
  1636. if (digitSettings.suffixShorten)
  1637. {
  1638. int currentSuffix = -1;
  1639. int integerLength = integers.Length;
  1640. while (integerLength > digitSettings.maxDigits && currentSuffix < digitSettings.suffixes.Count - 1 && integerLength - digitSettings.suffixDigits[currentSuffix + 1] > 0)
  1641. {
  1642. currentSuffix++;
  1643. integerLength -= digitSettings.suffixDigits[currentSuffix];
  1644. }
  1645. if (currentSuffix >= 0)
  1646. {
  1647. // Get shortened integers
  1648. string shortenedIntegers = integers.Substring(0, integerLength);
  1649. if (digitSettings.suffixDecimals > 0)
  1650. {
  1651. // Get decimals after the shortened integers
  1652. string decimals = integers.Substring(integerLength, digitSettings.suffixDecimals);
  1653. // Hide zeros
  1654. while(digitSettings.suffixHideZeros && decimals.EndsWith("0"))
  1655. {
  1656. decimals = decimals.Substring(0, decimals.Length - 1);
  1657. }
  1658. // Combine
  1659. if(decimals.Length > 0)
  1660. {
  1661. integers = shortenedIntegers + digitSettings.suffixDecimalChar + decimals + digitSettings.suffixes[currentSuffix];
  1662. }
  1663. else
  1664. {
  1665. integers = shortenedIntegers + digitSettings.suffixes[currentSuffix];
  1666. }
  1667. }
  1668. else
  1669. {
  1670. integers = shortenedIntegers + digitSettings.suffixes[currentSuffix];
  1671. }
  1672. shortened = true;
  1673. return integers;
  1674. }
  1675. }
  1676. // Dots
  1677. if (digitSettings.dotSeparation && digitSettings.dotDistance > 0)
  1678. {
  1679. char[] chars = integers.ToCharArray();
  1680. integers = "";
  1681. for (int n = chars.Length - 1; n > -1; n--)
  1682. {
  1683. integers = chars[n] + integers;
  1684. if ((chars.Length - n) % digitSettings.dotDistance == 0 && n > 0)
  1685. {
  1686. integers = digitSettings.dotChar + integers;
  1687. }
  1688. }
  1689. }
  1690. return integers;
  1691. }
  1692. string ApplyTextSettings(string text, TextSettings settings)
  1693. {
  1694. string newString = text;
  1695. if (text == "")
  1696. {
  1697. return "";
  1698. }
  1699. // Formatting
  1700. if (settings.bold)
  1701. {
  1702. newString = "<b>" + newString + "</b>";
  1703. }
  1704. if (settings.italic)
  1705. {
  1706. newString = "<i>" + newString + "</i>";
  1707. }
  1708. if (settings.underline)
  1709. {
  1710. newString = "<u>" + newString + "</u>";
  1711. }
  1712. if (settings.strike)
  1713. {
  1714. newString = "<s>" + newString + "</s>";
  1715. }
  1716. // Custom Color
  1717. if (settings.customColor)
  1718. {
  1719. newString = "<color=#" + ColorUtility.ToHtmlStringRGBA(settings.color) + ">" + newString + "</color>";
  1720. }
  1721. if (settings.mark)
  1722. {
  1723. newString = "<mark=#" + ColorUtility.ToHtmlStringRGBA(settings.markColor) + ">" + newString + "</mark>";
  1724. }
  1725. if (settings.alpha < 1)
  1726. {
  1727. newString = "<alpha=#" + ColorUtility.ToHtmlStringRGBA(new Color(1, 1, 1, settings.alpha)).Substring(6) + ">" + newString + "<alpha=#FF>";
  1728. }
  1729. // Change Size
  1730. if (settings.size > 0)
  1731. {
  1732. newString = "<size=+" + settings.size.ToString().Replace(',', '.') + ">" + newString + "</size>";
  1733. }
  1734. else if (settings.size < 0)
  1735. {
  1736. newString = "<size=-" + Mathf.Abs(settings.size).ToString().Replace(',', '.') + ">" + newString + "</size>";
  1737. }
  1738. // Character Spacing
  1739. if (settings.characterSpacing != 0)
  1740. {
  1741. newString = "<cspace=" + settings.characterSpacing.ToString().Replace(',', '.') + ">" + newString + "</cspace>";
  1742. }
  1743. // Spacing
  1744. if (settings.horizontal > 0)
  1745. {
  1746. string space = "<space=" + settings.horizontal.ToString().Replace(',', '.') + "em>";
  1747. newString = space + newString + space;
  1748. }
  1749. if (settings.vertical != 0)
  1750. {
  1751. newString = "<voffset=" + settings.vertical.ToString().Replace(',', '.') + "em>" + newString + "</voffset>";
  1752. }
  1753. // Return
  1754. return newString;
  1755. }
  1756. private void ClearMeshs()
  1757. {
  1758. if (meshs != null)
  1759. {
  1760. if (Application.isPlaying)
  1761. {
  1762. foreach (Mesh mesh in meshs)
  1763. {
  1764. Destroy(mesh);
  1765. }
  1766. }
  1767. else
  1768. {
  1769. foreach (Mesh mesh in meshs)
  1770. {
  1771. DestroyImmediate(mesh);
  1772. }
  1773. }
  1774. }
  1775. }
  1776. #endregion
  1777. #region Fading
  1778. void HandleFadeIn(float delta)
  1779. {
  1780. if (currentFade < 1)
  1781. {
  1782. currentFade = Mathf.Min(1, currentFade + delta * fadeInSpeed);
  1783. UpdateFade(enableOffsetFadeIn, offsetFadeIn, enableCrossScaleFadeIn, currentScaleInOffset, enableScaleFadeIn, scaleFadeIn, enableShakeFadeIn, shakeOffsetFadeIn, shakeFrequencyFadeIn);
  1784. }
  1785. }
  1786. void HandleFadeOut(float delta)
  1787. {
  1788. // Event
  1789. if (isFadingOut == false)
  1790. {
  1791. isFadingOut = true;
  1792. OnFadeOut?.Invoke();
  1793. }
  1794. // Update fade value
  1795. currentFade = Mathf.Max(0, currentFade - delta * fadeOutSpeed);
  1796. UpdateFade(enableOffsetFadeOut, offsetFadeOut, enableCrossScaleFadeOut, currentScaleOutOffset, enableScaleFadeOut, scaleFadeOut, enableShakeFadeOut, shakeOffsetFadeOut, shakeFrequencyFadeOut);
  1797. // Remove from dictionaries
  1798. RemoveFromDictionary();
  1799. if (currentFade <= 0)
  1800. {
  1801. DestroyDNP();
  1802. }
  1803. }
  1804. void UpdateFade(bool enablePositionOffset, Vector2 positionOffset, bool enableScaleOffset, Vector2 scaleOffset, bool enableScale, Vector2 scale, bool enableShake, Vector2 shakeOffset, float shakeFrequency)
  1805. {
  1806. Vector2 basePosition = Vector2.zero;
  1807. float inverseFade = currentFade - 1;
  1808. if (enableShake)
  1809. {
  1810. basePosition = shakeOffset * Mathf.Sin(inverseFade * shakeFrequency) * inverseFade;
  1811. }
  1812. // Position Offset
  1813. if (enablePositionOffset)
  1814. {
  1815. Vector2 posOffset = positionOffset * inverseFade;
  1816. SetLocalPositionA(basePosition + posOffset);
  1817. SetLocalPositionB(basePosition - posOffset);
  1818. }
  1819. else
  1820. {
  1821. SetLocalPositionA(basePosition);
  1822. SetLocalPositionB(basePosition);
  1823. }
  1824. // Scale & Scale Offset
  1825. if (enableScaleOffset)
  1826. {
  1827. if (enableScale)
  1828. {
  1829. Vector3 scaleA = Vector2.Lerp(scaleOffset * scale, Vector2.one, currentFade);
  1830. scaleA.z = 1;
  1831. Vector3 scaleB = Vector2.Lerp(new Vector3(1f / scaleOffset.x, 1f / scaleOffset.y, 1) * scale, Vector2.one, currentFade);
  1832. scaleB.z = 1;
  1833. transformA.localScale = scaleA;
  1834. transformB.localScale = scaleB;
  1835. }
  1836. else
  1837. {
  1838. Vector3 scaleA = Vector2.Lerp(scaleOffset, Vector2.one, currentFade);
  1839. scaleA.z = 1;
  1840. Vector3 scaleB = Vector2.Lerp(new Vector3(1f / scaleOffset.x, 1f / scaleOffset.y, 1), Vector2.one, currentFade);
  1841. scaleB.z = 1;
  1842. transformA.localScale = scaleA;
  1843. transformB.localScale = scaleB;
  1844. }
  1845. }
  1846. else if (enableScale)
  1847. {
  1848. Vector3 newScale = Vector2.Lerp(scale, Vector2.one, currentFade);
  1849. newScale.z = 1;
  1850. transformA.localScale = transformB.localScale = newScale;
  1851. }
  1852. // Alpha
  1853. UpdateAlpha(currentFade);
  1854. }
  1855. public void UpdateAlpha(float progress)
  1856. {
  1857. float alphaFactor = progress * progress * baseAlpha * baseAlpha;
  1858. if(meshs != null)
  1859. {
  1860. for (int n = 0; n < meshs.Count; n++)
  1861. {
  1862. if (colors[n] != null && meshs[n] != null)
  1863. {
  1864. Color[] color = colors[n];
  1865. float[] alpha = alphas[n];
  1866. for (int c = 0; c < color.Length; c++)
  1867. {
  1868. color[c].a = alphaFactor * alpha[c];
  1869. }
  1870. meshs[n].colors = color;
  1871. }
  1872. }
  1873. }
  1874. // Event
  1875. OnUpdateFade?.Invoke(progress);
  1876. // Abstract
  1877. InternalUpdateFade(progress);
  1878. }
  1879. #endregion
  1880. #region Movement
  1881. void HandleFollowing(float deltaTime)
  1882. {
  1883. // No Target
  1884. if (followedTarget == null)
  1885. {
  1886. lastTargetPosition = Vector3.zero;
  1887. return;
  1888. }
  1889. // Get Position
  1890. Vector3 targetPosition = GetOtherPosition(followedTarget);
  1891. // Get Offset
  1892. if (lastTargetPosition != Vector3.zero)
  1893. {
  1894. targetOffset += targetPosition - lastTargetPosition;
  1895. }
  1896. lastTargetPosition = targetPosition;
  1897. // Apply Drag
  1898. if (followSettings.drag > 0 && currentFollowSpeed > 0)
  1899. {
  1900. currentFollowSpeed -= followSettings.drag * deltaTime;
  1901. if (currentFollowSpeed < 0)
  1902. {
  1903. currentFollowSpeed = 0;
  1904. }
  1905. }
  1906. // Move to Target
  1907. Vector3 oldOffset = targetOffset;
  1908. targetOffset = Vector3.Lerp(targetOffset, Vector3.zero, deltaTime * followSettings.speed * currentFollowSpeed);
  1909. position += oldOffset - targetOffset;
  1910. }
  1911. void HandleLerp(float deltaTime)
  1912. {
  1913. float deltaFactor = Mathf.Min(1, deltaTime * lerpSettings.speed);
  1914. Vector3 deltaOffset = remainingOffset * deltaFactor;
  1915. remainingOffset -= deltaOffset;
  1916. position += deltaOffset;
  1917. }
  1918. void HandleVelocity(float deltaTime)
  1919. {
  1920. if (velocitySettings.dragX > 0)
  1921. {
  1922. currentVelocity.x = Mathf.Lerp(currentVelocity.x, 0, deltaTime * velocitySettings.dragX);
  1923. }
  1924. if (velocitySettings.dragY > 0)
  1925. {
  1926. currentVelocity.y = Mathf.Lerp(currentVelocity.y, 0, deltaTime * velocitySettings.dragY);
  1927. }
  1928. currentVelocity.y -= velocitySettings.gravity * deltaTime * GetPositionFactor();
  1929. position += (GetUpVector() * currentVelocity.y + GetRightVector() * currentVelocity.x) * deltaTime;
  1930. }
  1931. Vector3 ApplyShake(Vector3 vector, ShakeSettings shakeSettings, float time)
  1932. {
  1933. float currentTime = time - startTime;
  1934. float currentFactor = Mathf.Sin(shakeSettings.frequency * currentTime) * GetPositionFactor();
  1935. if (shakeSettings.offset.y != 0)
  1936. {
  1937. vector += GetUpVector() * currentFactor * shakeSettings.offset.y;
  1938. }
  1939. if (shakeSettings.offset.x != 0)
  1940. {
  1941. vector += GetRightVector() * currentFactor * shakeSettings.offset.x;
  1942. }
  1943. return vector;
  1944. }
  1945. public Vector3 GetTargetPosition()
  1946. {
  1947. return position + remainingOffset;
  1948. }
  1949. public virtual Vector3 GetPosition()
  1950. {
  1951. return transform.position;
  1952. }
  1953. protected virtual void SetLocalPositionA(Vector3 localPosition)
  1954. {
  1955. transformA.localPosition = localPosition;
  1956. }
  1957. protected virtual void SetLocalPositionB(Vector3 localPosition)
  1958. {
  1959. transformB.localPosition = localPosition;
  1960. }
  1961. public virtual void SetPosition(Vector3 newPosition)
  1962. {
  1963. position = transform.position = newPosition;
  1964. }
  1965. protected virtual Vector3 GetOtherPosition(Transform other)
  1966. {
  1967. return other.position;
  1968. }
  1969. /// <summary>
  1970. /// Updates position without changing the position variable.
  1971. /// Used for temporary position changes like shaking movement.
  1972. /// </summary>
  1973. protected virtual void SetFinalPosition(Vector3 newPosition)
  1974. {
  1975. transform.position = newPosition;
  1976. }
  1977. /// <summary>
  1978. /// This function is for the GUI version of damage numbers pro.
  1979. /// It will position the damage number at your mouse position.
  1980. /// Leave 'canvasCamera' as 'null' if your canvas render mode is "Screen Space - Overlay"e
  1981. /// </summary>
  1982. public virtual void SetToMousePosition(RectTransform rectParent, Camera canvasCamera)
  1983. {
  1984. Vector2 mousePosition = Vector3.zero;
  1985. #if ENABLE_INPUT_SYSTEM && DNP_NewInputSystem
  1986. if(Mouse.current != null) {
  1987. RectTransformUtility.ScreenPointToLocalPointInRectangle(rectParent, Mouse.current.position.ReadValue(), canvasCamera, out mousePosition);
  1988. }
  1989. #else
  1990. RectTransformUtility.ScreenPointToLocalPointInRectangle(rectParent, Input.mousePosition, canvasCamera, out mousePosition);
  1991. #endif
  1992. SetAnchoredPosition(rectParent, mousePosition);
  1993. }
  1994. /// <summary>
  1995. /// Use this function after you spawn a GUI version of damage numbers pro.
  1996. /// </summary>
  1997. public virtual void SetAnchoredPosition(Transform rectParent, Vector2 anchoredPosition)
  1998. {
  1999. // Old Transform
  2000. Vector3 oldScale = transform.localScale;
  2001. Vector3 oldRotation = transform.eulerAngles;
  2002. // Set Parent and Position
  2003. transform.SetParent(rectParent, false);
  2004. transform.position = anchoredPosition;
  2005. // New Transform
  2006. transform.localScale = oldScale;
  2007. transform.eulerAngles = oldRotation;
  2008. }
  2009. /// <summary>
  2010. /// Use this function after you spawn a GUI version of damage numbers pro.
  2011. /// </summary>
  2012. public virtual void SetAnchoredPosition(Transform rectParent, Transform rectPosition, Vector2 relativeAnchoredPosition)
  2013. {
  2014. // Old Transform
  2015. Vector3 oldScale = transform.localScale;
  2016. Vector3 oldRotation = transform.eulerAngles;
  2017. // Set Parent and Position
  2018. transform.SetParent(rectParent, false);
  2019. transform.position = rectPosition.position + (Vector3)relativeAnchoredPosition;
  2020. // New Transform
  2021. transform.localScale = oldScale;
  2022. transform.eulerAngles = oldRotation;
  2023. }
  2024. protected virtual float GetPositionFactor()
  2025. {
  2026. return 1f;
  2027. }
  2028. #endregion
  2029. #region Spam Control
  2030. /// <summary>
  2031. /// Use this function to change the spam group of the popup.
  2032. /// Using the public spamGroup variable will also work within the spawn frame.
  2033. /// </summary>
  2034. public void SetSpamGroup(string newSpamGroup)
  2035. {
  2036. RemoveFromDictionary();
  2037. spamGroup = newSpamGroup;
  2038. AddToDictionary();
  2039. }
  2040. /// <summary>
  2041. /// Use this function to manually trigger spam control features like Combination, Destruction, Collision and Push.
  2042. /// </summary>
  2043. public void TriggerSpamControl()
  2044. {
  2045. if (spamGroup != "")
  2046. {
  2047. float time = unscaledTime ? Time.unscaledTime : Time.time;
  2048. TryCombination(time);
  2049. TryDestruction(time);
  2050. TryCollision();
  2051. TryPush();
  2052. }
  2053. }
  2054. void AddToDictionary()
  2055. {
  2056. if (spamGroup != "")
  2057. {
  2058. // Create Dictionary
  2059. if (spamGroupDictionary == null)
  2060. {
  2061. spamGroupDictionary = new Dictionary<string, HashSet<DamageNumber>>();
  2062. }
  2063. // Create HashSet
  2064. if (spamGroupDictionary.ContainsKey(spamGroup) == false)
  2065. {
  2066. spamGroupDictionary.Add(spamGroup, new HashSet<DamageNumber>());
  2067. }
  2068. // Add to HashSet
  2069. if (spamGroupDictionary[spamGroup].Contains(this) == false)
  2070. {
  2071. spamGroupDictionary[spamGroup].Add(this);
  2072. }
  2073. removedFromDictionary = false;
  2074. }
  2075. else
  2076. {
  2077. removedFromDictionary = true;
  2078. }
  2079. }
  2080. void RemoveFromDictionary()
  2081. {
  2082. if (!removedFromDictionary && spamGroup != "")
  2083. {
  2084. if (spamGroupDictionary != null && spamGroupDictionary.ContainsKey(spamGroup))
  2085. {
  2086. if (spamGroupDictionary[spamGroup].Contains(this))
  2087. {
  2088. spamGroupDictionary[spamGroup].Remove(this);
  2089. removedFromDictionary = true;
  2090. }
  2091. }
  2092. }
  2093. }
  2094. #endregion
  2095. #region Combination
  2096. void HandleCombination(float delta, float time)
  2097. {
  2098. if (myAbsorber != null)
  2099. {
  2100. if(myAbsorber.myAbsorber != null)
  2101. {
  2102. myAbsorber = myAbsorber.myAbsorber;
  2103. }
  2104. if (time - startTime < combinationSettings.spawnDelay)
  2105. {
  2106. absorbStartPosition = position;
  2107. absorbStartTime = time;
  2108. return;
  2109. }
  2110. // Reset Lifetime
  2111. startLifeTime = time;
  2112. // Combination Progress
  2113. float combinationProgress = combinationSettings.absorbDuration > 0 ? (time - absorbStartTime) / combinationSettings.absorbDuration : 1f;
  2114. // Move
  2115. if (combinationSettings.moveToAbsorber)
  2116. {
  2117. position = Vector3.Lerp(absorbStartPosition, myAbsorber.position, combinationProgress);
  2118. }
  2119. // Scale
  2120. combinationScale = combinationSettings.scaleCurve.Evaluate(combinationProgress);
  2121. // Alpha
  2122. baseAlpha = 1f * combinationSettings.alphaCurve.Evaluate(combinationProgress);
  2123. UpdateAlpha(currentFade);
  2124. if(combinationSettings.instantGain && combinationProgress > 0)
  2125. {
  2126. GiveNumber(time);
  2127. }
  2128. if (combinationProgress >= 1)
  2129. {
  2130. GiveNumber(time);
  2131. DestroyDNP();
  2132. }
  2133. }
  2134. }
  2135. void TryCombination(float time)
  2136. {
  2137. if (enableCombination == false) return; // No Combination
  2138. myAbsorber = null;
  2139. // Combination Methods
  2140. switch (combinationSettings.method)
  2141. {
  2142. case (CombinationMethod.ABSORB_NEW):
  2143. float oldestStartTime = time + 0.5f;
  2144. DamageNumber oldestNumber = null;
  2145. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2146. {
  2147. if (otherNumber != this && otherNumber.enableCombination && otherNumber.myAbsorber == null && otherNumber.startTime <= oldestStartTime)
  2148. {
  2149. if (Vector3.Distance(otherNumber.GetTargetPosition(), GetTargetPosition()) < combinationSettings.maxDistance * GetPositionFactor())
  2150. {
  2151. oldestStartTime = otherNumber.startTime;
  2152. oldestNumber = otherNumber;
  2153. }
  2154. }
  2155. }
  2156. if (oldestNumber != null)
  2157. {
  2158. GetAbsorbed(oldestNumber, time);
  2159. }
  2160. break;
  2161. case (CombinationMethod.REPLACE_OLD):
  2162. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2163. {
  2164. if (otherNumber != this && otherNumber.enableCombination)
  2165. {
  2166. if (Vector3.Distance(otherNumber.position, position) < combinationSettings.maxDistance * GetPositionFactor())
  2167. {
  2168. if (otherNumber.myAbsorber == null)
  2169. {
  2170. otherNumber.startTime = time - 0.01f;
  2171. }
  2172. otherNumber.GetAbsorbed(this, time);
  2173. }
  2174. }
  2175. }
  2176. break;
  2177. case (CombinationMethod.IS_ALWAYS_ABSORBER):
  2178. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2179. {
  2180. if (otherNumber != this && otherNumber.enableCombination && otherNumber.combinationSettings.method == CombinationMethod.IS_ALWAYS_VICTIM)
  2181. {
  2182. if (Vector3.Distance(otherNumber.position, position) < combinationSettings.maxDistance * GetPositionFactor())
  2183. {
  2184. if (otherNumber.myAbsorber == null)
  2185. {
  2186. otherNumber.startTime = time - 0.01f;
  2187. }
  2188. otherNumber.GetAbsorbed(this, time);
  2189. }
  2190. }
  2191. }
  2192. break;
  2193. case (CombinationMethod.IS_ALWAYS_VICTIM):
  2194. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2195. {
  2196. if (otherNumber != this && otherNumber.enableCombination && otherNumber.myAbsorber == null && otherNumber.combinationSettings.method == CombinationMethod.IS_ALWAYS_ABSORBER)
  2197. {
  2198. if (Vector3.Distance(otherNumber.GetTargetPosition(), GetTargetPosition()) < combinationSettings.maxDistance * GetPositionFactor())
  2199. {
  2200. GetAbsorbed(otherNumber, time);
  2201. break;
  2202. }
  2203. }
  2204. }
  2205. break;
  2206. }
  2207. }
  2208. void GetAbsorbed(DamageNumber otherNumber, float time)
  2209. {
  2210. if (myAbsorber != null)
  2211. {
  2212. return;
  2213. }
  2214. // Disable Other Features
  2215. otherNumber.enablePush = otherNumber.enableCollision = false;
  2216. // Set Absorber
  2217. myAbsorber = otherNumber;
  2218. // Initialize Variables
  2219. absorbStartPosition = position;
  2220. absorbStartTime = time;
  2221. givenNumber = false;
  2222. // Reset Lifetime
  2223. myAbsorber.startLifeTime = time;
  2224. // Spawn in Absorber
  2225. if (combinationSettings.teleportToAbsorber)
  2226. {
  2227. position = otherNumber.position;
  2228. }
  2229. }
  2230. void GiveNumber(float time)
  2231. {
  2232. if (!givenNumber)
  2233. {
  2234. givenNumber = true;
  2235. myAbsorber.number += number;
  2236. if (myAbsorber.myAbsorber == null)
  2237. {
  2238. myAbsorber.combinationScale = combinationSettings.absorberScaleFactor;
  2239. myAbsorber.startTime = time;
  2240. myAbsorber.currentLifetime = myAbsorber.lifetime + combinationSettings.bonusLifetime;
  2241. }
  2242. myAbsorber.UpdateText();
  2243. // Event
  2244. myAbsorber.OnAbsorb?.Invoke(number, myAbsorber.number);
  2245. }
  2246. }
  2247. #endregion
  2248. #region Destruction
  2249. void HandleDestruction(float time)
  2250. {
  2251. if (enableDestruction && isDestroyed)
  2252. {
  2253. if (time - startTime < destructionSettings.spawnDelay)
  2254. {
  2255. destructionStartTime = time;
  2256. return;
  2257. }
  2258. float destructionProgress = destructionSettings.duration > 0 ? (time - destructionStartTime) / destructionSettings.duration : 1f;
  2259. if (destructionProgress >= 1)
  2260. {
  2261. DestroyDNP();
  2262. }
  2263. else
  2264. {
  2265. baseAlpha = 1f * destructionSettings.alphaCurve.Evaluate(destructionProgress);
  2266. UpdateAlpha(currentFade);
  2267. destructionScale = destructionSettings.scaleCurve.Evaluate(destructionProgress);
  2268. }
  2269. }
  2270. }
  2271. void TryDestruction(float time)
  2272. {
  2273. if (enableDestruction)
  2274. {
  2275. isDestroyed = false;
  2276. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2277. {
  2278. if (otherNumber.isDestroyed == false && otherNumber != this && otherNumber.enableDestruction)
  2279. {
  2280. if (Vector3.Distance(otherNumber.GetTargetPosition(), GetTargetPosition()) < destructionSettings.maxDistance * GetPositionFactor())
  2281. {
  2282. otherNumber.isDestroyed = true;
  2283. otherNumber.destructionStartTime = time;
  2284. }
  2285. }
  2286. }
  2287. }
  2288. }
  2289. #endregion
  2290. #region Collision
  2291. void TryCollision()
  2292. {
  2293. if (enableCollision)
  2294. {
  2295. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2296. {
  2297. otherNumber.collided = false;
  2298. }
  2299. TryCollision(GetTargetPosition());
  2300. }
  2301. }
  2302. void TryCollision(Vector3 sourcePosition)
  2303. {
  2304. if (enableCollision)
  2305. {
  2306. collided = true;
  2307. Vector3 myTargetPosition = GetTargetPosition();
  2308. float radius = collisionSettings.radius * simulatedScale * GetPositionFactor();
  2309. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2310. {
  2311. if (otherNumber.enableCollision && otherNumber != this)
  2312. {
  2313. Vector3 otherTargetPosition = otherNumber.GetTargetPosition();
  2314. Vector3 offset = otherTargetPosition - myTargetPosition;
  2315. float distance = offset.magnitude;
  2316. if (distance < radius)
  2317. {
  2318. Vector3 offsetOrigin = otherTargetPosition - sourcePosition + offset + collisionSettings.desiredDirection * GetPositionFactor();
  2319. if (offsetOrigin == Vector3.zero)
  2320. {
  2321. offsetOrigin = new Vector3(Random.value - 0.5f, Random.value - 0.5f, Random.value - 0.5f);
  2322. }
  2323. otherNumber.remainingOffset += offsetOrigin.normalized * (radius - distance) * collisionSettings.pushFactor;
  2324. if(!otherNumber.collided)
  2325. {
  2326. otherNumber.TryCollision(sourcePosition);
  2327. }
  2328. }
  2329. }
  2330. }
  2331. }
  2332. }
  2333. #endregion
  2334. #region Push
  2335. void TryPush()
  2336. {
  2337. if (enablePush)
  2338. {
  2339. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2340. {
  2341. otherNumber.pushed = false;
  2342. }
  2343. TryPush(GetTargetPosition());
  2344. }
  2345. }
  2346. void TryPush(Vector3 sourcePosition)
  2347. {
  2348. if (enablePush)
  2349. {
  2350. pushed = true;
  2351. Vector3 myTargetPosition = GetTargetPosition();
  2352. float radius = pushSettings.radius * simulatedScale * GetPositionFactor();
  2353. DamageNumber bestPushTarget = null;
  2354. float pushDirection = pushSettings.pushOffset > 0 ? 1 : -1;
  2355. float heightCap = myTargetPosition.y + 1000 * pushDirection;
  2356. foreach (DamageNumber otherNumber in spamGroupDictionary[spamGroup])
  2357. {
  2358. if (otherNumber.enablePush && !otherNumber.pushed)
  2359. {
  2360. Vector3 targetPosition = otherNumber.GetTargetPosition();
  2361. if (targetPosition.y * pushDirection < heightCap* pushDirection && Vector3.Distance(myTargetPosition, targetPosition) < radius)
  2362. {
  2363. heightCap = targetPosition.y;
  2364. bestPushTarget = otherNumber;
  2365. }
  2366. }
  2367. }
  2368. if (bestPushTarget != null)
  2369. {
  2370. float heightDifference = (heightCap - myTargetPosition.y);
  2371. float pushDistance = (pushSettings.pushOffset * GetPositionFactor() - heightDifference);
  2372. bestPushTarget.remainingOffset.y += pushDirection > 0 ? Mathf.Max(pushDistance, 0) : Mathf.Min(pushDistance, 0);
  2373. bestPushTarget.TryPush(sourcePosition);
  2374. }
  2375. }
  2376. }
  2377. #endregion
  2378. #region Scale and Rotation
  2379. protected virtual void UpdateRotationZ()
  2380. {
  2381. SetRotationZ(meshRendererA.transform);
  2382. SetRotationZ(meshRendererB.transform);
  2383. }
  2384. protected void SetRotationZ(Transform transformTarget)
  2385. {
  2386. Vector3 localRotation = transformTarget.localEulerAngles;
  2387. localRotation.z = currentRotation;
  2388. transformTarget.localEulerAngles = localRotation;
  2389. }
  2390. void HandleRotateOverTime(float delta, float time)
  2391. {
  2392. currentRotation += currentRotationSpeed * delta * rotateOverTime.Evaluate((time - startTime) / currentLifetime);
  2393. }
  2394. void UpdateScaleAnd3D(bool beforeMeshBuild = false)
  2395. {
  2396. Vector3 appliedScale = originalScale;
  2397. lastScaleFactor = 1f;
  2398. // Scale Down from Combination
  2399. if (enableCombination)
  2400. {
  2401. combinationScale = Mathf.Lerp(combinationScale, 1f, Time.deltaTime * combinationSettings.absorberScaleFade);
  2402. lastScaleFactor *= combinationScale;
  2403. }
  2404. // Scale by Number Size
  2405. if (enableScaleByNumber)
  2406. {
  2407. lastScaleFactor *= numberScale;
  2408. }
  2409. // Scale over Lifetime
  2410. if (enableScaleOverTime)
  2411. {
  2412. float time = unscaledTime ? Time.unscaledTime : Time.time;
  2413. appliedScale *= scaleOverTime.Evaluate((time - startTime) / (currentLifetime + durationFadeOut));
  2414. }
  2415. // Destruction Scale
  2416. if (enableDestruction)
  2417. {
  2418. appliedScale *= destructionScale;
  2419. }
  2420. // Orthographic Scale
  2421. if (enableOrthographicScaling && !beforeMeshBuild)
  2422. {
  2423. appliedScale *= Mathf.Min(maxOrthographicSize, targetOrthographicCamera.orthographicSize / defaultOrthographicSize);
  2424. }
  2425. // Perspective
  2426. #region Perspective
  2427. if (enable3DGame && targetCamera != null)
  2428. {
  2429. // Face Camera
  2430. if(faceCameraView)
  2431. {
  2432. if(lookAtCamera)
  2433. {
  2434. transform.LookAt(transform.position + (transform.position - targetCamera.position));
  2435. }
  2436. else
  2437. {
  2438. transform.rotation = targetCamera.rotation;
  2439. }
  2440. }
  2441. // Initialize Offset
  2442. Vector3 offset = default;
  2443. float distance = default;
  2444. // Consistent Screen Size
  2445. if (consistentScreenSize)
  2446. {
  2447. // Calculate Offset
  2448. offset = finalPosition - targetCamera.position;
  2449. distance = Mathf.Max(0.004f, offset.magnitude);
  2450. // Calculate Scale
  2451. lastScaleFactor *= distance / distanceScalingSettings.baseDistance;
  2452. if (distance < distanceScalingSettings.closeDistance)
  2453. {
  2454. lastScaleFactor *= distanceScalingSettings.closeScale;
  2455. }
  2456. else if (distance > distanceScalingSettings.farDistance)
  2457. {
  2458. lastScaleFactor *= distanceScalingSettings.farScale;
  2459. }
  2460. else
  2461. {
  2462. lastScaleFactor *= distanceScalingSettings.farScale + (distanceScalingSettings.closeScale - distanceScalingSettings.farScale) * Mathf.Clamp01(1 - (distance - distanceScalingSettings.closeScale) / Mathf.Max(0.01f, distanceScalingSettings.farDistance - distanceScalingSettings.closeScale));
  2463. }
  2464. }
  2465. // FOV Scaling
  2466. if (scaleWithFov && !beforeMeshBuild)
  2467. {
  2468. lastScaleFactor *= targetFovCamera.fieldOfView / defaultFov;
  2469. }
  2470. // Apply scale
  2471. appliedScale *= lastScaleFactor;
  2472. simulatedScale = appliedScale.x;
  2473. // Render Through Walls
  2474. if (renderThroughWalls)
  2475. {
  2476. float near = 0.3f;
  2477. if (Camera.main != null)
  2478. {
  2479. near = Camera.main.nearClipPlane;
  2480. }
  2481. // Move close to camera
  2482. if (!consistentScreenSize)
  2483. {
  2484. offset = finalPosition - targetCamera.position;
  2485. distance = Mathf.Max(0.004f, offset.magnitude);
  2486. }
  2487. near += 0.0005f * distance + 0.02f + near * 0.02f * Vector3.Angle(offset, targetCamera.forward);
  2488. transform.position = offset.normalized * near + targetCamera.position;
  2489. // Adjust Scale
  2490. renderThroughWallsScale = near / distance;
  2491. appliedScale *= renderThroughWallsScale;
  2492. }
  2493. }
  2494. else
  2495. {
  2496. appliedScale *= lastScaleFactor;
  2497. simulatedScale = appliedScale.x;
  2498. }
  2499. #endregion
  2500. // Apply
  2501. transform.localScale = appliedScale;
  2502. // First Frame Fix
  2503. if (firstFrameScale)
  2504. {
  2505. if(durationFadeIn > 0)
  2506. {
  2507. transform.localScale = Vector3.zero;
  2508. }
  2509. firstFrameScale = false;
  2510. }
  2511. }
  2512. #endregion
  2513. #region C# Events
  2514. /// <summary>
  2515. /// This event is called during fade in and fade out.
  2516. /// The float value of the event determines the fade progress (0 = invisible, 1 = fully faded in).
  2517. /// </summary>
  2518. public event System.Action<float> OnUpdateFade;
  2519. /// <summary>
  2520. /// This event is called when the text content of the damage number has been updated.
  2521. /// </summary>
  2522. public event System.Action OnUpdateText;
  2523. /// <summary>
  2524. /// This event is called when the damage number starts fading out.
  2525. /// </summary>
  2526. public event System.Action OnFadeOut;
  2527. /// <summary>
  2528. /// This event is called when the damage number starts fading in.
  2529. /// </summary>
  2530. public event System.Action OnSpawn;
  2531. /// <summary>
  2532. /// This event is called when the damage number has fully faded out.
  2533. /// </summary>
  2534. public event System.Action OnDespawn;
  2535. /// <summary>
  2536. /// Called when a damage number absorbs another damage number.
  2537. /// The first float value is the absorbed number and the second float value is the new sum.
  2538. /// </summary>
  2539. public event System.Action<float, float> OnAbsorb;
  2540. #endregion
  2541. #region Internal Events
  2542. /// <summary>
  2543. /// This function is called while fading in or out.
  2544. /// </summary>
  2545. /// <param name="currentFade">The current fade and alpha factor meaning 0 = 0% and 1 = 100% faded in.</param>
  2546. protected virtual void InternalUpdateFade(float currentFade)
  2547. {
  2548. }
  2549. /// <summary>
  2550. /// This function is called on every update interval.
  2551. /// Frequency can be configured under performance settings.
  2552. /// </summary>
  2553. protected virtual void InternalUpdate(float deltaTime)
  2554. {
  2555. }
  2556. /// <summary>
  2557. /// This function is called whenever a damage number is spawned.
  2558. /// </summary>
  2559. protected virtual void InternalOnSpawn()
  2560. {
  2561. }
  2562. /// <summary>
  2563. /// Called every late update.
  2564. /// </summary>
  2565. protected virtual void OnLateUpdate()
  2566. {
  2567. }
  2568. /// <summary>
  2569. /// This event was created to fix a GUI specific issue.
  2570. /// </summary>
  2571. protected virtual void InternalOnPreSpawn()
  2572. {
  2573. }
  2574. #endregion
  2575. #region Unity Events
  2576. void OnDestroy()
  2577. {
  2578. // Static Reference
  2579. if (activeInstances != null && activeInstances.Contains(this))
  2580. {
  2581. activeInstances.Remove(this);
  2582. }
  2583. // Updater
  2584. DNPUpdater.UnregisterPopup(unscaledTime, updateDelay, this);
  2585. // Remove from dictionaries
  2586. RemoveFromDictionary();
  2587. // Clear Mesh
  2588. ClearMeshs();
  2589. // Remove from Pool
  2590. if (enablePooling && pools != null)
  2591. {
  2592. if (pools.ContainsKey(poolingID))
  2593. {
  2594. if (pools[poolingID].Contains(this))
  2595. {
  2596. pools[poolingID].Remove(this);
  2597. }
  2598. }
  2599. }
  2600. if (enablePooling && disableOnSceneLoad)
  2601. {
  2602. SceneManager.sceneLoaded -= OnSceneLoaded;
  2603. }
  2604. }
  2605. void OnSceneLoaded(Scene scene, LoadSceneMode mode)
  2606. {
  2607. if(currentFade > 0)
  2608. {
  2609. DestroyDNP();
  2610. }
  2611. }
  2612. void Reset()
  2613. {
  2614. if (!Application.isPlaying)
  2615. {
  2616. CheckAndEnable3D();
  2617. }
  2618. }
  2619. #endregion
  2620. }
  2621. }