Форум программистов, компьютерный форум, киберфорум
Unity, Unity3D
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.67/15: Рейтинг темы: голосов - 15, средняя оценка - 4.67
2 / 1 / 0
Регистрация: 31.05.2021
Сообщений: 48
1

Оптимизация движения большого количества объектов

24.03.2023, 00:09. Показов 3100. Ответов 52

Author24 — интернет-сервис помощи студентам
Я занимаюсь разработкой игры, в которую хочу добавить что-то по типу конвейеров, которые в моей игре являются трубами. На данный момент я занимаюсь кодом который реализует передвижение ресурсов по трубам. И здесь я столкнулся с проблемой, которую уже долгое время не могу решить и это проблема оптимизации и производительности этих самых объектов. Меня не сильно волнует рендеринг объектов так как в будущем я планирую всё это перенести в DrawMeshes. Но даже так я понятия не имею как оптимизировать поиск пути и движения этих объектов на данный момент максимум сколько я могу двигать одновременно объектов это 5000 ресурсов для 60+ фпс, который скачет от 60 до 100. Я хочу услышать что-то кроме "кешируй объекты, не используй Linq, не используй .Find" и тд. вещи потому что это даёт прирост спору нет. Но у меня уже руки опускаются потому что не знаю что делать, я считаю что выжал все фпс из своего кода настолько, насколько мог сам.
Буду очень благодарен за вашу помощь, спрашивайте, если нужна какая-то дополнительная информация, свой код я прикрепил ниже.

Вот мой код на данный момент:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;
using UnityEngine.Profiling;
using Cysharp.Threading.Tasks;
 
public class ResourceManager : MonoBehaviour
{
    [SerializeField] private Pipes pipes;
    [SerializeField] private Buildings buildings;
    private TileBase[] directionTile;
    private TileBase[][] turnPipes = new TileBase[4][];
    private TileBase[] teePipes = new TileBase[4];
    private TileBase crossPipe;
    private Transform[] neighborPipes = new Transform[4];
    public Dictionary<int, Transform> _resourcesDict = new Dictionary<int, Transform>();
    public Dictionary<int, Resource> _resourcesComponentDict = new Dictionary<int, Resource>();
 
    public int resourceDictCount = 0;
    private Vector3Int[] offsets = new Vector3Int[] {
        new Vector3Int(0, 1, 0),
        new Vector3Int(1, 0, 0),
        new Vector3Int(0, -1, 0),
        new Vector3Int(-1, 0, 0)
    };
    private CancellationTokenSource cancellationTokenSource;
    private async void Awake()
    {
        directionTile = pipes.DirectionTile;
        turnPipes[0] = pipes.TurnPipesUp;
        turnPipes[1] = pipes.TurnPipesRight;
        turnPipes[2] = pipes.TurnPipesDown;
        turnPipes[3] = pipes.TurnPipesLeft;
        teePipes = pipes.TeePipes;
        crossPipe = pipes.CrossPipe;
        cancellationTokenSource = new CancellationTokenSource();
        await ResourceTask(cancellationTokenSource.Token);
    }
 
