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