RS Game Maker Community
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

 
Расширенный поиск
  Главная  Форум   Вики Блоги FAQ Игры Статьи Примеры Войти Регистрация  
Вики
Все желающие приглашаются редактировать вики. Можете писать о своих (или чужих) играх, размещать туториалы, постить статьи - главное навесить категорию.
Страниц: 1   Вниз
  Печать  
Азы оптимизации
0 Пользователей и 1 Гость смотрят эту тему.
CH@$ER
Новичок


Репутация: 8
Offline Offline

Сообщений: 10


16 Февраля 2008 в 09:45
  • Не используйте логический оператор "<>" (не равно). Вместо него можно использовать "!=".
  • Не обращайтесь к эдементам массива слишком часто (например, в каких либо вычислениях в событии шага).
  • Если есть возможность, то не используйте собственную отрисовку объектов (т.е. не нужно рисовать спрайты объектов в draw). GM работает быстрее без этого события.
  • Использовать функции вида collision_* нужно крайне осторожно. Они могут пригодится только когда нужно проверить столкновение объектов, не имеющих формы (маски). В противном случае будет достаточно instance_place.
  • Старайтесь избегать включения точной проверки столкновения у спрайтов (Precise Collision Checking), а также не используйте соответствующий флаг (prec) в функциях collision_*. В этом случае проверка столкновения будет проходить по прямоугольникам, но скорость намного выше. Часто люди просто забывают ее выключать, поэтому алгоритм работает впустую - проверяет весь спрайт, вместо того, чтобы проверить лишь пересечение прямоугольников.
  • Если вам нужно расчитать координату в заданном направлении (например, движения по кругу), то используйте lengthdir_x/y вместо синусов и косинусов. Они работают очень медленно.
  • Старайтесь избегать повторного и ненужного вычисления какой-либо переменной. Например, код:
x += lengthdir_x(point_direction(x, y, x1, y1), 10); 
y += lengthdir_y(point_direction(x, y, x1, y1), 10);
будет работать медленнее, чем:
var d;
d = point_direction(x, y, x1, y1);
x += lengthdir_x(d, 10);
y += lengthdir_y(d, 10);
  • Всегда, повторяю, ВСЕГДА заносите временные переменные в список var в начале скрипта! Иначе они останутся с объектом до конца игры, тем самым захламляя память.
  • Объеденяйте объекты с одинаковыми свойствами. Например, для стен можно сделать заготовки различных размеров - так можно сократить количество стен в комнате и существенно повысить производительность. Также можно деактивизировать эти объекты за пределами вида.
  • По возможности используйте деактивацию статических объектов (например, бочек или ящиков).
  • Игра работает медленнее при включеном режиме отладки.
  • Когда завершаете игру, не забывайте убирать все вызовы процедуры show_debug_message() - она заметно отбирает кадры в секунду.
  • Постарайтесь как можно меньше использовать вызовы собственных скриптов, особенно в частых вычислениях. Некоторые "товарищи" специально выносят код из объектов в скрипты. Это категорически противопоказано!
  • Отрисовать спрайт в виде звезды намного быстрее, чем сделать тоже самое с помощью примитивов.
  • Чем меньше текcта - тем лучше. Отрисовка текста - самая медленная из всего. Достаточно лишь представить, что каждый символ - это спрайт.
  • Удаляйте ненужные спрайты во время загрузки игры. Таковыми являются спрайты для объектов, которые в конечном итоге не будут видимыми - всякие контролееры, например.
  • Никогда не пытайтесь заменить стандартные функции своими. Они никогда не будут работать быстрее из-за того, что процедура вызова скриптов очень медленная.

Заблужденные мнения
  • Размер комнаты не влияет на размер полученного ехе-файла и не влияет на производительность. Здесь имеется ввиду изменение размера комнаты при фиксированном количестве объектов.
  • Нет никакой разници, будет ли в скрипте 100 пробелов или 1000. При загрузке игры скрипты компилируются в более оптимизированный вид. В нем нет ни комментариев, ни лишних пробелов - ничего. Поэтому рекомендуется писать код по правилам, а не в одну строчку.
  • Время загрузки игры не зависит от того, в игре ли ресурсы, или нет. Только, если, конечно, они грузятся все и сразу.
  • Верно, что собственная проверка столкновений в шаге работает быстрее, чем события столкновения, НО! Ваша проверка никогда не будет работать корректно, если в нужной области находится несколько объектов, с которыми возможно столкновение. А стандартная проверка проверяет все объекты на пересечение... собственно, отсюда и скорость ниже.

 