    private async UniTask ResourceTask(CancellationToken cancellationToken)
    {
        while (true)
        {
            int objectsPerFrame = Mathf.CeilToInt((resourceDictCount + 50) / 5);
            int objectsProcessed = 0;
            for (int key = 0; key < resourceDictCount; key++)
            {
                if (objectsProcessed % objectsPerFrame == 0) await UniTask.Yield();
                if (_resourcesDict.TryGetValue(key, out Transform transform) && _resourcesComponentDict.TryGetValue(key, out Resource resource))
                {
                    if (pipes._pipeGroupDict.TryGetValue(Vector3Int.FloorToInt(resource.pos), out Pipes.PipeObj pipeByPos))
                    {
                        Vector3Int cellPosition = Vector3Int.FloorToInt(resource.pos);
                        for (int i = 0; i < 4; i++)
                        {
                            if (pipes._pipeGroupDict.TryGetValue(cellPosition + offsets[i], out Pipes.PipeObj neiPipe)) neighborPipes[i] = neiPipe.transform;
                            else neighborPipes[i] = null;
                        }
                        if (resource.lastPipe != pipeByPos.transform)
                        {
                            resource.currentPipe = pipeByPos.Pipe;
                            resource.neighboringPipes = neighborPipes;
                        }
                        resource.lastPipe = pipeByPos.transform;
                        if (resource.currentPipe != null)
                        {
                            if (resource.startPipe == null)
                            {
                                resource.startPipe = resource.currentPipe;
                                resource.isStart = resource.currentPipe == resource.startPipe && Vector3.Distance(resource.pos, resource.currentPipe.transform.position) > 0.1f;
                            }
                            int neighbors = 0;
                            for (int i = 0; i < 4; i++)
                            {
                                if (resource.neighboringPipes[i] || ((i + 2) % 4 == resource.lastDirection)) neighbors++;
                                else if (buildings._lineGroupDict.ContainsKey(cellPosition + offsets[i])) neighbors++;
                            }
                            if (Vector3.Distance(resource.pos, resource.targetPosition) < 0.01f || resource.targetPosition == Vector3.zero)
                            {
                                if (Enumerable.ReferenceEquals(resource.neighboringPipes, neighborPipes) == false)
                                {
                                    resource.currentPipe = pipeByPos.Pipe;
                                    resource.neighboringPipes = neighborPipes;
                                }
                                if (resource.isStart)
                                {
                                    resource.isStart = resource.currentPipe == resource.startPipe && Vector3.Distance(resource.pos, resource.currentPipe.transform.position) > 0.01f;
                                    resource.targetPosition = resource.currentPipe.transform.position;
                                }
                                else if (neighbors == 2) TGP_DirectionAndTurn(resource);
                                else if (neighbors == 3) TGP_Tee(resource);
                                else if (neighbors == 4 && resource.currentPipe.currentTile == crossPipe) TGP_Cross(resource);
                            }
                            if (Vector3.Distance(resource.pos, resource.targetPosition) > 0.01f || neighbors != 1 && resource.neighboringPipes[resource.lastDirection]) MoveToCenter(resource.targetPosition, transform);
                        }
                    }
                    else resource.currentPipe = null;
                    objectsProcessed++;
                }
            }
            await UniTask.Yield();
        }
    }
    private void TGP_DirectionAndTurn(Resource r)
    {
        for (int i = 0; i < directionTile.Length; i++)
        {
            if (r.currentPipe.currentTile == directionTile[i] && r.neighboringPipes[i])
            {
                r.targetPosition = r.neighboringPipes[i].transform.position;
                r.lastDirection = i;
                break;
            }
            else if (i == 3 && r.currentPipe.currentTile != directionTile[i])
            {
                for (int j = 0; j < turnPipes[r.lastDirection].Length; j++)
                {
                    int directionIndex = r.lastDirection % 2 == 0 ? 3 : 2;
                    int index = directionIndex / (directionIndex * (j % 2) + (j % 2 == 0 ? 1 : 0));
                    if (r.currentPipe.currentTile == turnPipes[r.lastDirection][j] && r.neighboringPipes[directionIndex == 2 && index == 1 ? 0 : index])
                    {
                        r.targetPosition = r.neighboringPipes[directionIndex == 2 && index == 1 ? 0 : index].transform.position;
                        r.lastDirection = directionIndex == 2 && index == 1 ? 0 : index;
                    }
                }
            }
        }
    }
 
    private void TGP_Tee(Resource r)
    {
        int[] indices = new int[3];
        var pipesOutput = r.currentPipe.PipesOutput;
        for (int i = 0; i < 4; i++)
        {
            if (r.currentPipe.currentTile == teePipes[i])
            {
                int index = 0;
                int direction = (r.lastDirection + 2) % 4;
                for (int j = 0; j < 4; j++) if (j != direction && r.currentPipe.IsNeighbor[j] && index < 2) indices[index++] = j;
                if (pipesOutput[indices[0]][indices[0]] || r.neighboringPipes[indices[0]] == false)
                {
                    if (pipesOutput[indices[1]][indices[1]] || r.neighboringPipes[indices[1]] == false)
                    {
                        pipesOutput[indices[0]][indices[0]] = false;
                        pipesOutput[indices[1]][indices[1]] = false;
                    }
                }
                int indexCached = 0;
                for (int j = 0; j < 2; j++)
                {
                    indexCached = indices[j];
                    if (pipesOutput[indexCached][indexCached] == false && r.neighboringPipes[indexCached])
                    {
                        r.targetPosition = r.neighboringPipes[indexCached].transform.position;
                        r.lastDirection = indexCached;
                        pipesOutput[indexCached][indexCached] = true;
                        break;
                    }
                }
            }
        }
    }
 
