Объединено Roulette MEGA Tracker (RMT) - калькулятор ставок Шпилевого

8 мес. 1 день назад - 8 мес. 1 день назад #16 от Shpilevoy

Снежанна пишет:
Почему так долго?


Ха-ха... 
= я делаю ИДЕАЛЬНЫЙ ПРОДУКТ )))

Сравни, например

1) графики мои, после 5 дней (!!!) разбора как вообще работает вершинный шейдер в UI




2) и графики профессиональные



/////////////////////
я хочу, чтобы прила была вылизанная и мега-крутая... я не студент запускать дрова, слепленные на коленках.... я почти "гуру" в юнити )))) далеко, конечно, но я стремлюсь к лучшему

поэтому надо найти работы мастеров и понять, как они ЭТО делают....

/////////////////////////////////////////////////////

После долгих экспериментов написал скрипт!

Скрипт C#, который в UNITY идеально отображает график UI по исходному массиву float list arCredit. При этом линия и точка - это префабы image. График масштабируется под размеры родительской панели и имеет легенду по оси Y.
p.s. Любые попытки рисовать аппаратно из самого скрипта, без префабов - обречены на ошибочное отображение элементов или их непрезентабельный вид.



Как говорится = почувствуйте разницу ))) а это неделя на такую мелочь!!!

Нет хода? Ходи конем!
Вложения:
Спасибо сказали: klick, Axsee

8 мес. 1 день назад - 8 мес. 1 день назад #17 от Shpilevoy
UNITY. Любой шрифт .TTF перегнать в TEXTMESHPRO


Нет хода? Ходи конем!
Вложения:

7 мес. 4 нед. назад - 7 мес. 4 дн. назад #18 от Shpilevoy
Как-то так ))))

прива друзья! меня спрашивают = когда можно будет скачать? не могу сказать, в начале надо набить базовый функционал по всем разделам.... и главное = не просто дать хорошие стратегии, а запустить нейросети по выявлению шаблонов в ответах адаптива на рулетке... а ЭТО вообще новая моя тема = сколько буду с ней разбираться, не знаю

////////////////////////

1. Любые азартные игры могут привести к зависимости. Играйте ответственно.

2. Выделяйте в месяц на игры казино не более 30% от своего общего дохода. Никогда не играйте на кредитные деньги и не ставьте задачей с помощью казино решить свои текущие финансовые трудности.

3. Цель игровой сессии на слотах казино - получить умножение вложенных в игру денег. Научитесь достигать x2 от общей суммы заведенных в игру денег. Для общего профита в долгосрочной игре с казино вам необходимо стабильно уметь делать x2 и более от суммы вложенных денег как минимум в 2 из 10 играх.
 
4. Для профита в периоде long-run нужен опыт, запас денег и расчет:
* выбирайте лицензированные слоты известных провайдеров
* изучить характер слота
* поймите волновую природу динамики кредита
* на мелких ставках ощутите волны дисперсии выплат
* четко понимайте дистанции в просадке баланса
* реально понимайте частоту и возможности ожидаемых выигрышей
* научитесь плавно разгонять свои ставки и линии в долгом периоде
* принимайте обоснованные решения в типичных игровых ситуациях
* строго придерживайтесь ограничений stop-loss в игре
* всегда контролируйте свои эмоции и деньги
* разделяйте риски
* будьте готовы к последствиям как выигрыша, так и проигрыша 
* научитесь ждать свой апстрик днями или даже неделями

5. Прежде, чем серьезно играть в любом казино - научитесь побеждать на мелких депозитах и ставках. Еще лучше, воспользуйтесь ДЕМО режимом игры. Есть только одна стратегия победы: ФАН - МЕЛКИЙ РЕАЛ - РЕАЛ - ХАЙРОЛ. Каждый переход на следующий уровень может происходить только после уверенного долгосрочного профита на предыдущем. Рост масштаба игры и ставок должен быть всегда только из плюсовых денег предыдущего уровня.

6. Предложенный симулятор многолинейных слотов казино обеспечивает математику реальных слотов. В нем реализованы основные принципы как лицензионных игровых автоматов, также их скриптовых пиратских копий:
* Чем выше ставка, тем реже выигрыши
* Чем выше ставка, тем хуже коэффициенты выигрышей
* Выигрыш прячется в наборе линий, но все линии играть экономически не выгодно

7. RTP - это фиктивный рекламный показатель для слотов казино: 
* Доказанная отдача игрового автомата может быть 99,9% но при этом игрок никогда не увидит даже +1$ к вложенным деньгам.
* Казино считает % RTP на миллиардах спинов и короткий период любой реальной игры никак не корреспондируется с заявленным процентом, потому что главный фактор - дисперсия выплат.

///////////////////////////

1. Any gambling can lead to addiction. Play responsibly.

2. Allocate no more than 30% of your total income per month to casino games. Never play for credit money and do not set a goal with the help of a casino to solve your current financial difficulties.

3. The purpose of the gaming session on casino slots is to get a multiplication of the money invested in the game. Learn to reach x2 of the total amount of money brought into the game. For the overall profit in a long-term game with a casino, you need to consistently be able to make x2 or more of the amount of money invested in at least 2 out of 10 games.
 
4. For profit in the long-run period, you need experience, a supply of money and calculation:
* choose licensed slots from well-known providers
* explore the nature of the slot
* understand the wave nature of credit dynamics
* Feel the waves of payout dispersion on small stakes
* clearly understand the distances in the balance drawdown
* really understand the frequency and possibilities of expected wins
* learn how to smoothly accelerate your bets and lines in the long run
* make informed decisions in typical game situations
* strictly adhere to stop-loss restrictions in the game
* always control your emotions and money
* share the risks
* be prepared for the consequences of both winning and losing
* learn to wait for your upstreak for days or even weeks

5. Before playing seriously in any casino, learn how to win on small deposits and bets. Even better, use the DEMO game mode. There is only one winning strategy: FUN - SMALL REAL - REAL - HIROLL. Each transition to the next level can occur only after a confident long-term profit on the previous one. The growth of the scale of the game and the stakes should always be only from the plus money of the previous level.

6. The proposed casino multi-line slots simulator provides the mathematics of real slots. It implements the basic principles of both licensed slot machines and their scripted pirated copies:
* The higher the bet, the rarer the winnings
* The higher the bet, the worse the odds of winnings
* Winning is hidden in a set of lines, but it is not economically profitable to play all the lines

7. RTP is a fictitious promotional metric for casino slots:
* The proven return of the slot machine can be 99.9%, but the player will never see even +1$ to the invested money.
* The casino calculates the % RTP on billions of spins and the short period of any real game does not correspond in any way with the stated percentage, because the main factor is the payout dispersion.

Нет хода? Ходи конем!
Спасибо сказали: Снежанна, klick, mouse, WoodForest, Edwerk, Виталий, Axsee

7 мес. 3 нед. назад - 7 мес. 3 нед. назад #19 от Shpilevoy
Когда вы на симуляторе слотов научитесь стабильно достигать x2 от начальных 5000, тогда придет понимание каркаса действий в ЛЮБЫХ СЛОТАХ.

А дальше нужно выбрать площадку и "правильные" слоты в онлайн казино, которые стабильно и честно работают... и базовую стратегию масштабировать не под 5000, а под свои реальные депозиты.


Нет хода? Ходи конем!
Вложения:

7 мес. 2 нед. назад - 7 мес. 2 нед. назад #20 от Shpilevoy
Тренажер в слоты = это так развлекуха )))
Основной доход и стабильность = это только РУЛЕТКА.

///////////////////////////
Сегодня пьем шампанское!
Закончил первый атом 
SIMPLEX
= это игра в горячий сектор поля/трека
через подтверждение(!!!), т.е. фактически, это третий ход в догон коллизии разгона
при этом имеем виртуальное отслеживание точек входа и сквозной БРМ



 

750 спинов, +960 фишек

Это как раз подойдет Edwerk = усидчиво ждать и иногда ставить = скорость примерно +1 фишка за каждый спин ожидания... при том, что на 1000 спинов будет всего лишь около 80-100 выставлений ставок!
Заработок долгий-нудный-небольшой, но стабильный )))

Повторюсь, для мульти-столов это ОТЛИЧНАЯ СХЕМА с большими ожиданиями точек входа в игру = адаптив никак не может присесть конкретно на тебя, т.к. у тебя редкие ставки... казино любит убивать конкретно игрока ПОДРЯД спин-за-спином в ноль. Поэтому за мультистолом адаптив быстрее воткнется в кого-то, кто не пропускает ходы и каждый спин делает ставки ))))

=== Чем дольше ждешь/играешь, тем больше зарабатываешь.

Нет хода? Ходи конем!
Вложения:
Спасибо сказали: Снежанна, mouse, Edwerk, Виталий

7 мес. 2 нед. назад - 7 мес. 2 нед. назад #21 от Shpilevoy
Закончил второй атом 
MIRROR
= играет на перевернутые номера 12-21, 6-9 и т.п., зеркальные комбинации A-B-C-B-A и т.п., зеркальные предикаты L/R







////////////////////
Когда интерфейсы по атомам готовы, изменять только математическую начинку очень быстро. Могу свободно и легко добавлять новые стратегии или корректировать старые.

Нет хода? Ходи конем!
Вложения:
Спасибо сказали: Снежанна, Coin

7 мес. 2 нед. назад - 7 мес. 2 нед. назад #22 от Shpilevoy
Tips Mirror
///////////////////

MIRROR atom

Principle of operation:
a) играет на перевернутые номера 12-21, 6-9 и т.п. 
б) зеркальные комбинации номеров ABCBA, ABBA
в) зеркальные предикаты дистанций ABCBA, ABBA

Игра на зеркала является одним из обязательных навыков профессионала в рулетке. Они не являются "магнитами" по притяжению очевидного номера, завершающего красивую комбинацию. Казино часто их использует для поддержания интереса в силу их очевидности.
Умозрительно игрок может обнаружить лишь малую долю явных шаблонов. Применение бота в этом вопросе дает бесспорное преимущество.

Required skills to play:
Алгоритм работает в облаке вариаций ХАОСА генераций номеров. 
Облако проявляется в том, что, например, сегодня и завтра 12-21 (до 2L) играют примерно так:
- - - + + + - + - + + - - - - - + + + - +
А вот после-завтра все резко меняется в генерации спинов для 12-21 и этот рисунок на том же столе держится сотни спинов: 
- - - - - + + - - - - - - 
Когда есть только заходы на зеркало и нет реализованных, значит сегодня на этом столе рулетки явно не зеркальный день и нет смысла слишком акцентировать ставку в зеркала.

Важно!

В казино на старте нужно понять какие сегодня тренды для конкретных комбинаций. И только потом выбирать линию игры в рамках конкретных стратегий (ключей). Адаптив казино проснется автоматически, когда кредит пробьет дозволенную верхнюю планку кредита. И как казино заставить продолжать давать прибыль - это и есть ключевой вопрос понимания АДАПТИВА для профессиональных игроков.

Примеры из жизни:
Я облизывался сидел.  И три раза так 31 номер закрывал по 4-5к в один  номер и лезло!
Ждать нужно.  Просто не играть, а ждать
.... /фото Coin rezult/

Нет хода? Ходи конем!
Спасибо сказали: Снежанна, Coin, Edwerk, Виталий

7 мес. 2 нед. назад #23 от Снежанна
Simplex и Mirror это классические CMP атомы. Можно еще DCold добавить с графиком?

7 мес. 6 дн. назад #24 от Shpilevoy

Снежанна пишет: Simplex и Mirror это классические CMP атомы. Можно еще DCold добавить с графиком?


Хер с ним, с Cosmolot
100pravda.com/forum/...igrat?start=30#70039
Буду добивать вывод денег по-ходу....

///////////////
Сегодня ужаснулся = последний раз открывал UNITY 8 дней назад! капец время летит и я проект тормознул...
Игра в зеркала давно сделана



Следующий атом пусть будет с предикатами
но не DCold.... а вот эта стратегия, на которой +1,400,000 зашло

 

ПРЕДИКАТЫ L/R
Описание здесь
100pravda.com/forum/...vogo?start=240#69961

Нет хода? Ходи конем!
Вложения:
Спасибо сказали: Coin, Axsee

7 мес. 4 дн. назад #25 от Axsee
Ждем, ждем:)

В разное время игры одни атомы разгоняют депозит, другие сливают. 
Но в старом приложении не совсем удобно между атомами перемещаться.
Интересно возможно ли реализовать, что бы прогнозы со всех атомов отображались на одном экране?
Хотя возможно места не хватит для интерфейса.
Спасибо сказали: LUCKY-13, Shpilevoy

7 мес. 3 дн. назад #26 от Shpilevoy
Возможно. Скроллинг экрана и компонент ScrollView в Unity = нет никаких ограничений по ширине и высоте контента.

Нет хода? Ходи конем!

5 мес. 4 нед. назад - 5 мес. 4 нед. назад #27 от Shpilevoy
Лучшее = враг хорошего )))
Прошла первая волна реальных испытаний на разных рулетках, в разных условиях, в разных казино.
Надо сказать, что в земных казино питы не дремлют = сразу подбегают смотреть, чем там человек на телефоне занимается )))

/////////////////////

Все предыдущие наработки ушли под нож (кроме внутренней механики).
Нейросети сдохли вообще = нулевая эффективность.
Атомы и БРМ затачиваются во взаимодействии.
Спасибо, всем кто помогал тестить прилу и оставил практические отзывы. Сейчас я все это пересматриваю и переделываю прогу, чтобы она была СВЕРХ-УДОБНОЙ.

/////////////////////

Новые интерфейсы, новая структура программы === НОВОЕ НАЗВАНИЕ.



Прошу Админа поменять название этой темы.
RMT
ROULETTE MEGA TRACKER

Еще пара месяцев переделок, испытаний и тестов, окончательной полировки алгоритмов
=== в ранний доступ прилу можно будет выкладывать

Нет хода? Ходи конем!
Спасибо сказали: Coin, mouse, WoodForest

5 мес. 4 нед. назад #28 от Coin
Заинтриговал. Ждемс-с-с-с.

☜♡☞ Roulette foreva ☜♡☞
Спасибо сказали: Shpilevoy, mouse

5 мес. 2 нед. назад - 5 мес. 2 нед. назад #29 от Shpilevoy
ЗАВЕРШАЮЩИЙ ЭТАП КОДИНГА!
///////////////////////////////

Так. Столкнулся с неожиданной проблемой в редакторе UNITY =
При активации 6+ атомов прога начинает неожиданно и произвольно зависать. Ошибки не выдает! Музыка фоновая продолжает играть, а редактор в режиме выполнения крепко висит...

Мне такая ОШИБКА не подходит ))) Мега-трекер со временем будет иметь много атомов = именно под такую НЕОГРАНИЧЕННУЮ структуру все и планировалось.

Нашел хорошую статью по оптимизации проекта..... выполнил только первые два правила...
и ОШИБКА ИСЧЕЗЛА!

Сохраню эту статью на всякий случай )))
///////////////////////////////

Первое правило: никаких новых объектов в методах Update
В идеале методы Update, FixedUpdate и LateUpdate не должны содержать ключевых слов «new». Всегда нужно использовать то, что у вас уже есть.

Иногда создание нового объекта сокрыто в некоторых внутренних методах Unity, поэтому оно не так очевидно. Мы расскажем об этом позже.

Второе правило: создавать один раз и использовать многократно!
По сути, это означает, что выделять память для всего, что можно, следует в методах Start и Awake. Это правило очень похоже на первое. На самом деле это просто ещё один способ устранения ключевых слов «new» из методов Update.

Код, который:

создаёт новые экземпляры ищет какие-нибудь игровые объекты
следует всегда стараться перемещать из методов Update в Start или Awake.

Вот примеры внесённых нами изменений:

Выделение памяти под списки в методе Start, их очистка (Clear) и повторное использование при необходимости.

//Bad codeprivate List<GameObject> objectsList;void Update(){
objectsList = new List<GameObject>(); objectsList.Add(......)
}
//Better Codeprivate List<GameObject> objectsList;void Start(){
objectsList = new List<GameObject>();}
void Update(){
objectsList.Clear();
objectsList.Add(......)
}
Хранение ссылок и повторное использование их следующим образом:

//Bad codevoid Update(){
var levelObstacles = FindObjectsOfType<Obstacle>(); foreach(var obstacle in levelObstacles) { ....... }}
//Better codeprivate Object[] levelObstacles;void Start(){
levelObstacles = FindObjectsOfType<Obstacle>();
}
void Update(){
foreach(var obstacle in levelObstacles) { ....... }}
То же относится к методу FindGameObjectsWithTag или любому другому методу, возвращающему новый массив.

Третье правило: остерегайтесь строк и избегайте их конкатенации
Когда дело доходит до создания мусора, то строки ужасны. Даже простейшие операции со строками могут создавать много мусора. Почему? Строки — это просто массивы, и эти массивы неизменяемы (immutable). Это означает, что каждый раз, когда вы конкатенируете две строки, создаётся новый массив, а старый превращается в мусор. К счастью, можно использовать StringBuilder, чтобы избежать или минимизировать такое создание мусора.

Вот пример того, как можно улучшить ситуацию:

//Bad codevoid Start(){
text = GetComponent<Text>();
}
void Update(){
text.text = "Player " + name + " has score " + score.toString();}
//Better codevoid Start(){
text = GetComponent<Text>();
builder = new StringBuilder(50);}
void Update(){
//StringBuilder has overloaded Append method for all types builder.Length = 0; builder.Append("Player "); builder.Append(name);
builder.Append(" has score "); builder.Append(score);
text.text = builder.ToString();
}
С показанным выше примером всё в порядке, но в нём есть ещё много возможностей для улучшения кода. Как видите, почти всю строку можно считать статической. Мы разделяем строку на две части для двух объектов UI.Text. Сначала одна содержит только статический текст «Player » + name + " has score ", который можно назначить в методе Start, а вторая содержит значение счёта, которое обновляется в каждом кадре. Всегда делайте статические строки действительно статическими и генерируйте их в методе Start или Awake. После этого усовершенствования почти всё в порядке, но по-прежнему генерируется немного мусора при вызове Int.ToString(), Float.ToString() и т.п.

Мы решили эту проблему генерацией и предварительным выделением памяти под все возможные строки. Это может показаться глупой тратой памяти, но такое решение идеально соответствует нашим потребностям и полностью решает проблему. Итак, в конечном итоге мы получили статический массив, доступ к которому можно напрямую получать доступ при помощи индексов, чтобы брать нужную строку, обозначающую число:

public static readonly string[] NUMBERS_THREE_DECIMAL = { "000", "001", "002", "003", "004", "005", "006",..........

Четвёртое правило: кешировать значения, возвращаемые методами доступа
Это может быть очень сложно, потому что даже простой метод доступа (accessor), наподобие показанного ниже, генерирует мусор:

//Bad Codevoid Update(){
gameObject.tag;
//or gameObject.name;
}
Старайтесь избегать использования методов доступа в методе Update. Вызывайте метод доступа только один раз в методе Start и кешируйте возвращаемое значение.

В общем случае я рекомендую НЕ вызывать никаких методов доступа к строкам или методов доступа к массивам в методе Update. В большинстве случаев достаточно один раз получить ссылку в методе Start.

Вот ещё два распространённых примера ещё одного неоптимизированного кода методов доступа:

//Bad Codevoid Update(){
//Allocates new array containing all touches Input.touches[0];}
//Better Codevoid Update(){
Input.GetTouch(0);}
//Bad Codevoid Update(){
//Returns new string(garbage) and compare the two strings gameObject.Tag == "MyTag";}
//Better Codevoid Update(){
gameObject.CompareTag("MyTag");}
Пятое правило: используйте функции, не выполняющие выделение памяти
Для некоторых функций Unity можно найти альтернативы, не выделяющие память. В нашем случае все эти функции связаны с физикой. Наше распознавание коллизий основано на

Physics2D. CircleCast();
Для этого конкретного случая можно найти не выделяющую память функцию под названием

Physics2D. CircleCastNonAlloc();
Многие другие функции тоже имеют подобные альтернативы, поэтому всегда проверяйте в документации наличие NonAlloc-функций.

Шестое правило: не используйте LINQ
Просто не делайте этого. Я имею в виду, что не нужно использовать его в любом коде, который выполняется часто. Знаю, при использовании LINQ код проще читается, но во многих случаях производительность и выделение памяти такого кода ужасны. Разумеется, его можно иногда использовать, но, если честно, в своей игре мы вообще не применяем LINQ.

Седьмое правило: создавать один раз и использовать многократно, часть 2
На этот раз речь идёт о пулинге объектов. 

В нашем случае используется следующий сценарий пулинга объектов. У нас есть сгенерированный уровень, заполненный препятствиями, существующими в течение определённого периода времени, пока игрок не пройдёт эту часть уровня. Экземпляры таких препятствий создаются из префабов в случае соблюдения определённых условий. Код находится в методе Update. Этот код совершенно неэффективен с точки зрения памяти и времени выполнения. Мы решили проблему, сгенерировав пул из 40 препятствий: при необходимости мы получаем препятствия из пула и возвращаем объект обратно в пул, когда он больше не нужен.

Восьмое правило: внимательнее с упаковкой-преобразованием (Boxing)!
Boxing генерирует мусор! Но что такое boxing? Чаще всего boxing возникает, когда вы передаёте тип значения (int, float, bool и т.д.) в функцию, которая ожидает параметр типа Object.

Вот пример boxing-а который нам нужно исправить в нашем проекте:

Мы реализовали в проекте собственную систему сообщений. Каждое сообщение может содержать неограниченное количество данных. Данные хранятся в словаре, задаваемом следующим образом:

Dictionary<string, object> data;
Также у нас есть сеттер, задающий значения в этом словаре:

public Action SetAttribute(string attribute, object value){
data[attribute] = value;}
Boxing здесь довольно очевиден. Можно вызвать функцию следующим образом:

SetAttribute("my_int_value", 12);
Тогда значение «12» подвергается boxing-у и это генерирует мусор.

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

Dictionary<string, object> data;Dictionary<string, bool> dataBool;Dictionary<string, int> dataInt;.......
Также у нас есть отдельные сеттеры для каждого типа данных:

SetBoolAttribute(string attribute, bool value)SetIntAttribute(string attribute, int value)
И все эти сеттеры реализованы таким образом, что вызывают одинаковую обобщённую функцию:

SetAttribute<T>(ref Dictionary<string, T> dict, string attribute, T value)
Проблема boxing-а решена!

Девятое правило: циклы всегда под подозрением
Это правило очень похоже на первое и второе. Просто старайтесь убрать весь необязательный код из циклов из соображений производительности и выделения памяти.

В общем случае мы стремимся избавиться от циклов в методах Update, но если без них не обойтись, то мы по крайней мере избегаем любого выделения памяти в таких циклах. Итак, следуйте правилам 1–8 и примените их к циклам в целом, а не только в методах Update.

Десятое правило: никакого мусора во внешних библиотеках
В случае, если выяснится, что часть мусора генерируется кодом, скачанным из Asset store, то у этой проблемы есть множество вариантов решения. Но прежде чем выполнять реверс-инжиниринг и отладку, просто снова зайдите в Asset store и обновите библитеку. В нашем случае все используемые ассеты по-прежнему поддерживались авторами, продолжающими выпускать улучшающие производительность обновления, поэтому это решило все наши проблемы. Зависимости должны быть актуальными! Я лучше избавлюсь от библиотеки, чем сохраню неподдерживаемую.

Часть 2: максимально снижаем время выполнения
Некоторые из представленных выше правил вносят едва заметную разницу, если код вызывается редко. В нашем коде есть один большой цикл, выполняемый в каждом кадре, поэтому даже эти небольшие изменения оказали огромный эффект.

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

Честно говоря, некоторые из этих правил приводят к гораздо хуже читаемому коду, а иногда даже нарушают рекомендации, например, упомянутое в одном из представленных ниже правил встраивание кода.

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

Первое правило: правильный порядок выполнения
Переместите код из методов FixedUpdate, Update, LateUpdate в методы Start и Awake. Знаю, это звучит безумно, но поверьте, если вы углубитесь в свой код, то найдёте сотни строк кода, которые можно переместить в методы, исполняемые только один раз.

В нашем случае такой код обычно связан с

Вызовами GetComponent<>Вычислениями, которые на самом деле возвращают в каждом кадре одинаковый результатМногократным созданием экземпляров одних и тех же объектов, обычно списковПоиском GameObjectsПолучением ссылок на Transform и использованием других методов доступа
Вот список примеров кода, который мы переместили из методов Update в методы Start:

//There must be a good reason to keep GetComponent in UpdategameObject.GetComponent<LineRenderer>();
gameObject.GetComponent<CircleCollider2D>();

//Examples of calculations returning same result every frameMathf.FloorToInt(Screen.width / 2);var width = 2f * mainCamera.orthographicSize * mainCamera.aspect;var castRadius = circleCollider.radius * transform.lossyScale.x;var halfSize = GetComponent<SpriteRenderer>().bounds.size.x / 2f;//Finding objectsvar levelObstacles = FindObjectsOfType<Obstacle>();var levelCollectibles = FindGameObjectsWithTag("COLLECTIBLE");//ReferencesobjectTransform = gameObject.transform;
mainCamera = Camera.main;
Второе правило: выполняйте код только тогда, когда это необходимо
В нашем случае это в основном относилось к скриптам обновления UI. Вот пример того, как мы изменили реализацию кода, отображающего текущее состояние собираемых предметов на уровне.

//Bad codeText text;
GameState gameState;

void Start(){
gameState = StoreProvider.Get<GameState>();
text = GetComponent<Text>();
}

void Update(){
text.text = gameState.CollectedCollectibles.ToString();
}
Так как на каждом уровне есть всего несколько собираемых предметов, то не имеет никакого смысла изменять текст UI в каждом кадре. Поэтому мы изменяем текст только при изменении числа.

//Better codeText text;
GameState gameState;
int collectiblesCount;void Start(){
gameState = StoreProvider.Get<GameState>();
text = GetComponent<Text>();
collectiblesCount = gameState.CollectedCollectibles;
}

void Update(){
if(collectiblesCount != gameState.CollectedCollectibles) {
//This code is ran only about 5 times each level collectiblesCount = gameState.CollectedCollectibles;
text.text = collectiblesCount.ToString();
}
}
Этот код гораздо лучше, особенно если действия намного сложнее, чем простое изменение UI.

Как бы то ни было, этого всё равно было для нас недостаточно, и мы хотели реализовать совершенно обобщённое решение, поэтому создали библиотеку, реализующую Flux в Unity. Это привело к очень простому решению, при котором всё состояние игры хранится в объекте «Store», а все элементы UI и другие компоненты уведомляются при изменении состояния и реагируют на это изменение без кода в методе Update.

Третье правило: циклы всегда под подозрением
Это точно такое же правило, которое я упоминал в первой части статьи. Если в коде есть какой-то цикл, итеративно обходящий большое количество элементов, то для улучшения производительности цикла используйте оба правила из обеих частей статьи.

Четвёртое правило: For лучше, чем Foreach
Цикл Foreach очень легко написать, но «очень сложно» выполнять. Внутри цикла Foreach используются Enumerator для итеративной обработки набора данных и возврата значения. Это сложнее, чем итерация индексов в простом цикле For.

Поэтому в нашем проекте мы по возможности заменили циклы Foreach на For:

//Bad codeforeach (GameObject obstacle in obstacles)//Better codevar count = obstacles.Count;for (int i = 0; i < count; i++) { obstacles;
}
В нашем случае с большим циклом for это изменение очень значимо. Простой цикл for ускорил код в два раза.

Пятое правило: массивы лучше, чем списки
В своём коде мы выяснили, что большинство списков (List) имеет постоянную длину или мы можем вычислить максимальное количество элементов. Поэтому мы реализовали их заново на основе массивов и в некоторых случаях это привело к двукратному ускорению итераций над данными.

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

Шестое правило: операции с Float лучше, чем операции с векторами
Это различие едва заметно, если вы не выполняете тысячи подобных операций, как и было в нашем случае, поэтому для нас увеличение производительности оказалось значимым.

Мы вносили подобные изменения:

Vector3 pos1 = new Vector3(1,2,3);Vector3 pos2 = new Vector3(4,5,6);//Bad codevar pos3 = pos1 + pos2;//Better codevar pos3 = new Vector3(pos1.x + pos2.x, pos1.y + pos2.y, ......);Vector3 pos1 = new Vector3(1,2,3);//Bad codevar pos2 = pos1 * 2f;//Better codevar pos2 = new Vector3(pos1.x * 2f, pos1.y * 2f, ......);
Седьмое правило: искать объекты правильно
Всегда думайте, действительно ли нужно использовать метод GameObject.Find(). Этот метод тяжёл и занимает безумное количество времени. Никогда не следует использовать такой метод в методах Update. Мы выяснили, что большинство наших вызовов Find можно заменить прямыми ссылками в редакторе, что, разумеется, гораздо лучше.

//Bad CodeGameObject player;

void Start(){
player = GameObject.Find("PLAYER");}

//Better Code//Assign the reference to the player object in editor[SerializeField]GameObject player;
void Start(){
}
В случае, если так сделать невозможно, то хотя бы рассмотрите возможность использования меток (Tag) и поиска объекта по его метке при помощи GameObject.FindWithTag.

Итак, в общем случае: Прямая ссылка > GameObject.FindWithTag() > GameObject.Find()

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

Вот пример

//Bad Codevoid DetectCollision(){
var count = Physics2D.CircleCastNonAlloc( position, radius, direction, results, distance);
for (int i = 0; i < count; i++) { var obj = results.collider.transform.gameObject; if(obj.CompareTag("FOO")) { ProcessCollision(results);
}
}
}

//Better Code//We added all objects with tag FOO into the same layervoid DetectCollision(){
//8 is number of the desired layer var mask = 1 << 8; var count = Physics2D.CircleCastNonAlloc( position, radius, direction, results, distance, mask);
for (int i = 0; i < count; i++) { ProcessCollision(results);
}
}
Девятое правило: правильно используйте метки
Нет никаких сомнений, что метки очень полезны и могут улучшить производительность кода, но помните, что существует только один правильный способ сравнения меток объектов!

//Bad CodegameObject.Tag == "MyTag";//Better CodegameObject.CompareTag("MyTag");
Десятое правило: опасайтесь хитростей с камерой!
Так легко использовать Camera.main, но производительность этого действия очень плоха. Причина заключается в том, что за кулисами каждого вызова Camera.main движок Unity на самом деле выполняет для получения результата FindGameObjectsWithTag(), поэтому мы уже понимаем, что часто его вызывать не нужно, и лучше всего решить эту проблему, кешировав ссылку в методе Start или Awake.

//Bad codevoid Update(){
Camera.main.orthographicSize //Some operation with camera}

//Better Codeprivate Camera cam;void Start(){
cam = Camera.main;
}
void Update(){
cam.orthographicSize //Some operation with camera}
Одиннадцатое правило: LocalPosition лучше, чем Position
Везде, где это возможно, используйте для геттеров и сеттеров Transform.LocalPosition вместо Transform.Position. Внутри каждого вызова Transform.Position выполняется гораздо больше операций, например, вычисление глобальной позиции в случае вызова геттера или вычисление локальной позиции из глобальной в случае вызова сеттера. В нашем проекте выяснилось, что можно использовать LocalPositions в 99% случаев использования Transform.Position, и в коде при этом не нужно делать никаких других изменений.

Двенадцатое правило: не использовать LINQ
Об этом уже говорили в первой части. Просто не используйте его, вот и всё.

Тринадцатое правило: не бойтесь (иногда) нарушать правила
Иногда даже вызов простой функции может быть слишком затратным. В этом случае всегда следует рассмотреть возможность встраивания кода (Code Inlining). Что это значит? По сути, мы просто берём код из функции и копируем его непосредственно в место, где хотим использовать функцию, чтобы избежать вызовов дополнительных методов.

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

В нашем случае нашлось несколько функций, которые мы решили встроить для улучшения производительности, особенно в большом цикле for.

Вывод
Применив перечисленные в статье правила, мы легко добились стабильных 60 fps в игре для iOS даже на iPhone 5S. Возможно, некоторые из правил могут быть специфичными только для нашего проекта, но я считаю, что большинство из них стоит помнить при написании кода или его проверке, чтобы избежать проблем в дальнейшем. Всегда лучше постоянно писать код с учётом производительности, чем позже рефакторить большие фрагменты кода.

Нет хода? Ходи конем!

5 мес. 2 нед. назад - 5 мес. 2 нед. назад #30 от Shpilevoy

Shpilevoy пишет: Так. Столкнулся с неожиданной проблемой в редакторе UNITY ...
Нашел хорошую статью по оптимизации проекта..... выполнил только первые два правила...
и ОШИБКА ИСЧЕЗЛА!




Нет, не исчезла!
Еще есть надежда, что это траблы чисто редактора UNITY. Иногда 300 спинов без зависания, а иногда на 30 спин виснет...


Подключил 7 атомов = для игры нормально.... но в планах идти дальше и больше. Что делать?
Выкладывать "как есть" не хочу, еще поборюсь =

1) Посмотрю на телефоне что будет происходить.
2) Запишу контрольную генерацию на которой виснет = если будет повторяемость ошибки в одном месте, то это логика программы. Поочередно включать атомы и тестить по 1000 спинов на зависание.... в каком именно происходит
3) FixedUpdate поменяю на просто Update = вдруг успеет в логи выкинуть текст ошибки на очередном зависании...

//////////////////////

Пока пройдусь по залам, поработаю )))

Нет хода? Ходи конем!
Вложения:
Спасибо сказали: LUCKY-13, Снежанна