Флейм
GameDev.ru / Флейм / Форум / Движок к стратежке [смена сезонов] (2 стр)

Движок к стратежке [смена сезонов] (2 стр)

Поделиться

Страницы: 1 2 3 4 Следующая

MadwareПостоялецwww6 фев. 201717:08#15
Впервые вижу код на этом языке, но у меня есть вопрос. Тут каждый раз заново аллоцируется память?
// рендеринг
        render_state = game_state.clone();

        for (x, y) in active.shape {
            render_state.set(active.x+x, active.y+y, active.shape.get(x, y));
        }

Джек АллигаторПостоялецwww6 фев. 201719:31#16
Да.
Игровая логика сделана через битмаски.

Есть двухмерный массив 10х20 с состоянием игрового поля - статичными блоками (game_state).
И есть массив 4х4 с информацией о текущей фигуре, которая падает вниз (active).
Каждый кадр идут проверки на коллизии, притом что фигура не существует в пространстве поля пока не упадет.

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

Конечно можно было запилить без лишних состояний, передавая на рендер ссылку на статическое поле и отдельно ссылку на фигуру(притом это более правильный подход), но я не особо запаривался по оптимизации, тк это всего лишь тетрис.

Джек АллигаторПостоялецwww19 фев. 20171:40#17
kipar
> зачем core.delta не совсем ясно. Для отображения нужен (сглаженный) ФПС, а для
> игровой логики лучше таймер или что-то с фиксированной дельтой времени. Конечно
> фиксированную дельту легко свелосипедить на дельте, но зачем?
Сначала не воспринял это всерьез.
Теперь пилю трехмерную игру и от изменения fps очень сильно плывёт дельта, а следовательно - неравномерный поворот камеры во время движения мыши и тд.

Решил таки сделать:
1) фиксированную дельту таймером для фазы апдейта объектов
2) плавающую дельту в бесконечном цикле рендера
И никак не могу понять как это дело реализовать без многопоточности, ведь если будет один бесконечный цикл рендера, то таймер здесь уже не запилишь без второго потока.
Попробовал сделать многопоточность и не осилил. Да и вообще на геймдеве в аналогичных темах советовали не выносить апдейт в отдельный поток, поэтому забил на это.

Придется имитировать фиксированную дельту через плавающую, внутри бесконечного цикла рендера?
Наткнулся на статью https://habrahabr.ru/post/136878/ и там как раз так и делают(последний вариант).
Но мне не нравится это решение - если рендер не уложится в отведенное ему время, то никакой фиксированной дельты не получится.
В статье предлагают делать несколько апдейтов на случай долгого рендера. Кто-то так делал? Какие подводные камни у этого решения?

...

Вообще говоря я собираюсь делать TBS и высокий fps не нужен.
Какие недостатки решения делать вместо бесконечного цикла тупо таймер и рендерить, допустим, ровно 25 кадров каждую секунду?

Правка: 19 фев. 2017 1:46

Джек АллигаторПостоялецwww20 фев. 20179:03#18
Никто ничего так и не посоветовал и я решил добить идею с разделением на потоки. И у меня удалось! Притом совершенно безгеморно. Не понимаю массовой истерики по этому поводу. Да и синхронизировать ничего не надо.

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

kiparПостоялецwww20 фев. 201713:46#19
ыы, я видел тему и уже собирался ответить, но потом отвлекли и я забыл.
В общем
- физика намного проще если она с фиксированным шагом. И считается быстрее и проблем меньше. Соответственно допустим у нас шаг 1/60 секунды.
Теперь есть два варианта тормозов - если тормозит физика (ну и игровая логика, в общем обновление мира) и если тормозит рендер.

Если тормозит физика - ничего не сделаешь, она просто не будет укладываться в 1/60 секунды и игра замедлится. Либо не допускать этой ситуации выбрав шаг побольше, либо смириться с тем что действие в игре будет замедляться.
Если тормозит рендер - нет смысла честно считать физику 60 раз в секунду если пользователь все равно увидит изменения только 5 раз в секунду. С тем же успехом мы могли бы считать по 12 апдейтов физики (все с тем же фиксированным шагом 1/60) перед каждым кадром. Ну и как это реализуется я думаю очевидно.

Насчет потоков - проблемы без синхронизации возникают если во время апдейта может возникать несогласованное состояние. Например игрок телепортировался и мы сначала обновили Х (тут вклинился рендер) а потом обновили Y. В итоге он нарисуется не в том углу.

Джек АллигаторПостоялецwww20 фев. 201718:24#20
kipar
> Насчет потоков - проблемы без синхронизации возникают если во время апдейта
> может возникать несогласованное состояние. Например игрок телепортировался и мы
> сначала обновили Х (тут вклинился рендер) а потом обновили Y. В итоге он
> нарисуется не в том углу.
Это Rust :D
Здесь на этот случай есть каналы. Поток А закидывает данные в канал. Поток Б проверяет его на предмет новых данных. Притом в А этих данных уже нет. Поэтому гарантируется целостность передачи - в один момент времени у данных есть только один владелец. Да, в потоке рендера придется дублировать информацию, но как мне кажется - не такая это уж и проблема.

> Если тормозит физика - ничего не сделаешь
> Если тормозит рендер - нет смысла честно считать физику
Я делаю TBS типа Total War, поэтому вполне есть смысл, тк в игре будет три ситуации, когда потоки будут вести себя по разному(допустим максимальная скорость рендера 60fps а апдейта 25fps):
1. Обычный геймплей когда игрок кликает по юнитам и городам и отдает им приказы. Здесь никаких тормозов быть не может - update 25fps и render 60fps. Анимация и движение интерполируются, игрок доволен ровным геймплеем и плавными анимациями.
2. Игрок пропускает ход и в дело вступает ИИ ботов, который в TW довольно тяжеловесен и фризит игру по 1-5сек на ход каждого бота. update 5fps и render 60fps. Здесь будет выигрыш от двух потоков в том плане, что игрок не будет испытывать дискомфорт от фризов.
3. Тормозит рендер - не справляется видеокарта(или не оптимизирован поток рендера). update 25fps и render 15fps. Игровой процесс будет продолжаться как обычно и мне не придется шаманить с delta time. Притом сохранится отзывчивость управления, что на, мой взгляд, самое главное преимущество разделения на потоки, тк по моим наблюдениям тормоза выбешивают не столько из-за низких fps, сколько из-за потери отзывчивости управления, когда время от времени input просто теряется.

Вообще говоря, два потока - не конечная цель. Вот весь план:
1. core - этот тред открывает окно, стартует обработку его событий, запускает остальные треды и обеспечивает общение между ними. если какой-то тред грохнулся, перезапускает - а для остальных тредов это будет совершенно прозрачно
2. update - вся игровая логика здесь. цикл обновляется ровно 25 раз в сек. на вход поступает информация о событиях окна из core(input), событиях ui через core. на выход - формализованный список объектов на рендер, обновления для ui
3. ui - обработка всех элементов интерфейса фиксированно 60 раз в сек. отдельный поток на тот случай, если update виснет - сохранится отзывчивость(можно будет выйти в меню даже если игра подвисла). принимает input из core и обновления своего состояния из update через core, отправляет свои события в update через core и формализованный список объектов для рендера
4. render - здесь все вызовы opengl. получает списки объектов из update и ui, сохраняет как текущее состояние. А то что было текущим - как предыдущее. Каждый кадр интерполирует их состояния. Подключает один шейдер - рисует один класс объектов, затем другой шейдер и другой класс и тд. + здесь будет заливка буферов на GPU если создаются новые объекты и тд в таком духе.

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

kiparПостоялецwww21 фев. 201713:46#21
Джек Аллигатор
> Здесь на этот случай есть каналы.
Ну каналы это один из способов синхронизации. А то "синхронизировать ничего не надо" меня уж очень поразило.
nesПостоялецwww21 фев. 201714:15#22
>Покритикуйте движок
Движок - говно.
Джек АллигаторПостоялецwww21 фев. 201715:37#23
kipar
> Ну каналы это один из способов синхронизации. А то "синхронизировать ничего не
> надо" меня уж очень поразило.
хм, походу я чето напутал с терминологией. тк имел ввиду тот случай, когда требуется остановить оба потока и только тогда что-то делать с общими данными. асинхронные каналы не требуют остановки потоков, потому на мой взгляд это наиболее подходящее решение
MadwareПостоялецwww22 мая 20176:59#24
Есть прогресс по движку? Стал развивать его вообще?
Джек АллигаторПостоялецwww22 мая 201721:25#25
Madware, конечно. Но в марте появились дела поважнее, поэтому отложил разработку до осени.
На данный момент стабильно работает отображение планеты, состоящей из десятков тысяч тайлов (гексагоны + 12 пентагонов), есть возможность кликнуть мышью на любой из них, есть возможность разместить юнита на любой из них. Работает отображение gui, состоящего из независимых окон, способных выводить 2д изображения или текст, притом различный для состояний дефолт, наведения мыши и клика мыши. Все gui элементы могут иметь маску активности, что позволяет придать им любую форму(круглые, овальные, треугольные и тд).
И всё это в двух независимых потоках: один для рендера, другой для игровой логики.

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

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

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

Правка: 22 мая 2017 21:38

MadwareПостоялецwww23 мая 201714:09#26
Интерес есть.

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

То что код пока не подчищен это конечно жаль. Ну да все равно полезно будет, для обучения то

Джек АллигаторПостоялецwww23 мая 201723:08#27
https://github.com/jackalligator/TBS
Если захочешь скомпилировать - учти что изначально папка crates была в другом месте, поэтому необходимо откорректировать Cargo.toml с учетом нового расположения.
В отдельные крейты я вынес независимые от движка модули: инструменты для удобной работы с json и самописную мат.библиотеку(которая подойдет так же и к тетрису из нульпоста).

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

> Я тут заинтересовался кодированием на Rust, даже начал что-то пытаться на нем
> писать, но хлебнул горя сразу с порога. Все эти штуки типа владения, времени
> жизни, типажи вместо обычных объектов.
> В итоге я сейчас пишу вообще на C# некий фреймворк, но хочется позже еще раз
> вернуться к этому языку (пока что книжку читаю).
На самом деле ничего сложного там нет, вопрос привычки. Достаточно один раз понять, чтобы потом не задумываться об этом. Все эти владения и времена жизни очень сильно помогают в предотвращении возникновения ошибок, если ими уметь правильно пользоваться. На других языках каждый опытный программист проделывает те же действия в голове, а тут машина за тебя отдувается. Один фиг при возникновении ошибок будешь следить, какие данные где находятся и когда уничтожаются.

> типажи вместо обычных объектов
Тип описывает объекты, у которых одинаковые свойства и методы. Типаж описывает объекты, у которых одинаковые методы, а свойства могут быть разными. Это позволяет писать функции, обращающиеся к объектам неизвестного типа, зная лишь какие методы они имплементировали. Лично для меня эта концепция гораздо проще, чем классы С++, которую я до сих пор так и не освоил.

Только с лайфтаймами может быть гемор, тк многие объявленные тобой лайфтаймы компилятор примет за корректные, а по смыслу получится чушь. Надо внимательно следить за тем, чтобы не объявлять лишних лт и везде проставлять отношения, какой лайфтайм короче, какой длинее. Просто прописать объявления 'a, 'b не выйдет, хотя на первый взгляд кажется что всё работает.

Чтобы вкурить всё это, летом 2016 я выделил пару недель свободного времени и потихоньку постигал суть раста. В результате получился такой конспект: https://github.com/jackalligator/TBS/blob/master/docs/rust-docs.rs
По большей части - перевод http://rustbyexample.com
Он актуален только для лета 2016, по новым версиям смотри что-нибудь типа такого:
https://habrahabr.ru/post/315192/
https://habrahabr.ru/post/324448/
https://habrahabr.ru/post/327708/

Правка: 24 мая 2017 1:05

MadwareПостоялецwww24 мая 201712:28#28
Джек Аллигатор
Спасибо за ссылки.

Меня интересует такой вопрос. Есть у меня какой-то типаж "коллижен шейп", он реализуется различными фигурами для столкновений — квадрат, круг, выпуклый многоугольник.
И все коллижн шейпы должны храниться в одном векторе, для того чтобы система коллизий могла их разом все обсчитать в цикле в определенный момент.

Я правильно понимаю что мне нужно для всех структур реализовать этот типаж а потом все их поместить в enum и определять конкретный тип через сопоставление с образцом?
А как сделать чтобы они все просто полиморфно обрабатывались и не было нужды плодить на каждый отдельный тип фигуры свои дублирующиеся строки?

Джек АллигаторПостоялецwww24 мая 201713:06#29
Madware
> нужно для всех структур реализовать этот типаж
Да

> потом все их поместить в enum и определять конкретный тип через сопоставление с
> образцом?
Можно и так, но это излишнее усложнение. Во время использования методов треита тебе ведь не надо знать, какого конкретного типа объект ты обрабатываешь в данный момент. Достаточно знать, что у него есть метод треита, а это у тебя автоматически есть сразу после того как ты пишешь, например:

impl CollisionShape for Rect{}

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

> А как сделать чтобы они все просто полиморфно обрабатывались и не было нужды
> плодить на каждый отдельный тип фигуры свои дублирующиеся строки?
Может я чето не так понял, но есть возможность прямо в треите написать общий для всех метод. И, если есть необходимость, в отдельных impl для каждого типа написать свои специфические реализации.
В любом случае строку impl - for -, писать придется, просто чтобы дать знать компилятору что треит поддерживается.

Страницы: 1 2 3 4 Следующая

/ Форум / Флейм / ПроЭкты

2001—2017 © GameDev.ru — Разработка игр