Как улучшить и оптимизировать скрипт движения модели в Unity? (за полезный совет можно договориться о донате)

117
21 ноября 2021, 10:00

Как мне улучшить и оптимизировать мой скрипт движения моделей? В модуле движения у меня есть путевые точки, которые я задаю когда начинаю делать сцену: Сам скрипт splineMove получился громоздким. У меня сцены разбиты на 4 типа:

1 Distribution.Когда есть в начале путевые точки(M-113 (0)cloneMiniPath), общие путевые точки(GroupPath) и конечные путевые точки(M-113 (0)_ENDcloneMiniPath).

2 Shooting. Когда модели не двигаются, а просто стоят на сцене и ведут стрельбу.

3 ShellingMovement. Когда их обстре́ливают во время движения.

4 DistributionOne. Когда есть один общий путь(замкнуты или не замкнуты) в конце которого движение начинается в начале первой путевой точке.

Массив путевых точек - public PathManager pathContainer;

Текущая путевая точка, на данный момент времени - public int currentPoint;

Объект в группе относительно, которого считается скорость движения позади идущей техники public GameObject LeaderGObjInstance = null;.

if (this.LeaderGObjInstance != null && this.LeaderGObjInstance.gameObject.name != transform.name)
                {
                    float DistanceObj = Vector3.Distance(transform.position, GameObject.Find(this.LeaderGObjInstance.gameObject.name).transform.position);
                    System.Random r = new System.Random();
                    var space = r.Next(25, 30);
                    //if(SceneName == "DistributionThree"){space = r.Next (40, 50);}else{space = r.Next (25, 30);}
                    if (DistanceObj > (float)space)
                    {
                        speed = speed + 0.1f;
                        if (speed > 6)
                        {
                            speed = 6;
                        }
                    }
                    else if (DistanceObj <= (float)space)
                    {
                        speed = speed - 0.3f;
                        if (speed <= 0)
                        {
                            speed = 0;
                        }
                    }
                }

Инициализация, Start, Update:

...
    public class splineMove : MonoBehaviour
    {
        // Массив путевых точек
        public PathManager pathContainer;
        public Vector3 offset;
        public bool onStart = true;
        public float speed = 5f;
        // Текущая путевая точка, на данный момент времени
        [HideInInspector]
        public int currentPoint = 0;
        [HideInInspector]
        public float force = 5;
        [HideInInspector]
        public Rigidbody rb = null;
        [HideInInspector]
        public float dis_changewaypoint = 5F;
        private Vector3 current_waypoint;
        [HideInInspector]
        public float turn_speed = 1F;
        [SerializeField]
        public float ddelta = 0.0f;
        GameObject GOSelect = null;
        // Объект в группе относительно, которого считается скорость движения позади идущей техники
        [SerializeField]
        public GameObject LeaderGObjInstance = null;
        public bool stopModelBool = false;
        public bool DistributionBool = false;
        // float time = 0;
        int indexDamegedModel = 1;
        bool boolDetour = false;
        bool boolDetourLeftRight = false;
        List<Vector3> tempDetourTrapeze = new List<Vector3>();
        int tempCountDetourTrapeze = 0;

        GameObject GamObjList = null;
        GameObject CameraGamObj = null;
        RTCTankController tankController = null;
        RTCTankGunController gunController = null;
        string SceneName;
        public enum SceneType
        {
            Distribution,
            Shooting,
            ShellingMovement,
            DistributionOne
        }
        public SceneType sceneType = SceneType.Distribution;
        public int startTimeInterval = 10;
        public Vector3 targetDefeat;
        public GameObject targetRotationCameraGameObj;
        [Header("Sensors")]
        public float sensorLength = 20f;
        public Vector3 frontSensorPosition = new Vector3(0f, 3f, 0f);
        public float frontSideSensorPossition = 2f;
        public float frontSensorAngle = 30f;
        int ExplCount = 0;
        void Awake()
        {
            if (transform.name != "Vehicles")
            {
                initFindAllObj();
                if (sceneType == SceneType.Distribution)
                {

                    onStart = false;
                    stopModelBool = false;
                    SavingGroupOFObjectsManager SGObjManager = GamObjList.GetComponent<SavingGroupOFObjectsManager>() as SavingGroupOFObjectsManager;
                    GameObject[] LOrder = SGObjManager.ListOrder;
                    for (int i = 0; i < LOrder.Length; i++)
                    {
                        if (LOrder[i].name == transform.name)
                        {
                            Invoke("BoolOnStart", (float)i * startTimeInterval);
                            // BoolOnStart();
                            break;
                        }
                    }
                    Leader.LeaderGObj = new GameObject();
                    Leader.index = 0;
                    Leader.LeaderGObj.name = "empty";

                }
                else if (sceneType == SceneType.ShellingMovement)
                {

                    if (sceneType == SceneType.ShellingMovement)
                    {
                        if (transform.name == "Camera")
                        {
                            Invoke("doWorkInvokeTwo", 56f);
                            Invoke("DefeatList", 54f);
                            Invoke("doWorkInvokeTwoCancel", 90f);
                        }
                    }
                    else if (SceneName == "ShellingMovementTwo")
                    {
                        doWorkInvokeTwo();
                        Invoke("Defeat", 10f);
                    }
                    else if (SceneName == "ShellingMovementThree")
                    {
                        doWorkInvokeTwo();
                        Invoke("Defeat", 5f);
                        Invoke("Defeat", 40f);
                        Invoke("Defeat", 55f);
                    }
                    Leader.LeaderGObj = new GameObject();
                    Leader.index = 0;
                    Leader.LeaderGObj.name = "empty";

                }
                else if (sceneType == SceneType.Shooting)
                {
                    if (transform.name != "Camera")
                    {
                        onStart = false;
                    }

                }
                currentPoint = 0;
                if (pathContainer)
                {
                    transform.position = pathContainer.waypoints[currentPoint].position;
                    current_waypoint = pathContainer.waypoints[currentPoint].position;
                    Vector3 relativePos = current_waypoint - transform.position;
                    Face_waypoint(relativePos);
                }
            }
            ExplCount = 0;
        }
        void initFindAllObj()
        {
            if (transform.name != "Camera")
            {
                Transform Gun;
                if (Gun = transform.Find("MainGunGroup"))
                {
                    gunController = Gun.GetComponentInChildren<RTCTankGunController>();
                }
            }
            GamObjList = GameObject.Find("Vehicles");
            CameraGamObj = GameObject.Find("Camera");
            SceneName = SceneManager.GetActiveScene().name;
            tankController = transform.GetComponent<RTCTankController>();
            rb = GetComponent<Rigidbody>();
        }
        void BoolOnStart()
        {
            if (transform.name != "Vehicles")
            {
                onStart = true;
                // onStart = (onStart==true) ? false : true;
            }
            // if(SceneName=="ShellingMovement") {
            //  CameraGamObj.GetComponent<RTCCamera> ().enabled = true;
            //  GOSelect = null;
            // }
        }
        void Update()
        {
            if (transform.name != "Vehicles")
            {
                if (onStart)
                {
                    EnemyMovement();
                }
                if (sceneType == SceneType.ShellingMovement)
                {
                    CameraActivation();
                    if (gunController != null) { gunController.toOriginalRotationGun(); }
                }
                else if (sceneType == SceneType.Shooting)
                {
                    ShootingTarget(targetDefeat);
                    if (SceneName == "ShootingDefeat")
                    {
                        //if(transform.name=="Camera") {
                        CameraActivation();
                        //}
                    }
                }
                else
                {
                    if (gunController != null) { gunController.toOriginalRotationGun(); }
                }
            }
        }

Сам код движения моделей по путевым точкам:

void EnemyMovement()
{
    if (pathContainer != null)
    {
        if (transform.name != "Camera")
        {
            if (this.LeaderGObjInstance != null && this.LeaderGObjInstance.gameObject.name != transform.name)
            {
                float DistanceObj = Vector3.Distance(transform.position, GameObject.Find(this.LeaderGObjInstance.gameObject.name).transform.position);
                System.Random r = new System.Random();
                var space = r.Next(25, 30);
                //if(SceneName == "DistributionThree"){space = r.Next (40, 50);}else{space = r.Next (25, 30);}
                if (DistanceObj > (float)space)
                {
                    speed = speed + 0.1f;
                    if (speed > 6)
                    {
                        speed = 6;
                    }
                }
                else if (DistanceObj <= (float)space)
                {
                    speed = speed - 0.3f;
                    if (speed <= 0)
                    {
                        speed = 0;
                    }
                }
            }
            if (tankController)
            {
                tankController.Inputs((float)speed * 1 / 30);
            }
        }
        float range = speed * Time.deltaTime;
        Vector3 relativePos = current_waypoint - transform.position;
        Waypoint_selecetion_code(relativePos);
        current_waypoint = pathContainer.waypoints[currentPoint].position;
        if (sceneType == SceneType.DistributionOne)
        {
            RotationCamera();
            //transform.position = Vector3.MoveTowards(transform.position, pathContainer.waypoints[currentPoint].position, range - ddelta);
        }
        else if (sceneType == SceneType.ShellingMovement)
        {
            if (transform.name == "Camera")
            {
                transform.LookAt(LeaderGObjInstance.transform);
            }
            else
            {
                Face_waypoint(relativePos);
            }
        }
        else
        {
            Face_waypoint(relativePos);
            // transform.position = Vector3.MoveTowards(transform.position, pathContainer.waypoints[currentPoint].position, range - ddelta);
            // Moveobject();
        }
        transform.position = Vector3.MoveTowards(transform.position, pathContainer.waypoints[currentPoint].position, range - ddelta);
        Moveobject();
    }
}

Метод выбора следующей путевой точки и метод старта, остановки движения:

void Waypoint_selecetion_code(Vector3 relativePos)
{
    if (Mathf.Abs(relativePos.x) < dis_changewaypoint && Mathf.Abs(relativePos.z) < dis_changewaypoint)
    {
        currentPoint++;
        if (currentPoint < pathContainer.waypoints.Length)//waypoints.Count shows number of elements in list
        {
            current_waypoint = pathContainer.waypoints[currentPoint].position;
        }
        else
        {
            if (sceneType == SceneType.Shooting && transform.name == "Camera")
            {
                onStart = true;
                currentPoint = 0;
                current_waypoint = pathContainer.waypoints[currentPoint].position;
            }
            else if (sceneType == SceneType.Distribution && DistributionBool == false)
            {
                SavingGroupOFObjectsManager ParentGamObj = transform.gameObject.GetComponentInParent<SavingGroupOFObjectsManager>();
                transform.gameObject.GetComponent<splineMove>().pathContainer = ParentGamObj.pathCont;
                if (transform.name != "Camera")
                {
                    if (Leader.LeaderGObj.name == "empty")
                    {
                        Leader.LeaderGObj = transform.gameObject;
                        this.LeaderGObjInstance = Leader.LeaderGObj;
                    }
                    else
                    {
                        this.LeaderGObjInstance = Leader.LeaderGObj;
                        Leader.LeaderGObj = transform.gameObject;
                    }
                }
                DistributionBool = true;
                currentPoint = 0;
                current_waypoint = pathContainer.waypoints[currentPoint].position;
                onStart = true;
            }
            else if (sceneType == SceneType.Distribution)
            {
                if (stopModelBool)
                {
                    currentPoint = pathContainer.waypoints.Length - 1;
                    Invoke("BoolOnStartfalse", 1f);
                }
                else
                {
                    stopModelBool = true;
                    PathSelect();
                    current_waypoint = pathContainer.waypoints[currentPoint].position;
                    onStart = true;
                    this.LeaderGObjInstance = null;
                }
            }
            else if (sceneType == SceneType.ShellingMovement)
            {
                currentPoint--;
                onStart = false;
                //speed = 0f;
            }
            else
            {
                currentPoint = 0;
                current_waypoint = pathContainer.waypoints[currentPoint].position;
                onStart = true;
            }
        }
    }
}
void BoolOnStartfalse()
{
    onStart = false;
    speed = 0f;
    // GetComponent<Rigidbody> ().useGravity = false;
}
public void PathSelect()
{
    PathManager[] AllPath = GameObject.Find("AllPathManager").GetComponentsInChildren<PathManager>();
    //Regex PathRegex = new Regex(@""+transform.gameObject.name+"");
    for (int i = 0; i < AllPath.Length; i++)
    {
        string pattern = AllPath[i].name;
        if (pattern.Contains(transform.gameObject.name + "_END"))
        {
            transform.gameObject.GetComponent<splineMove>().pathContainer = AllPath[i];
            currentPoint = 0;
        }
    }
}

Метод поворота в сторону движения и физика:

void Moveobject()
{
    if (rb != null)
    {
        if (Mathf.Abs(force) > Mathf.Abs(rb.velocity.z + rb.velocity.x))
        {
            rb.AddForce(transform.forward * force);
        }
    }
}
void Face_waypoint(Vector3 relativePos)
{
    Quaternion rotation = Quaternion.LookRotation(relativePos, Vector3.up);
    transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime * turn_speed);  // slerp ( from.rotation, to.rotation, speed)
}