    private void TGP_Cross(Resource r)
    {
        int number = 0;
        int[] indices = new int[3];
        int direction = (r.lastDirection + 2) % 4;
        for (int j = 0; j < 4; j++) if (j != direction) indices[number++] = j;
        var pipesOutput = r.currentPipe.PipesOutput;
        int count = 0;
        int index = 0;
        for (int i = 0; i < 3; i++)
        {
            index = indices[i];
            if (pipesOutput[r.lastDirection][index] || r.neighboringPipes[index] == false)
            {
                count++;
                if (count == 3) for (int j = 0; j < 4; j++) pipesOutput[r.lastDirection][j] = false;
            }
        }
        for (int i = 0; i < 3; i++)
        {
            index = indices[i];
            if (pipesOutput[r.lastDirection][index] == false && r.neighboringPipes[index])
            {
                r.targetPosition = r.neighboringPipes[index].transform.localPosition;
                pipesOutput[r.lastDirection][index] = true;
                r.lastDirection = index;
                break;
            }
        }
    }
 
    private void MoveToCenter(Vector3 targetPosition, Transform transform)
    {
        if (targetPosition != Vector3.zero)
        {
            transform.localPosition = Vector3.MoveTowards(transform.localPosition, targetPosition, 5 * Time.deltaTime);
        }
    }
}
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
24.03.2023, 00:09
Ответы с готовыми решениями:

Оптимизация обработки большого количества объектов
Доброго времени суток, господа. У меня есть графическая программа на основе SFML. В программе...

Оптимизация хранения большого количества объектов в Dictionary
Добрый день. Имеется некий класс Quest и список public static Dictionary&lt;Subject, Dictionary&lt;byte,...

Оптимизация большого количества if else
В приере только 3 в игре будет 26. и как видите (p1=0 и p2=1) тоже самое что (p1=1 и p2=0)...

Оптимизация большого количества NPC на карте
Пытаюсь сделать Терарию на C#(без Unity и т.д).В этой игре есть довольно большая карта, на которой...

Оптимизация большого количества пользовательских элементов управления
Задача такова: необходимо создать элемент управления с предложением в котором выделяется каждое...

52
2 / 1 / 0
Регистрация: 31.05.2021
Сообщений: 48
28.03.2023, 02:47  [ТС] 41
Author24 — интернет-сервис помощи студентам
Я так понимаю ты имеешь в виду заменить bool на int? Просто в любом случае массив для хранения нужен или я что-то недопонимаю?
Или ты имеешь в виду что индекс значит то в какую ты сторону должен идти? Просто я уже особо не помню почему так но если я делал 1 массив для всех сторон, то-есть в этом случае возьмём 1 индекс, то оно работало не правильно, из за этого я сделал двумерный, в этом случае 4 индекса по индексу для каждой стороны. Завтра проверю, возможно я протупил сильно. Да и в общем то это не самая большая проблема из всех причин низкой производительности. Словарь использую потому что так как массив не подходит из за того что нету границы в количестве ресурсов, остаётся лист и словарь. Я подумал словарь будет удобнее, хотя не думаю что есть особая разница, Искал в интернете вроде бы словари быстрее чем листы. Могу также завтра проверить что будет быстрее.
0
2 / 1 / 0
Регистрация: 31.05.2021
Сообщений: 48
28.03.2023, 21:09  [ТС] 42
1max1
Цитата Сообщение от Irohh Посмотреть сообщение
Завтра проверю, возможно я протупил сильно.
Касательно индекса, я так и не смог нормально его добавить, то-есть самую обычную функцию он исполняет, но если что-то меняется то он уже не работает и например сейчас я не могу решить проблему с тем что если например 1 ресурс пошёл вправо, значит следующий должен идти влево (даже при том что направление левого пути направлено вправо) , но слева уже занята труба ресурсом который как раз таки шёл вправо, поэтому они стоят на месте блокирую друг друга. Я знаю как это обойти но, мне хочется придумать более хороший способ, а не убогий иф размером в 3 строки.
Цитата Сообщение от Irohh Посмотреть сообщение
Могу также завтра проверить что будет быстрее.
Проверил что быстрее, особой разницы нету. Они примерно одинаковы, с учётом того что лист сильнее нагружает при большем количестве вызовов.
Код теста:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
        for (int i = 0; i < 100000; i++)
        {
            Profiler.BeginSample("Pipe.Dictionare");
            if (_resourcesDict.TryGetValue(0, out Transform test) && _resourcesComponentDict.TryGetValue(0, out Resource testRes))
            {
                Transform transform1 = test;
                Resource resource1 = testRes;
            }
            Profiler.EndSample();
            Profiler.BeginSample("Pipe.List");
            if (_resourcesList[0] && _resourcesComponentList[0])
            {
                Transform transform2 = _resourcesList[0];
                Resource resource2 = _resourcesComponentList[0];
            }
            Profiler.EndSample();
        }
