Best practice for placing private public functions

I see there are public and private functions in different scripts. As the files get bigger, I’m sure it will be difficult to read the functions. In app development (e.g. SWIFT XCODE for IOS), we have constants, utilities, extensions files to store the public or static functions. Is there a similar approach in Unity?

Unfortunately, I’m not familiar with Swift XCode.

In Unity/C#, we want to keep the methods (functions) with the components that use them.

I do find it’s a good idea to organize your scripts to make it easier to locate members within them… If you use the #region/#endregion tags, most editors will let you collapse these regions to reduce clutter within the scripts… Here’s an example of a completed Mover script (this contains some methods that are not in use quite yet:

using RPG.Core;
using GameDevTV.Saving;
using Newtonsoft.Json.Linq;
using UnityEngine;
using UnityEngine.AI;
using RPG.Attributes;

namespace RPG.Movement
{
    public class Mover : MonoBehaviour, IAction, ISaveable, IJsonSaveable
    {
    
    #region fields
        [SerializeField] Transform target;
        [SerializeField] float maxSpeed = 6f;
        [SerializeField] float maxNavPathLength = 40f;
    #endregion
            
    #region variables
        NavMeshAgent navMeshAgent;
        Health health;
    #endregion
            
    #region callbacks
        private void Awake() {
            navMeshAgent = GetComponent<NavMeshAgent>();
            health = GetComponent<Health>();
        }

        void Update()
        {
            navMeshAgent.enabled = !health.IsDead();

                UpdateAnimator();
        }
    #endregion
    
    #region public methods
        public void StartMoveAction(Vector3 destination, float speedFraction)
        {
            GetComponent<ActionScheduler>().StartAction(this);
            MoveTo(destination, speedFraction);
        }

        public bool CanMoveTo(Vector3 destination)
        {
            NavMeshPath path = new NavMeshPath();
            bool hasPath = NavMesh.CalculatePath(transform.position, destination, NavMesh.AllAreas, path);
            if (!hasPath) return false;
            if (path.status != NavMeshPathStatus.PathComplete) return false;
            if (GetPathLength(path) > maxNavPathLength) return false;

            return true;
        }

        public void MoveTo(Vector3 destination, float speedFraction)
        {
            navMeshAgent.destination = destination;
            navMeshAgent.speed = maxSpeed * Mathf.Clamp01(speedFraction);
            navMeshAgent.isStopped = false;
        }

        public void Cancel()
        {
            navMeshAgent.isStopped = true;
        }
        
    #endregion
        
    #region private methods    
        
        private void UpdateAnimator()
        {
            Vector3 velocity = navMeshAgent.velocity;
            Vector3 localVelocity = transform.InverseTransformDirection(velocity);
            float speed = localVelocity.z;
            GetComponent<Animator>().SetFloat("forwardSpeed", speed);
        }

        private float GetPathLength(NavMeshPath path)
        {
            float total = 0;
            if (path.corners.Length < 2) return total;
            for (int i = 0; i < path.corners.Length - 1; i++)
            {
                total += Vector3.Distance(path.corners[i], path.corners[i + 1]);
            }

            return total;
        }
    #endregion
        
    #region ISaveable
        public object CaptureState()
        {
            return new SerializableVector3(transform.position);
        }

        public void RestoreState(object state)
        {
            SerializableVector3 position = (SerializableVector3)state;
            navMeshAgent.enabled = false;
            transform.position = position.ToVector();
            navMeshAgent.enabled = true;
            GetComponent<ActionScheduler>().CancelCurrentAction();
        }

        public JToken CaptureAsJToken()
        {
            // Vector3 is not implicitly serializable due to the way serializers handle properties.  If you try to
            // serialize a Vector3, you will get an error which is caused by the serializer getting caught in a 
            // recursive cycle.  While there is a library you can add to help Newtonsoft Json convert Vector3 values, 
            // there is an issue building projects which sometimes causes these library values to NOT be linked
            // into the project.  
            // Using static extension method ToToken() to automatically turn the Vector3 into a JObject suitable for Json serialization.
            return transform.position.ToToken();
        }

        public void RestoreFromJToken(JToken state)
        {
            // Using static extension method ToVector3() to automatically convert the state into a Vector3
            transform.position = state.ToVector3();
        }
    #endregion        
    }
}

By using regions, you keep in the habit of keeping your scripts organized.

Thanks for the insight!

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

Privacy & Terms