Skip to content

deadlykam/CodeOptPro

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

CodeOptPro

Introduction

This is a simple Unity system that helps with performance.

Table of Contents:

Prerequisites

Unity Game Engine

Unity version 2021.3.25f1 and above should work. Some previous Unity versions should work as well but has not been tested. The main branch version is 2021.3.25f1


Stable Build

Stable-v1.3.0 is the latest stable build of the project. The unitypackage for this project can also be found there. If development is going to be done on this project then it is adviced to branch off of any Stable branches because they will NOT be changed or updated except for README.md. Any other branches are subjected to change including the main branch.


Installation

  1. First download the latest CodeOptPro-v1.3.0.unitypackage from the latest Stable build.
  2. Once download is completed open up the Unity project you want to use this project in.
  3. Now go to Assets -> Import Package -> Custom Package.
  4. Selet the CodeOptPro-v1.2.0 you just downloaded and open it.
  5. Make sure everything is selected in the Import Unity Package otherwise there will be errors. Press the Import button to import the package.
  6. Once import is done a new menu will popup called KamranWali.
  7. This step is optional. To open the interface for CodeOptPro simply go to KamranWali -> CodeOptPro -> CodeOptPro.

Setup

You must set the scene up before using the CodeOptPro. This MUST be done in every scene if this system is to be used. It is very easy to set up the scene for CodeOptPro. I made sure this process is also automated as well. Follow the steps below.

  1. Open the CodeOptPro interface by going to KamranWali -> CodeOptPro -> CodeOptPro.
  2. Once opened clicked the "SCENE SETUP" button. This will create a GameObject in the scene called Managers. By default two components will be added to called MonoAdvManager_Call and MonoAdvManager. That is it and the scene is ready for CodeOptPro.

Features

Advance Awake And Start Methods:

The advance awake and start feature works like Unity's API Awake and Start method. The differences are that only one class, MonoAdvManager_Call, calls the Unity's API Awake and Start methods and that class calls the custom awake(AwakeAdv) and start(StartAdv) methods of other classes. This reduces the need to use Unity's API which in turn helps with performance. The custom awake and start methods are also called for the GameObjects that are inactive as well. Also as of this writing this feature will NOT work with instantiated objects but only with already loaded objects.

You can now have multiple MonoAdvManager_Call components in one scene. I have also allowed grouping for this component as well. By default DefaultManagerCallHelper group is used. You can use your own groups as well. It is simple to create a new group. Just right click any folder where you want to store the group then go to Create -> CodeOptPro -> ScriptableObjects -> Managers -> MonoAdvManager_CallHelper. Give the group any name you want. Then select the game object that contains the newly created MonoAdvManager_Call component. In the Helper field select the newly created group. Now select a game object that contains MonoAdvManager component. In the Manager_Caller field select the newly created group. That is it, you are done setting up a new MonoAdvManager_Call component to work in the same scene. This is mainly useful when creating prefabs or updating them.

To use this feature simply import from using KamranWali.CodeOptPro.Managers; and then extend from the class called MonoAdv. Afterwards just implement the imported methods which will be AwakeAdv() and StartAdv(). Add your script to a GameObject. Now select your GameObject to open up the inspector for the script. Under MonoAdv Global Properties we need to set the Manager field. Click the field to open the selection window and for now select DefaultManagerHelper, I will later discuss in more details about DefaultManagerHelper. Now press the Play button and everything should be working.

Once you exit the play mode you will notice that your scene has become dirty. This is done intentionally because just before entering the play mode CodeOptPro automatically finds all the objects needs to be referenced and stores them to the correct managers. So once this process is done only then will the play mode start. If you want to can enable auto save from the CodeOptPro interface from KamranWali -> CodeOptPro. Enabling this will save everything when entering play mode. So if you make any changes to the scene for testing those too will be saved. That is why I have it disabled by default so that the user understands what will happen if enabled.

Awake And Start Call Order:

Another powerful feature of CodeOptPro is that you can order which group's awake and start methods should be called first. By default every group belongs to DefaultManagerHelper. Adding a new group is easy. Just follow the steps below:

  1. In the Project tab right any folder where you want to create a new group. Then go to Create -> CodeOptPro -> ScriptableObjects -> Managers -> MonoAdvManagerHelper. Give the group a name.
  2. Select the Managers GameObject in the scene. Then add a new component called MonoAdvManager. (You can add this component to any GameObject. It does NOT need to be the Managers GameObject).
  3. Now for the Helper property under the MonoAdvManager Global Properties select the newly created group.
  4. Now open up the CodeOptPro interface, KamranWali -> CodeOptPro, if not opened already and press the SETUP button.
  5. Finally select the Managers game object again. Now notice the MonoAdvManager_Call script. You will see under the Managers property the newly added group is added to the bottom. So basically this means that the DefaultManagerHelper will be first to be called in Awake() and Start() and then the newly added group. You can change the order by dragging the elements.