Silen
Старожил
******

Репутация: 277
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 482


Indie Game Developer

Ответ № 1 16 Февраля 2008 в 17:00
Хорошая статья.
Отрисовка текста - самая медленная из всего. Достаточно лишь представить, что каждый символ - это спрайт.
Пожалуй обычные виндовзские фонты скорее можно сравнить с отрисовкой примитивов. Что, впрочем, ничем не лучше )

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

Don't tell why, tell why not.
CH@$ER
Новичок


Репутация: 8
Offline Offline

Сообщений: 10


Ответ № 2 17 Февраля 2008 в 09:09
стандартные шрифты и правда, векторные, но не все, и причем:
1) они рисуются очень быстро
2) они рисуются один раз при изменении
не знаю на счет аларма, но на этот счет есть гораздо более действенный способ. когда выйду с компа расскажу

Lex
Частый посетитель
***

Репутация: 43
Offline Offline

Сообщений: 367


Ответ № 3 17 Февраля 2008 в 12:09
Вопрос про draw:
эффект достигается если события draw нет вообще, или просто надо свести к минимуму?
Silen
Старожил
******

Репутация: 277
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 482


Indie Game Developer

Ответ № 4 17 Февраля 2008 в 16:29
Кстати, ведь при отрисовки через draw в случае нахождения обьекта за пределами вида, он автоматически не отрисовывается и не нужно прибегать к дополнительной проверке его местоположения.

Don't tell why, tell why not.
CH@$ER
Новичок


Репутация: 8
Offline Offline

Сообщений: 10


Ответ № 5 17 Февраля 2008 в 21:51
там все гораздо сложнее... но вообще эффект достигается когда события рисования у объекта нет вообще.
то, что объект не рисуется за пределами экрана обрабатывает дикс, но ему тоже нужно помогать и не передавать лишние вершины для обработки. просто когда событие рисование присутствует, то гм тогда не сможет узнать формы выводимого объекта и следовательно не сможет узнать, находится ли этот объект в пределах экрана и передает эти лишние вершины на обработку. когда стоит стандартная отрисовка, то гм знает параметры текущего спрайта и может определить выводить ли его или нет

CH@$ER
Новичок


Репутация: 8
Offline Offline

Сообщений: 10


Ответ № 6 18 Февраля 2008 в 12:35
Вот что я хотел сказать (это я одному чуваку объяснял):
Сначала немного определений:
Кадр. Он проходит fps раз в секунду.
Шаг. Нам необходимо, чтобы он проходил только 30 раз в секунду и не больше.

Что такое fps? Это число, показывающее количество кадров, которое рисуется в секунду. Т.е. 1000 / fps - время в миллисекундах отрисовки одного кадра. Отсюда можно заметить, что чем больше fps, тем меньше занимает времени отрисовка.
Пусть нам нужна скорость игры 30 кадров. Тогда значит один шаг должен занимать 1000 / 30 = 33.3 миллисекунд. Что если fps = 60? Тогда каждый кадр рисуется за время 16.6. Ровно в два раза быстрее, чем нужно. А что, если fps = 120? Отрисовка идет уже в четыре раза быстрее, чем нужно. Т.е. в данном случае нам нужно пропустить 4 кадра, чтобы выполнить событие шага. Но такая дискретизация нам не нужна, ведь fps может менятся с каждым кадром. Решение очень простое: заводим переменную, которая указывает текущее время, прошедшее с предыдущего шага. Каждый новый шаг оно обнуляется, а каждый новый кадр к нему добавляется время, прошедшее с предыдущего кадра (1000 / fps). Если получившееся значение больше необходимого времени между шагами (33.3), то значит выполняем событие шага и обнуляем значение времени.
Но есть одна проблема. В GameMaker FPS обновляется раз в секунду. Из-за этого получаются огромные скачки при сильной смене текущего значения fps. Вместо 1000 / fps можно использовать current_time. Тогда длинна одного кадра будет разницой времени текущего и предыдущего. Но это значение также не очень часто обновляется. Тут уж ничем не поможешь, кроме как использования дополнительной библиотеки.

Код:
У нас будет главный объект, который за всем будет следить.
Событие Create:
global.step = false; //Будет указывать для всех объектов является ли текущий кадр шагом.
prev_time = current_time;
Alarm = 0; //текущее время между шагами

Событие Begin Step:
var time;

time = current_time;
Alarm += time - prev_time; //Увеличиваем значение времени на длинну кадра
prev_time = time;

global.step = false; //Говорим, что текущий кадр не событие шага
if Alarm > 1000 / 30 //Это сделано для наглядности, лучше заменить на 33.3
{
  global.step = true;
  Alarm = Alarm - 1000 / 30; //Если счетчик перескочил немного, то делаем следующий шаг короче
}

В других объектах событие Step, Begin Step, End step начало скрипта:
if !global.step exit;

//Дальше любой код

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

zagzi
Mr.Z from zagzonia
Активный участник
**

Репутация: 6
Offline Offline

Сообщений: 191


Ответ № 7 13 Сентября 2008 в 21:10
Постарайтесь как можно меньше использовать вызовы собственных скриптов, особенно в частых вычислениях. Некоторые "товарищи" специально выносят код из объектов в скрипты. Это категорически противопоказано!
тоесть получается что если я сделаю один скрипт к которому будут оброщатся 25 разных юнитов это будит работать хуже чем еслиб я в каждом юните написал этот скрипт?  :(
(или как всегда я не чё не понял)

Репрессируем демократов, сломаем механизм капитализма!
Silen
Старожил
******

Репутация: 277
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 482


Indie Game Developer

Ответ № 8 13 Сентября 2008 в 21:22
Да. Не нужно бояться скриптов, а просто использовать их целесообразно и не выносить в них всё подряд.

Don't tell why, tell why not.
zagzi
Mr.Z from zagzonia
Активный участник
**

Репутация: 6
Offline Offline

Сообщений: 191


Ответ № 9 13 Сентября 2008 в 21:45
но разве не лутьше будет если зделать один скрипт для всех даже если он будит нечтожным?

Репрессируем демократов, сломаем механизм капитализма!
Lex
Частый посетитель
***

Репутация: 43
Offline Offline

Сообщений: 367


Ответ № 10 13 Сентября 2008 в 22:53
лучше, но медленнее.
Silen
Старожил
******

Репутация: 277
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 482


Indie Game Developer

Ответ № 11 13 Сентября 2008 в 23:01
Для удобства программиста лучше, для производительности хуже. При обращение к скриптам расходуются ресурсы процессора + память на создание временных переменных для аргументов. Производительность при этом падает в 3 раза. Сам бы уже давно тест провёл.

Don't tell why, tell why not.
Lex
Частый посетитель
***

Репутация: 43
Offline Offline

Сообщений: 367


Ответ № 12 13 Сентября 2008 в 23:12
Вобщем если скрипт использует много объектов, но ты его точно менять не будешь, то лучше прям в у объектов код создавай.
Combo
Активный участник
**

Репутация: 14
Offline Offline

Сообщений: 186


НЕ женат

Ответ № 13 23 Сентября 2008 в 22:31
  • Старайтесь избегать включения точной проверки столкновения у спрайтов (Precise Collision Checking), а также не используйте соответствующий флаг (prec) в функциях collision_*. В этом случае проверка столкновения будет проходить по прямоугольникам, но скорость намного выше. Часто люди просто забывают ее выключать, поэтому алгоритм работает впустую - проверяет весь спрайт, вместо того, чтобы проверить лишь пересечение прямоугольников.

Что значит проверять весь спрайт, т.е. как работает алгоритм пресайз колижн?

  • Всегда, повторяю, ВСЕГДА заносите временные переменные в список var в начале скрипта! Иначе они останутся с объектом до конца игры, тем самым захламляя память.

Можно ли удалить переменную? Если да, то как?

  • По возможности используйте деактивацию статических объектов (например, бочек или ящиков).

Что происходит с деактивированным объектом? У него просто перестают проверяться все эвенты?

  • Постарайтесь как можно меньше использовать вызовы собственных скриптов, особенно в частых вычислениях. Некоторые "товарищи" специально выносят код из объектов в скрипты. Это категорически противопоказано!

Если в скрипте нету argumentX вообще, то потерь производительности не будет?
Будет ли эффективнее вместо создания скрипта создать отдельный объект с кастом эвентами, и вместо вызова скрипта вызывать event_perform_object(...)?

Еще вопрос. В дебаг моде я заметил что у объектов со спрайтом состоящим из одной картинки с каждым степом растет image_number. И, кстати, у тех, у которых спрайта нет - тоже О_о. Стоит ли у таких объектов в криэйте прописывать image_speed=0?
ЗЫ извиняюсь, если в чем то повторился.
Последнее редактирование: 23 Сентября 2008 в 22:39 от Combo

RIP: execute_string, execute_program, variable_local_get...

У программера есть два состояния: "Втупляю" и "Попёрло".
WertyXBOCT
Горжусь Россией!
Частый посетитель
***

Репутация: 32
Offline Offline

Сообщений: 311


^^O','(Uu)SJJ - собери котенка

Ответ № 14 28 Сентября 2008 в 18:06
"Что значит проверять весь спрайт, т.е. как работает алгоритм пресайз колижн?"
Сравниваются все точки спрайта с точками другого спрайта, если они пересекаются то каюк)
"Можно ли удалить переменную? Если да, то как?"
Нет. Стандартными средствами ГМ нельзя
"Что происходит с деактивированным объектом? У него просто перестают проверяться все эвенты?"
Да. В целом он перестает быть активным. Все переменные которые ты сохранишь в нем будут в том же состоянии. Это уменьшает количество обращений к процессору, но увеличивает количество занимаемой памяти неактивными объектами(переменными,спрайтами,беками и т.д. простите за каламбур).

www.weslompo.ru - мой тихий бложек :).
Silen
Старожил
******

Репутация: 277
Offline Offline

Награды:
Легенда сайта
Сообщений: 2 482


Indie Game Developer

Ответ № 15 28 Сентября 2008 в 18:48
Стоит ли у таких объектов в криэйте прописывать image_speed=0?
Судя по всему в объекте постоянно выполняется следующий алгоритм:
if image_index<image_count then image_index+=image_speed else image_index=0
Так можно объяснить бесконечное тикание image_index. То есть, если ты выставишь image_speed=0, ничего не изменится.

Don't tell why, tell why not.
CaptainFaust
Активный участник
**

Репутация: 5
Offline Offline

Сообщений: 159

Ответ № 16 20 Декабря 2016 в 17:58
Вставлю свои 5 копеек. Заодно может кто-то обьяснит эту магию.

Есть комната с кучей объектов и плохим ФПС. Пытаюсь улучшить ФПС. Меняю ситуацию и делаю замеры. Замеры делаются так: каждый тик счетчик FPS увеличивается на fps_real, а на экран выводится счетчик FPS разделенный на кол-во тиков. Т.е. средний FPS.

Больше всего объектов на карте, это декор (мебель, стены и т.п.) и персонажи.

И так, список действий:

1. Ничего не делаем, изначальное состояние комнаты - 70 фпс
2. Отключаем логику у дальних врагов. Поиск пути, проверка видимости игроков. Проверка типа "если расстояние до игрока больше N, то не выполнять весь следующий код" - 135 фпс
3. Пробую деактивировать дальний декор - 235 фпс
4. Пробую  деактивировать дальних врагов - 200 фпс
5. Пробую  деактивировать дальних врагов и декор - 360 фпс
6. Пробую закомментировать врагам и декору код в эвенте Draw - 185 фпс
7. Пробую прописать в эвенте Draw врагов и декора условие if(false) - 150 фпс (в ситуации когда на экране ни одного врага или элемента декора)
8. Запускаю автоматический деактиватор дальнего декора. Объект все время проходится по списку инстансов декора, проверяет расстояние до игрока, включает ближние и выключает дальние - 235 фпс
9. Запускаю деактиватор, но возвращаю просчет логики дальних врагов - 95 фпс

Собственно вопросы
1. Почему отключение декора и врагов вместе дает больший прирост чем по отдельности?
2. Почему полное комментирование (но не удаление) кода Draw дает больший прирост чем невыполнение условие обработки кода?
3. Почему  отключение логики дальних врагов само по себе дало прирост 65 фпс, но при работающем деактиваторе и возврате логики фпс упал не на 65 - с 235 до 170, а аж на 150  - с 235 до 90.
input.txt
Не очень
Активный участник
**

Репутация: 15
Offline Offline

Сообщений: 108


12.8 Gb

Ответ № 17 20 Декабря 2016 в 20:25
2) Почему полное комментирование (но не удаление) кода Draw дает больший прирост чем невыполнение условие обработки кода?

Дело в том, что компилятор GM:S не оптимизирует условия, и в исполняемом коде так и остаётся что-то вроде:
push 0
jz 0xDEADC0DE
то есть ненужная проверка, жрущая время (впрочем немного).
Кстати, константные выражения оптимизируются на этапе компиляции: a = (2+2)*2; даст тот же код, что и a = 8;
Объявляю себя местным экспертом по байт-коду студии )


3) Почему  отключение логики дальних врагов само по себе дало прирост 65 фпс, но при работающем деактиваторе и возврате логики фпс упал не на 65 - с 235 до 170, а аж на 150  - с 235 до 90.

Очевидно, деактиватор сам по себе требует вычислительных ресурсов. Попробуй замерить без логики, но с деактиватором.
CaptainFaust
Активный участник
**

Репутация: 5
Offline Offline

Сообщений: 159

Ответ № 18 20 Декабря 2016 в 20:55
2) Почему полное комментирование (но не удаление) кода Draw дает больший прирост чем невыполнение условие обработки кода?

Дело в том, что компилятор GM:S не оптимизирует условия, и в исполняемом коде так и остаётся что-то вроде:
push 0
jz 0xDEADC0DE
то есть ненужная проверка, жрущая время (впрочем немного).
Кстати, константные выражения оптимизируются на этапе компиляции: a = (2+2)*2; даст тот же код, что и a = 8;
Объявляю себя местным экспертом по байт-коду студии )


3) Почему  отключение логики дальних врагов само по себе дало прирост 65 фпс, но при работающем деактиваторе и возврате логики фпс упал не на 65 - с 235 до 170, а аж на 150  - с 235 до 90.

Очевидно, деактиватор сам по себе требует вычислительных ресурсов. Попробуй замерить без логики, но с деактиватором.
2. Не обьясняет разницу между 185 и 150 fps
3. В том то и беда : БЕЗ логики и уже С деактиватором - 230 фпс. Да и врядли он требует ресурсов. Он у меня сначала перебирал 1 инстанс за 1 step. Получилось что проверка всех 650 экземпляров декора занимала почти 11 секунд. Я взял этот код в блок repeat(10) - то есть ускорил в 10 раз до 1.1 секунды - как было 230 фпс так и осталось. Увеличение до repeat(20) тоже не понизило фпс
CaptainFaust
Активный участник
**

Репутация: 5
Offline Offline

Сообщений: 159

Ответ № 19 20 Декабря 2016 в 21:06
если интересно то у деактиватора код onCreate такой

 
ObstacleCount=instance_number(obstacle_obj);

CurrentObstacle=0;
i=0;
Obstacle[999,2]=0;
for (i=ObstacleCount-1 ; i>=0 ; i-=1)
{
    Obstacle[i,0]=instance_find(obstacle_obj,i);
    Obstacle[i,1]=Obstacle[i,0].x;
    Obstacle[i,2]=Obstacle[i,0].y;
}
//______________________________________________________________________

WallDecorCount=instance_number(wall_decor_parent_obj);
//show_message(WallDecorCount);
CurrentWallDecor=0;
i=0;
WDecor[999,2]=0;
for (i=WallDecorCount-1 ; i>=0 ; i-=1)
{
    WDecor[i,0]=instance_find(wall_decor_parent_obj,i);
    WDecor[i,1]=WDecor[i,0].x;
    WDecor[i,2]=WDecor[i,0].y;
}

MaxDistanceToPlayer=(screen_rx-screen_lx)+200;


а onStep такой

 
repeat(10)
{
// Obstacles

if(instance_exists(Obstacle[CurrentObstacle,0]))
{
    Distance=point_distance(hero_obj.x,hero_obj.y,Obstacle[CurrentObstacle,1],Obstacle[CurrentObstacle,2]);
    if(Distance>MaxDistanceToPlayer)
        instance_deactivate_object(Obstacle[CurrentObstacle,0]);
}
else
{
    Distance=point_distance(hero_obj.x,hero_obj.y,Obstacle[CurrentObstacle,1],Obstacle[CurrentObstacle,2]);
    if(Distance<=MaxDistanceToPlayer)
        instance_activate_object(Obstacle[CurrentObstacle,0]);
}
CurrentObstacle+=1;
if(CurrentObstacle>=ObstacleCount)
    CurrentObstacle=0;
   
// Wal decor

if(instance_exists(WDecor[CurrentWallDecor,0]))
{
    Distance=point_distance(hero_obj.x,hero_obj.y,WDecor[CurrentWallDecor,1],WDecor[CurrentWallDecor,2]);
    if(Distance>MaxDistanceToPlayer)
        instance_deactivate_object(WDecor[CurrentWallDecor,0]);
}
else
{
    Distance=point_distance(hero_obj.x,hero_obj.y,WDecor[CurrentWallDecor,1],WDecor[CurrentWallDecor,2]);
    if(Distance<=MaxDistanceToPlayer)
        instance_activate_object(WDecor[CurrentWallDecor,0]);
}
CurrentWallDecor+=1;
if(CurrentWallDecor>=WallDecorCount)
    CurrentWallDecor=0;
   
//
}
Страниц: 1   Вверх
  Печать  
 
Перейти в:  

RSGMC (gmakers.ru) © 2007—2017
Счётчик–@Mail.ru