В группе сейчас 17 объектов, но может быть и до 200 объектов. Я вешаю на каждую модель этот скрипт , а так же на камеру.

Answer 1

Из того, что лежит совсем на поверхности:

  1. Избавиться от вызовов GameObject.Find(). Мало того, что это дурной стиль в Unity, дак еще и дорогостоящая операция. Лучше делать ссылки в инспекторе напрямую. Это быстрее и эффективней.
  2. Избавиться от вызовов GetComponent(). Тоже самое, что и в предыдущем пункте.
  3. У вас в коде поведения очень много проверок на типов объектов по их имени. Это эквивалент сравнения строк. Если учесть, что у вас таких объектов много и каждый из них обновляется в Update(), я бы задумался о том, что это не лучшее решение. Например, вы можете задавать тип объекта (враг, друг, камера или еще какие типы) заведя для этих целей специальный Enum и поле у самого объекта, которое вы будете устанавливать в инспекторе. Такое сравнение в коде будет эквивалентом сравнения 2 int значений, которые вам не придется ни откуда получать (так как установите их в инспекторе). Кроме того, вы еще и избавитесь от постоянных вызовов transform.name. А это тоже небольшой бонус, учитывая, что вы будете использовать закэшированные значения.
  4. Если количество юнитов, которые вы двигаете с помощью этого скрипта большое (думаю, что даже для 30-50+ уже ощутимо), имеет смысл минимизировать количество вызвов Update() за один кадр. Магические методы в юнити достаточно дорогие. Оптимизировать это достаточно легко - создайте отдельный класс-менеджер, который будет иметь ссылки на всех юнитов. Удалите метод Update() из кода самих юнитов и перенесите содержимое этого метода в какой-либо свой публичный метод. В самом классе-менеджере объявите метод Update() (который теперь будет 1 на всех) и в теле этого метода вызывайте напрямую новый публичный метод для перемещения юнитов.
  5. Если ваша игра про какую-нибудь тяжелую технику, которая передвигается достаточно медленно, вы можете не использовать Unity физику для перемещения объектов. Попробуйте реализовать тоже самое поведение через просчет координат и изменение transform.position. Если ваше движение сведется к 1 простой формуле, то это будет дешевле физики юнити и избавит вас от необходимости добавлять Rigitbody на каждый юнит.
  6. Попробуйте минимизировать количество объектов на сцене и количество компонентов на этих объектах. Используйте не MonoBehaviour классы. Каждый GameObject и компонент на нем априори более накладно, чем обычные C# классы. Такой совет однако даст наиболее видимый эффект, если у вас много объектов на сцене.
  7. Если не лень переписывать код практически с нуля, обратите внимание на ECS и DOTS в Unity. Такой подход оправдан при большом количестве объектов на сцене, каждый из которых регулярно обновляется, но при этом не производит каких-либо очень сложных расчетов. Однако, грамотное использование такого подхода может потребовать очень много времени.
  8. Достаточно мелочь, но при большом количестве объектов актуально:

       System.Random r = new System.Random();
       var space = r.Next(25, 30);
    

    Объект типа System.Random() можно создать только 1 раз, например при инициализации объекта, либо напрямую в свойстве или конструкторе. А каждый раз, когда надо получить новое число, просто вызывать r.Next(25, 30);

READ ALSO
Присвоить массиву GameObject[] какой-либо GameObject

Присвоить массиву GameObject[] какой-либо GameObject

Всем привет! У меня есть первый массив gameobject'ов (firstArr), где изначально лежат gameobject'ы с тегом Point и именами Point n (где n от 1 до 9)Мне нужно, чтобы...

180
Почему ищется не та таблица?

Почему ищется не та таблица?

Я начинающий в ASPNET Core MVC

103
Не удается подключиться к MySql server, используя EF core

Не удается подключиться к MySql server, используя EF core

Я пытаюсь подключиться к базе данных, используя Entity Framework, но получаю такую ошибку:

110
Инсталлер для программы

Инсталлер для программы

У меня есть форма написаная в visual studio на c#

196