Performant Update:

The other good feature of CodeOptPro is that you can use custom update to update your scripts. This custom update allows you to share one Update() method with many scripts. This in turn saves lot of performance issues as calling Unity's Update() takes a hit on performance. There are two types of custom update class in CodeOptPro, UpdateManagerLocal and UpdateManagerGlobal. The main logic between the two are same but the only difference is that the local one needs to be referenced in coupled way while the global one is referenced in a decoupled way. To use the custom update follow the steps below.

UpdateManagerLocal:
  1. Create a new script and extend the class called MonoAdvUpdateLocal.
  2. Import all the abstract methods. Now let me explain the importance of each methods below:
  • a. AwakeAdv(): Custom awake method
  • b. StartAdv(): Custom start method
  • c. UpdateObject(): Custom update method. All the update logics must go here. Also if your logic requires calculation with Time.delta time then it is suggested that you calculate with updateManager.GetTime() method. This method will return the correct delta time calculation for the custom update. If this is not used and Time.deltaTime is used instead then your logic will give weird results.
  • d. IsActive(): This method is called by the update manager to check if the object is active or NOT. If object is active then the object's UpdateObject() method will be called. If the object is deactive then the object's UpdateObject() will NOT be called. You can set any conditions as to here to say what is considered active or inactive. For example you could use the object's gameobject to check if the object should be active or not by using gameObject.activeSelf. Remember if you do NOT use gameObject.activeSelf to check if the object should be active or not then the UpdateObject() will still be called when the game object is hidden.
  • e. SetActive(bool): This method sets the active state of the object. Use this method to activate or deactivate the object's UpdateObject() method. For example if the gameObject.activeSelf is being used in the method IsActive() then in SetActive(bool) just hide or show the game object by using the code gameObject.SetActive(isActivate). Again you can use any logic here that decides how to activate the object for update.
  1. Now once you are done implementing your new script then go back to the editor. Create a new GameObject or object for which your new script will be used for. Before adding your new script we first need to add the custom update manager. Click the Add Component button and search and add the script called UpdateManagerLocal.
  2. Now add your script. Once added drag and drop the UpdateManagerLocal into the updateManager field of your script which is under the MonoAdvUpdateLocal Local Properties.
  3. Finally press the play button and your script should work especially the update logic inside UpdateObject() method.

Now if you create more scripts that requires the use of update per frame then just drag and drop the UpdateManagerLocal to the updateManager field and they too will start to use update per frame and share the main Update() method from the UpdateManagerLocal. There is a field in UpdateManagerLocal called NumUpdate. This value means how many objects should be updated per frame. For example if this value is set to 5 then 5 objects will be update in one frame cycle. If there are too many objects that needs to be updated then increasing this value should make the update process much better but that depends on your scripts and their logic.

UpdateManagerGlobal:
  1. Create a new script and extend the class called MonoAdvUpdateGlobal.
  2. Import all the abstract methods and implement them. For explanation see point 2 in UpdateManagerLocal. It is similar to that.
  3. Create a new GameObjet or object for which your new script will be used for. Before adding your new script we first need to add the custom update manager. Click the Add Component button and search and add the script called UpdateManagerGlobal. It is suggested to add the UpdateManagerGlobal in a GameObject which is inside the Managers game object so that it remains organized because a scriptable object is used to access this update manager in a decouple fashion.
  4. Now we need to create the scriptable object for the UpdateManagerGlobal. Right click any folder where you want to store the helper scriptable object then go to Create -> CodeOptPro -> ScriptableObjects -> Managers -> UpdateManagerGlobalHelper. Then give it any name you want. After that set the newly created UpdateManagerGlobalHelper in the field called Helper under the UpdateManagerGlobal Global Properties inside the UpdateManagerGlobal.
  5. Now add the newly created UpdateManagerGlobalHelper in your script in the field called updateManager under MonoAdvUpdateGlobal Global Properties.
  6. Finally press the play button and your script should work especially the update logic inside UpdateObject() method.

UpdateManagerGlobal has the NumUpdate field as well and works similarly to UpdateManagerLocal so for explanation on how the field works please check the notes there. The good thing about UpdateManagerGlobal is that you only need to add the helper scriptable object for the update to happen. No need to search and drag and drop. Just simply add the scriptable object from the list and it is done. This saves time too.

Vector3 Performant Calculation:

I have also added performant Vector 3 calculations that will save some performance issue in the long run especially when it comes to Vector3 distance calculation. I will give just brief explanation of the methods

  1. Vec3.Distance(Vector3, Vector3) - This method calculates the distance between two Vector3s and the returned value is a squared value. This means that if you want to check if the distance of the two vector point is greater/less than 5 units then you must make the 5 squared which is simply 5x5 = 25. Meaning you are comparing against 25. This will save lot of performance issue later down the line when too many objects needs distance check.
  2. Vec3.Subtract(Vector3, Vector3) - This method subtracts two Vector3s without creating any garbage and returns a Vector3 value.
  3. Vec3.Add(Vector3, Vector3) - This method adds two Vector3s without creating any garbage and returns a Vector3 value.
  4. Vec3.Divide(Vector3, float) - This method multiplys a float value to the Vector3 value without creating any garbage and returns a Vector3 value.
  5. Vec3.Multiply(Vector3, float) - This method divides the Vector3 value with the float value without creating any garbage and returns a Vector3 value.

Performant Data Share/Use:

I have also added a feature that allows to share/use data in a performant way by using ScriptableObject. For now there are three categories of data share and each have their own different data types.

1. Actions

In this category different type of action delegates are used and share. So if you want to share action delegates with no parameters or some basic property parameters then you can do so. Basically if you want to share a specific method type in a script with other scripts then you can use the Action Types. Below are all the types.

  • Action - This shares delegates with no parameters. In the void Action.SetAction(System.Action) method only methods with no parameters can be set, example void SomeMethod(). Use void Action.CallAction() method to invoke the shared delegate. To use Action simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.Action.
  • ActionBool - This shares delegates with one bool parameter. In the void ActionBool.SetAction(System.Action) method only methods with one bool parameter can be set, example void SomeMethod(bool someBool). Use void ActionBool.CallAction() method to invoke the shared delegate. To use ActionBool simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionBool.
  • ActionDouble - This shares delegates with one double parameter. In the void ActionDouble.SetAction(System.Action) method only methods with one double parameter can be set, example void SomeMethod(double someDouble). Use void ActionDouble.CallAction() method to invoke the shared delegate. To use ActionDouble simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionDouble.
  • ActionFloat - This shares delegates with one float parameter. In the void ActionFloat.SetAction(System.Action) method only methods with one float parameter can be set, example void SomeMethod(float someFloat). Use void ActionFloat.CallAction() method to invoke the shared delegate. To use ActionFloat simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionFloat.
  • ActionGameObject - This shares delegates with one GameObject parameter. In the void ActionGameObject.SetAction(System.Action) method only methods with one GameObject parameter can be set, example void SomeMethod(GameObject someGameObject). Use void ActionGameObject.CallAction() method to invoke the shared delegate. To use ActionGameObject simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionGameObject.
  • ActionInt - This shares delegates with one int parameter. In the void ActionInt.SetAction(System.Action) method only methods with one int paramater can be set, example void SomeMethod(int someInt). Use void ActionInt.CallAction() method to invoke the shared delegate. To use ActionInt simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionInt.
  • ActionQuaternion - This shares delegates with one quaternion parameter. In the void ActionQuaternion.SetAction(System.Action) method only methods with one quaternion parameter can be set, example void SomeMethod(Quaternion someQuaternion). Use void ActionQuaternion.CallAction() method to invoke the shared delegate. To use ActionQuaternion simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionQuaternion.
  • ActionString - This shares delegates with one string parameter. In the void ActinoString.SetAction(System.Action) method only methods with one string parameter can be set, example void SomeMethod(string someString). Use void ActionString.CallAction() method to invoke the shared delegate. To use ActionString simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionString.
  • ActionTransform - This shares delegates with one transform parameter. In the void ActionTransform.SetAction(System.Action) method only methods with one transform parameter can be set, example void SomeMethod(Transform someTransform). Use void ActionTransform.CallAction() method to invoke the shared delegate. To use ActionTransform simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionTransform.
  • ActionVector2 - This shares delegates with one vector2 parameter. In the void ActionVector2.SetAction(System.Action) method only methods with one vector2 parameter can be set, example void SomeMethod(Vector2 someVec2). Use void ActionVector2.CallAction() method to invoke the shared delegate. To use ActionVector2 simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionVector2.
  • ActionVector3 - This shares delegates with one vector3 parameter. In the void ActionVector3.SetAction(System.Action) method only methods with one vector3 parameter can be set, example void SomeMethod(Vector3 someVec3). Use void ActionVector3.CallAction() method to invoke the shared delegate. To use ActionVector3 simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.Actions.ActionVector3.
  • BaseAction - If you want to create your own shared System.Action delegate type then extend from BaseAction. Just check out the above Action scripts to know how to code for it, it is very simple. The only difference would be the type and the name of the menu.
2. FixedVars

In this category different type of data types are shared, example bool, float, int, string etc. You only need to create one fixed var and share it with multiple objects, example - If five objects needs an int value of 1 then create a fixed var of type int that has the value 1 and share that. In that way only one int value of 1 is created instead of five which saves some memory. Like the name suggests the values are fixed and can NOT be updated. Below are all the types.

  • FixedBoolVar - This FixedVar shares bool data types. When creating the FixedBoolVar set the value either true or false by clicking the tick box. To get the value simply call the method bool FixedBoolVar.GetValue(). To use FixedBoolVar simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.FixedVars.FixedBoolVar.
  • FixedDoubleVar - This FixedVar shares double data types. When creating the FixedDoubleVar set the value to any double type value. To get the value simply call the method double FixedDoubleVar.GetValue(). To use FixedDoubleVar simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.FixedVars.FixedDoubleVar.
  • FixedFloatVar - This FixedVar shares float data types. When creating the FixedFloatVar set the value to any float type value. To get the value simply call the method float FixedFloatVar.GetValue(). To use FixedFloatVar simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.FixedVars.FixedFloatVar.
  • FixedIntVar - This FixedVar shares int data types. When creating the FixedIntVar set the value to any int type value. To get the value simply call the method int FixedIntVar.GetValue(). To use FixedIntVar simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.FixedVars.FixedIntVar.
  • FixedLayerMaskVar - This FixedVar shares LayerMask data types. When creating the FixedLayerMaskVar select any layer mask values. To get the value simply call the method LayerMask FixedLayerMask.GetValue(). To use FixedLayerMaskVar simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.FixedVar.FixedLayerMaskVar.
  • FixedStringVar - This FixedVar shares string data types. When creating the FixedStringVar set the value to any string type value. To get the value simply call the method string FixedStringVar.GetValue(). To use FixedStringVar simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.FixedVars.FixedStringVar.
  • FixedVector2Var - This FixedVar shares Vector2 data types. When creating the FixedVector2Var set the value to any Vector2 type value. To get the value simply call the method Vector2 FixedVector2Var.GetValue(). To use FixedVector2Var simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.FixedVars.FixedVector2Var.
  • FixedVector3Var - This FixedVar shares Vector3 data types. When creating the FixedVector3Var set the value to any Vector3 type value. To get the value simply call the method Vector3 FixedVector3Var.GetValue(). To use FixedVector3Var simply import by calling using KamranWali.CodeOptPro.ScriptableObjects.FixedVars.FixedVector3Var.
  • BaseFixedVar - If you want to create your own FixedVar type then you must extend from BaseFixedVar. Just check out the above FixedVar scripts to know how to code for it, it is very simple. The only difference would be the type and the name of the menu.
3. Vars

Just like FixedVars this category shares different type of data types as well, example bool, float, int, string etc. The only difference is that you can NOT set any values here like FixedVars and the values may change. Vars basically shares values that are constantly changing. For example - You have 5 objects that wants to know the player's position. Then just create a Vector3Var and make the player script constantly update the newly created Vector3Var. Then add the newly created Vector3Var to the other 5 objects. Now all of those 5 objects have access to the player's position without the need of player script reference. Below are all the types.

  • BoolVar - This Var shares bool data types. To set the value simply call void BoolVar.SetValue(bool value). To get the value just call bool BoolVar.GetValue(). To use BoolVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.BoolVar.
  • CameraVar - This Var shares Camera data types. To set the value simply call void CameraVar.SetValue(Camera value). To get the value just call Camera CameraVar.GetValue(). To use CameraVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.CameraVar.
  • DoubleVar - This Var shares double data types. To set the value simply call void DoubleVar.SetValue(double value). To get the value just call double DoubleVar.GetValue(). To use DoubleVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.DoubleVar.
  • FloatVar - This Var shares float data types. To set the value simply call void FloatVar.SetValue(float value). To get the value just call float FloatVar.GetValue(). To use FloatVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.FloatVar.
  • GameObjectVar - This Var shares GameObject data types. To set the value simply call void GameObjectVar.SetValue(GameObject value). To get the value just call GameObject GameObjectVar.GetValue(). To use GameObjectVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.GameObjectVar.
  • IntVar - This Var shares int data types. To set the value simply call void IntVar.SetValue(int value). To get the value just call int IntVar.GetValue(). To use IntVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.IntVar.
  • QuaternionVar - This Var shares Quaternion data types. To set the value simply call void QuaternionVar.SetValue(Quaternion value). To get the value just call Quaternion QuaternionVar.GetValue(). To use QuaternionVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.QuaternionVar.
  • StringVar - This Var shares string data types. To set the value simply call void StringVar.SetValue(string value). To get the value just call string StringVar.GetValue(). To use StringVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.StringVar.
  • TransformVar - This Var shares Transform data types. To set the value simply call void TransformVar.SetValue(Transform value). To get the value just call Transform TransformVar.GetValue(). To use TransformVar just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.TransformVar.
  • Vector2Var - This Var shares Vector2 data types. To set the value simply call void Vector2Var.SetValue(Vector2 value). To get the value just call Vector2 Vector2Var.GetValue(). To use Vector2Var just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.Vector2Var.
  • Vector3Var - This Var shares Vector3 data types. To set the value simply call void Vector3Var.SetValue(Vector3 value). To get the value just call Vector3 Vector3Var.GetValue(). To use Vector3Var just import by calling using KamranWali.CodeOptPro.ScriptableObjects.Vars.Vector3Var.
  • BaseVar - If you want to create your own Var type then you must extend from BaseVar. Just check out the above Var scripts to know how to code for it, it is very simple. The only difference would be the type and the name of the menu.

Variable Creator

There are 2 ways to create a variable from any category. Below are the two ways.

  1. The first way is to right click any folder where you want to create a variable and then go to CodeOptPro -> ScriptableObjects -> Actions or CodeOptPro -> ScriptableObjects -> FixedVars or CodeOptPro -> ScriptableObjects -> Vars. This will then create a variable and you can name it anything you want and if it is a type where you can give values then you can give it a value as well.
  2. The second way is use the Variable Creator window to create any variable. You can open the Variable Creator window by going to the menu KamranWali -> CodeOptPro -> Variable Creator. I would recommend using the Variable Creator as it is faster to create a variable and you can select which location every specific variables should be created in. See the image below how the Variable Creator looks like and I will explain the highlighted parts.
Variable-Creator1.png
Variable Creator
  • a. Name - This where you give the name of the variable you want to create.
  • b. Update Path - If you want to update the path where the new variable will be created then right click the folder where the variable should be created and selected Copy Path. Paste the copied path in the path field, f. Finally press the Update Path button and the path will be updated. This will only update the path for 1 variable type, in this case Action type. This way the Variable Creator will allow you to have different paths for different variable types. The default path is Assets/KamranWali/CodeOptPro/SO_Data.
  • c. Category - This is where you get to select from which category the variable will be created. For now there are 3 categories which are Actions, FixedVariables and Variables.
  • d. Variable Type - This is where you get to select which type of variable to create. Each category have different type of variable types. The image shows Actions but this label changes when selecting a different category.
  • e. Log - Here the logs for the Variable Creator will be shown.
  • f. Path - This is the path or folder location where the new variable will be created. You can updated this path as well. Follow the instructions in b. to see how to update path.
  • g. Create Variable - This button will create the new variable type. Remember to give a name to the variable otherwise this button will NOT be visible. Also the name of the button Create Variable will change with the variable type selected so that you will know what type you are creating.

TimerCountdown:

Added a timer interface that handles everything timer related blue print. Used it to create countdown timer called TimerCountdown. The script does as the name suggests which is it count downs to 0. This timer also calculates the normal value for the count down which may help later to sync up some other logic or features of yours.

To use TimerCountdown you must first call the method void StartSetup(float) in the Start or StartAdv methods. The float value it takes in is in seconds which means 1f = 1s. Then in the Update method you must call void UpdateTimer(float) method for the count down to happen. The float value here determines how fast the count down will happen so 1 is normal speed, higher than 1 means countdown will be faster and less than 1 means countdown will happen slower. void UpdateTimer(float) also has Time.deltaTime being calculated so no need to calculate that. I have commented all the methods for the timer. If you want to know what each method does then please read all the comments for the method in ITimer.

Bars:

Added a bar interface that handles everything bar related blue print called IBar. Used it to create an abstract class called BaseBar that handles most common logics between bars. Then used that to create NormalBar that works like any common bars.

To use NormalBar you must first call the void StartSetup(int) in the Start or StartAdv methods. The int value it takes is the max value for the bar. The value of the bar will be from 0(included) to max value(included). I have commented about the methods in the script so please read the comments in IBar to get an idea. I recommend using NormalBar as health bars for npcs and players. I will give brief explanations of the methods below for the NormalBar

  1. void StartSetup(int) - This method sets up the bar at the start of the game. This method MUST be called in the start method otherwise there will be error. Later if you want to change the max value for the bar then you can call this method again.
  2. float GetNormalValue() - This method returns the normal value of the bar which is basically curValue/maxValue.
  3. void AddValue(int) - This will add to the bar's current value. The current value will not exceed the maximum value and will stop at the maximum value.
  4. int GetCurrentValue() - This method returns the current bar value.
  5. void SetCurrent(int) This method sets the current value. If the value given is higher than the maximum value then the maximum value will be set so current value will never exceed maximum value.
  6. int GetMaxValue - This method returns the maximum threshold value of the bar.
  7. bool IsDepleted() - This method checks if the bar has been depleted that is if the current value has become 0. 0 = true, 0 != false.
  8. void Restore() - This method restores the bar to the maximum value. That is making current value equal to maximum value.
  9. void RemoveValue(int) - This method removes an amount from the current value. The lowest current value will go is 0 and will not go below it.

Searches:

For now I have added a performant way to use the ray cast to hit objects and to get the farthest and closest objects. No need to keep repeating same code over and over again like a cycle. Below are the scripts that does that.

This script uses a ray to find all the objects that that ray has hit. It uses Unity's Physics.RaycastNonAlloc(...) method to find objects that have hit the ray. Using this method is very performant friendly. I have commented in the scripts in what each constructor and method does but below I will explain it briefly.

  • RayHit(int hitSize, float rayRange, LayerMask hitMask) - This is the constructor that MUST be called to initialize the RayHit object. The hitSize parameter will give a limited number of objects to be hit by the ray and then stored. The rayRange parameter defines the range of the ray or how far the ray can go. The hitMask parameter tells the ray which layer the ray can hit. Example: RayHit rayHit = new RayHit(5, 100f, someLayerMask);
  • void SetRay(Ray ray) - This method will replace the current ray with the given ray. So if there are other rays that you want to use then you can just replace the ray here. Example: rayHit.SetRay(someRay);
  • void SetRay(Vector3 origin, Vector3 dir) - This method will set the current ray with the given parameters. The origin parameter is the point from where the ray will be generated. The dir parameter is the direction the ray will go towards. Example: rayHit.SetRay(Vector3.zer0, Vector3.forward);
  • void CalculateHits - This method calculates and stores the number of objects the ray has hit. To get the number of objects hit call the method int GetNumHit() after. Example: rayHit.CalculateHits(); int numHit = rayHit.GetNumHit();
  • bool IsHit() - This method checks if at least 1 object has been hit. If there is a hit then it will return true. If there are NO hits then it will return false.
  • int GetNumHit() - This method gets the number of objects that has been hit. For this method to work void CalculateHits() must be called first. Example: rayHit.CalculateHits(); int numHit = rayHit.GetNumHit();

This script finds the closest or farthest hit object from the ray. This script also extends from RayHit script so it contains all the methods and features from there as well. Again I have commented in the script to help with understanding it so check the script out and will explain the script briefly here as well.

  • RaycastHit GetClosestHit(Vector3 origin) - This method finds the closest hit object. The hit objects are found when the ray hits the objects. The parameter origin is the point from where the distance check is done and from where the closest hit object is found.
Example:
RayHitSearch rayHitSearch = new RayHitSearch(5, 100f, someLayerMask);
rayHitSearch.SetRay(Vector3.zero, Vector3.forward);
rayHitSearch.CalculateHits();
RaycastHit hitObject = rayHitSearch.GetClosestHit(Vector3.zero);
  • RaycastHit GetFarthestHit(Vector3 origin) - This method finds the farthest hit object. The hit objects are found when the ray hits the objects. The parameter origin is the point from where the distance check is done and from where the farthest hit object is found.
Example:
RayHitSearch rayHitSearch = new RayHitSearch(5, 100f, someLayerMask);
rayHitSearch.SetRay(Vector3.zero, Vector3.forward);
rayHitSearch.CalculateHits();
RaycastHit hitObject = rayHitSearch.GetFarthestHit(Vector3.zero);

Pooling System

Added the pooling system feature. This tool allows to pool any objects or components. By default I have added pooling system for GameObjects and Transforms called PoolGameObjectGlobal, PoolGameObjectLocal, PoolTransformGlobal and PoolTransformLocal. You can also create your own pooling system by extending the scripts BasePoolGlobal or BasePoolLocal. Let me explain how the pooling system works with the already added default pool objects and how you can apply that to your own custom pooling objects or components.

For example let's use the PoolGameObjectGlobal. Create a GameObject in the scene called ExamplePool. Now add 10 more GameObjects inside ExamplePool. Now we need to create the RequestGameObject scriptable object. To do this simply right click anywhere in the project and go to Create -> CodeOptPro -> ScriptableObjects -> RequestObjects and then select RequestGameObject. Name it RequestSomeObjects. Now add the PoolGameObjectGlobal component to the ExamplePool GameObject. Now we need to fill up the properties of the component. For the Manager and Update Manager fields give whatever MonoAdvManagerHelper and UpdateManagerGlobalHelper you are using. Then for the AddRequest field give the RequestSomeObjects, for the SizeRequest give FixedIntVar value of 5 and for EnableAtStart give the FixedBoolVar value of true. I will explain what each of these field does later down below.

Now lets create a new script called RequestExample and write the code like below.

using KamranWali.CodeOptPro.Pools;
using KamranWali.CodeOptPro.ScriptableObjects.RequestObjects;
using UnityEngine;

public class RequestExample : MonoBehaviour, IRequestObject<GameObject>
{
    [SerializeField] private RequestGameObject _requestObject;

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)) _requestObject.CallAction(this);
    }

    public void ReceivePoolObject(GameObject poolObject)
    {
        Debug.Log($"Requested GameObject: {poolObject.name}");
    }
}

To recieve pool objects you must extend the generic interface script called IRequesetObject and because we are working with GameObjects the generic type must be GameObject as well. The method void ReceivePoolObject(GameObject) is the interface method that will receive the pool object. Here we will simply print the name of the game object received. To request for a pool object must call the _requestObject which in this case will take IRequestObject which the script is a child of.

Now go back to the editor. Create another GameObject anywhere in the scene and call it Receiver. Add the script RequestGameObject to it. For the Request Object field give the RequestSomeObjects scriptable object. Finally hit play and press the Space Bar. You will notice that each time you press the Space Bar a new GameObject is received and the name for it is shown. After hiting the Space Bar more than 10 times you will see the objects received begins to cycle. That is it, this is how you use the pooling system.

Alright so let me explain the last 3 fields of the pool system.

  • Add Request: This is the delegate scriptable object that will request the pooling system for an object.
  • Size Request: This is the size of the request array for the pooling system. This means that the pooling system can only handle n amount of requests at a time. If the total requests received at one time is greater than n then some of the requests will be lost. So make sure you give enough request size so that no requests are missed.
  • Enable At Start: This flag decides if to allow the pool system to work when the game starts. If you make it false then you can activate the pooling system yourself by sending true to the method SetActive(bool).
Custom Pooling System:

To create a custom pooling system is simple. There are two tasks you need to do for the custom pooling system to work. The first task is to extend either BasePoolGlobal or BasePoolLocal. Set the generic type to any object or component you wish, Example: To a script called SomeObject

Example:

public class PoolSomeObjectGlobal : BasePoolGlobal<SomeObject>
{
}

The seacond task is to create a BaseRequest scriptable object that will be used for requesting pool objects. The generic type here MUST be same as the one in pooling system, Example: To the script called SomeObject

Example:

[CreateAssetMenu(fileName = "RequestSomeObject",
                 menuName = "CodeOptPro/ScriptableObjects/RequestObjects/" +
                            "RequestSomeObject",
                 order = 1)]
public class RequestSomeObject : BaseRequest<SomeObject> { }

That is it. You are now done in creating the custom pooling system. All you have to do now is create the scriptable object and apply that to both the pooling system and the one that will receive the objects.

Folder Selection Palette

Variable-Creator1.png
Fig: Folder Selection Palette

This feature allows the user to store the path locations of any folder and then by clicking the button will select that folder in the Project view. No need to look around for the important folders anymore. To open Folder Selection Palette window click the menu KamranWali then CodeOptPro and finally Folder Selection Palette. Let me explain what each field does.

  • a. Name - This is where you give the name for the folder selection. It can be any name but keeping it same as the folder name will help you understand which folder it is.
  • b. Path - This is where you give the path of the folder you want to select. It is very easy to get the path just open up the folder in the Project View window then right click an empty space in the folder and select Copy Path. Once the path has been copied then just paste it in this field.
  • c. ADD FOLDER - Once you have given the name and the path this button will be enabled. Clicking this button will add a folder selection button below and the name will be same as the name given while adding. The image Fig: Added Selection Button below shows how it looks like when a selection button is added.
  • d. Mode Type - The mode type allows the user to either select the folder locations or edit the folder locations buttons. There are two modes. The first mode is called Select Mode. This mode allows the user to select the folder selection buttons. The second mode is called Edit Mode. This is the mode where the user can edit the selections buttons. See the image Fig: Edit Mode to see how the edit mode looks like. Here you can move around the buttons and/or delete them. Make sure after making all the changes you click the SAVE button to save all the changes.
Variable-Creator1.png
Fig: Added Selection Button
Variable-Creator1.png
Fig: Edit Mode

Developer

I tried to keep the development process for the developers as simple as possible. So if you want to modify CodeOptPro then I will try my best to explain how to.

This is the main class that handles all the automations for the CodeOptPro system. The Setup() method is where it happens. If create a new script that extends from classes MonoAdv, MonoAdvUpdateLocal, MonoAdvUpdateGlobal, UpdateManagerLocal or UpdateManagerGlobal then you do not need make any changes in this method. BUT if you create a new script that does not extend any of the mentioned classes then you have to apply the logics necesarry for automation and setting up for use. The best way to achieve this is to follow the steps for the field ums_Local, ums_Global or _objects.

This is the class that calls the custom awake and start methods for MonoAdvManager. This is also the only class that uses Unity's Awake() and Start() methods. That is why there should be only one of these classes throughout the entire scene. The Awake() method calls the PreAwakeAdv() and AwakeAdv(). The main purpose of PreAwakeAdv() is to allow the MonoAdvManager to setup up the helper reference which can then later be used by every script that has that reference. The Start() method calls the StartAdv() method of the manager.

The methods inside the region called Editor Script are NOT recommended to be called during runtime. This is only needed for CodeOptProSetupAuto during automation setup.

This is the class that calls all the custom awake and start methods for every objects that is stored inside it. The methods AwakeAdv() and StartAdv() loops through all the objects and calls each one of their custom awake and start methods. It is recommended NOT to call the editor scripts under the region called Editor Methods during runtime. Those methods are only used for CodeOptProSetupAuto.

This is the class responsible for calling the custom update method in MonoAdvUpdateLocal objects. This class shares the Update() methods with the objects stored inside it. The field _timeDelta is very important in this class. This field must be calculated with Time.deltaTime to get the actual time for accurate calculation. The method GetTime() already calculates the actual time. Also in the AwakeAdv() you can see how _timeDelta is calculated.

The method UpdateObject() is where objects' custom update are called. Here you can see the objects bool IsActive() method is called to see if the object is active and ONLY then their UpdateObject() method is called.

It is recommended NOT to call the editor scripts under the region called Editor Methods during runtime. Those methods are only used for CodeOptProSetupAuto.

This class is same as UpdateManagerLocal. Please see the description in UpdateManagerLocal for how UpdateManagerGlobal works. The only difference is that UpdateManagerGlobal uses a scriptable object called UpdateManagerGlobalHelper to communicate between different scripts. This allows the scripts to be decoupled where as for UpdateManagerLocal the scripts become coupled.

This is the class the allows custom awake and start to be used by its children. If you look at the code you will see there are 2 methods inside the region called Editor Scripts. The methods are Init() and bool HasManager(). Let me explain the methods below:

  • Init() - This method initializes the MonoAdv object and is ONLY called from CodeOptProSetupAuto. In this case it is adding itself to the helper manager. So if you require and form of setup before entering the play mode and need automation this is the method to override and implement.
  • bool HasManager() - This method just checks if all the managers has been setup. If you require certain logic to be true then this is the method to implement. If this method returns false then CodeOptProSetupAuto will stop the automation process and give a warning that something went wrong and needs fixing. This is basically to help the user know what went wrong. It is recommended NOT to call these methods during run time and ONLY to call them if you understand what they do.

This is the class that allows custom update to be used by its children. It also extends MonoAdv so all the children will also get the custom awake and start methods. These class 3 main methods and 2 editor methods. I will explain them briefly as follow:

  • Init() - This method initializes the MonoAdvUpdateLocal and is ONLY caled from CodeOptProSetupAuto. In this case it is adding itself to the MonoAdvManager and UpdateManagerLocal.
  • bool HasManager() - This method checks if all the managers has been setup. In this case the MonoAdvManager and the UpdateManagerLocal. This method is only called from CodeOptProSetupAuto.
  • UpdateObject() - This is an abstract method that needs to implemented by the children classes. This is the custom update method. So any upate logic should be done here.
  • SetActive(bool) - This method is an abstract method that needs to be implemented by the children classes. In this method you should decide how the object should be active/deactive for custom update.
  • bool IsActive() - This method is an abstract method that needs to be implemented by the children classes. This method checks if the object is active/deactive for custom update. When the method returns true then the object's UpdateObject() will be called by the update manager but when the method returns false then the object's UpdateObject() will NOT be called.

When extending this class you must refer a UpdateManagerLocal in the updateManager field in the inspector. Otherwise it won't work and the CodeOptPro system will throw an error warning while setup.

This is same as MonoAdvUpdateLocal. See the details there to understand. The only difference is that UpdateManagerGlobalHelper needs to be refered in the updateManager field in the inspector.


Updates

Here I will share all the updates done to the newer versions. Below are the updates.

  1. Added IUpdate interface. This is needed so that other interfaces can extend from this interface and get the methods. Later may also simplify search for MonoAdv objects by using IUpdate. This may allow other custom classes to be searched as well.
  2. Added pooling system.
  3. Added IUpdateManager interface. Now UpdateManagerLocal, UpdateManagerGlobal and UpdateManagerGlobalHelper are child of IUpdateManager. This helps with generic scripts with constraints.
  4. Added a new feature called Folder Selection Palette. This feature allows the user to store any folder locations and then later can go to those folders instantly, in the Project View, by clicking a button. Check out more details about Folder Selection Palette here.

Versioning

The project uses Semantic Versioning. Available versions can be seen in tags on this repository.


Authors


License

This project is licensed under the MIT License - see the LICENSE.md file for details.