Результаты прикрепил.
Миниатюры
Оптимизация движения большого количества объектов  
0
3358 / 1772 / 1027
Регистрация: 26.10.2018
Сообщений: 5,199
28.03.2023, 21:26 43
Делай как считаешь нужным, я не смогу тебе помочь)
0
2638 / 1566 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
29.03.2023, 06:33 44
Irohh, если вы планируете запускать тысячи объектов в трубах, то разве есть надобность дополнительно указывать направление для объекта в местах разветвления труб? Даже если объекты будут рандомно поворачивать в любую сторону на разветвлениях, то при потоке в тысячи объектов всё равно не будет заметно что там ещё что-то регулируется, ведь это будет практически сплошной поток, в котором и отдельный объект будет сложно разглядеть.
Либо я не совсем понимаю идею.

Вообще мне кажется в приоритете игры должен быть интерес геймплея, а не кол-во объектов. Вряд ли игрок будет в восторге, увидев миллион бегающих шариков, но при этом не понимающий что с ними делать и зачем вообще нужна вся эта вселенная движений.

Вот эта тема уже так разрослась, но лично я так и не понял до сих пор - что представляет из себя игра. Вроде бы конвейеры не самые главные в ней, но почему-то только о них и речь. Плюс выясняется что объект не должен поворачивать в трубу, которая уже занята ресурсом.. А куда деваться, если объектов будет тысячи, труб на каждый не хватит.
0
2 / 1 / 0
Регистрация: 31.05.2021
Сообщений: 48
31.03.2023, 20:26  [ТС] 45
samana, извиняюсь что долго не отвечал. Был занят изменением как раз поиском пути и у меня даже получилось улучшить алгоритм поиска пути, я немного ещё изменял его так что думаю производительность немного просела но в общем на скриншоте что я прикрепил видно что скорость поиска пути выросла в раз 7-8 и я не сказал бы что сильно, но доволен результатом (такие большие мс потому что я запускал поиск 100 тысяч раз).
Цитата Сообщение от samana Посмотреть сообщение
если объекты будут рандомно поворачивать в любую сторону
По поводу этого скажу что это сделано что бы можно было игроку надо поровну распределить все ресурсы на любое количество ответвлений в каких-то целях.
Цитата Сообщение от samana Посмотреть сообщение
Вот эта тема уже так разрослась, но лично я так и не понял до сих пор - что представляет из себя игра.
Изначально это был просто пробный проект, где я пытался сделать клон такой игры как Vectorio но со временем как-то всё начало разрастаться и это превратилось более в убогий и дешёвый клон таких игр как Factorio и Mindustry. Все 3 игры перечисленные игры мне очень нравятся и я хотел сделать что-то похожее на них. Я не думаю что моя игра вообще когда выйдет, так как сейчас у меня каникулы я собирался доделать всё связанное с трубами, но у меня осталось 2 дня до их конца, а трубы готовы на 50-60%. Представляйте мою игру как бесплатный клон Factorio который не смог.
Цитата Сообщение от samana Посмотреть сообщение
А куда деваться, если объектов будет тысячи, труб на каждый не хватит.
Ну по идеи, сначала ресурс добывается --> во что-то превращается (руда --> слиток) --> то что получилось идёт или в хранилище или перерабатывается ещё в что-то (всё что находится в хранилище можно продавать и полученные деньги использовать для постройки или прокачки чего-то). Но на данный момент у меня настолько всё сырое, потому что я всё время что добавляю потому убираю и переделываю. Честно говоря я даже не представляю что может из этого получится. Мне нужно времени и много, я просто буду стараться доделать это что бы хотя бы получить приятные слова от друзей.
Миниатюры
Оптимизация движения большого количества объектов  
0
2638 / 1566 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
31.03.2023, 21:19 46
Цитата Сообщение от Irohh Посмотреть сообщение
извиняюсь что долго не отвечал.
Всё нормально, это просто форум, здесь пишут тогда, когда хотят или могут.
0
0 / 0 / 0
Регистрация: 01.02.2019
Сообщений: 3
15.04.2023, 10:40 47
Вам необходимо использовать DOTS Entities, для ваших целей он подойдёт идеально. Используя эту архитектуру, можно двигать сотни тысяч объектов в сцене одновременно. В факторио скорее всего он используется, и в Vampire Survivors, например, тоже. Придётся много вещей изучить, но в программировании всегда так. Сложнее всего будет переключиться на логику Entities, так как реализация очень сильно отличается от обычного процедурного программирования и ООП. Жаль что юнити так и не доделали до конца свою реализацию Entities, но то что есть худо бедно работает.
0
2 / 1 / 0
Регистрация: 31.05.2021
Сообщений: 48
15.04.2023, 18:44  [ТС] 48
Mopnex777, если я правильно понимаю это помогает в случае с объектами на сцене. Но сейчас я полностью отказался от таких объектов и у меня есть только 1 объект менеджер который хранит в себе словарь ресурсов и массив матриц их положений, после рисуя их через DrawMeshInstanced. В таком случае есть ли смысл от ECS?
0
3358 / 1772 / 1027
Регистрация: 26.10.2018
Сообщений: 5,199
15.04.2023, 20:53 49
Цитата Сообщение от Irohh Посмотреть сообщение
если я правильно понимаю это помогает в случае с объектами на сцене
По факту это не имеет отношения к юнити, сама архитектура на ecs это просто принцип построения кода, просто у юнити есть пакет на эту тему.
0
0 / 0 / 0
Регистрация: 01.02.2019
Сообщений: 3
17.04.2023, 13:58 50
Цитата Сообщение от Irohh Посмотреть сообщение
В таком случае есть ли смысл от ECS?
Сам я касался этой темы поверхностно, но под задачу двигать тысячи объектов одновременно, ECS отлично подходит. Правда с ходу перейти на эту архитектуру будет непросто, необходимо будет немного перестроить своё мышление, а для того, чтобы оно всё адекватно работало, необходимо будет использовать только определенные типы и специальные оптимизации.
0
0 / 0 / 0
Регистрация: 01.02.2019
Сообщений: 3
21.04.2023, 22:56 51
Если ещё актуально, случайно наткнулся на такое видео:

0
2638 / 1566 / 853
Регистрация: 23.02.2019
Сообщений: 3,876
22.04.2023, 13:21 52
Mopnex777,

Не по теме:

Цитата Сообщение от Mopnex777 Посмотреть сообщение
Если ещё актуально, случайно наткнулся на такое видео:
Уже было на второй странице поста

1
2 / 1 / 0
Регистрация: 31.05.2021
Сообщений: 48
30.04.2023, 02:47  [ТС] 53
1max1, в любом случае не особо до конца понимаю откуда мне взять дополнительную производительность в спомощью ecs. Разве строение кода увеличивает скорость его исполнения? Возможно я ошибаюсь в своих мыслях по этому поводу.
Mopnex777, я уже давно не смотрел на тот код и как помню там всё уже было довольно сильно сжато и упрощено. Когда будет время поищу хорошие примеры работы ecs, так как на данный момент видел 2-3 маленьких примера и демку юнити. Мне кажется единственный способ который может мне помочь это банальное развитие как программиста до более высокого уровня с большими знаниями, когда я буду понимать то что происходит в том видео эмеральда как минимум. Спасибо за помощь!
0
30.04.2023, 02:47
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
30.04.2023, 02:47
Помогаю со студенческими работами здесь

Отрисовка большого количества объектов
Привет, у меня проблема: при отрисовке 3000 вершин телефон уже начинает тормозить(. Вопрос: на что...

Оптимизация вывода большого (более 7000) количества записей на странице
Здравствуйте. Прошу совета, как сделать более оптимальным решение. Имеется справочник КБК....

Проверка столкновения большого количества объектов
...и всё же. Вот на любом сайте, когда описывают и как сделать столкновение объектов, то приводят...

Создание большого количества GUI-объектов
Есть QScrollArea, которая содержит очень большое количество наследников QWidget. В процессе работы...

Вывод большого количества объектов в OpenGL
Я последнее время увлекся всякими экспериментами в 3д и использую для этого opengl. Чаще всего в...

Обработка большого количества объектов на листе
Здравствуйте, уважаемые форумчане. Помогите, пожалуйста советом. Исходные данные: На листе...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
53
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru