stiry improvements

This commit is contained in:
anton.gurov 2019-11-11 20:08:17 +03:00
parent d304ff7837
commit 1ba189697f
2 changed files with 204 additions and 7 deletions

View File

@ -1,10 +1,172 @@
Делать
---
#### декодирование ввода в отдельном потоке
Этот пункт исключительно только ради удобства. По умолчанию в BLT ввод отдается в виде int-ов, мне гораздо удобнее
получать ввод в виде "<модификатор(ы)+><имя клавиши>", например "Ctrl+Space".
Для этого пришлось написать map[int]string и пеервести все BLT-токены для клавиатуры в строки, а также написать
простенький обработчик:
```go
package main
func decodeInput(ctx util.ClientCtx) {
var exit = false
for !exit {
select {
case keycode := <-State.RawInput:
if keycode == blt.TK_NONE {
continue
}
if keycode == blt.TK_CLOSE {
State.Exit <- struct{}{}
return
}
var pressed = ""
var isModifier, _ = util.IntInSlice(keycode, modifiers)
if !isModifier {
pressed = ui.Scancodemap[keycode]
if blt.Check(blt.TK_SHIFT) != 0 {
pressed = "Shift+" + pressed
}
if blt.Check(blt.TK_ALT) != 0 {
pressed = "Alt+" + pressed
}
if blt.Check(blt.TK_CONTROL) != 0 {
pressed = "Ctrl+" + pressed
}
//global hotkeys
switch pressed {
//case "Ctrl+q", "Escape":
case "Ctrl+q":
State.Exit <- struct{}{}
exit = true
return
default:
if pressed != "" {
State.Input <- pressed
}
}
}
}
}
}
```
и в main функции обустроить всё для ее работы:
```go
var State = gamestate.GameState{
Exit: make(chan struct{}, 1),
Input: make(chan string, 1),
RawInput: make(chan int, 1),
}
...
func main() {
...
go decodeInput()
...
var exit = false
for !exit {
select {
case State.RawInput <- readKeyCode():
break
case pressed := <-State.Input:
screenMgr.CurrentScreen.HandleInput(pressed)
break
case <-State.Exit:
mainCtx.Logger().Warn().Msg("quitting NOW")
exit = true
break
...
}
...
}
func readKeyCode() int {
if !blt.HasInput() {
return blt.TK_NONE
}
return blt.Read()
}
```
Теперь как только что-то приезжает к канал State.Input - просто передаем это в обработчик ввода текущего экрана.
И пользуемся удобными обозначениями клавиш.
#### Реализация FPS и троттлинг
FPS в случае использования скажем SDL напрямую может уехать за несколько сотен. Это не лучшим способом скажется
на утилизации CPU.
Иногда нужен отдельный FPS для некоторых видов анимации (в моем случае есть слегка анимиррованные тайлы).
реализуется это достаточно просто:
```go
type TerrainRenderer {
...
animateTiles *time.Ticker //поле для тикера
...
}
декодирование ввода в отдельном потоке
func NewTerrainRenderer() *TerrainRenderer {
t := &TerrainRenderer{}
...
t.animateTiles = time.NewTicker(time.Second / 12)
return t
}
...
var redraw = true
...
Реализация FPS и троттлинг
//Это запускается в main thread ДО main loop в отдельном потоке - go terrainRenderer.Listen()
func (tr *TerrainRenderer) Listen(state gamestate.GameState) {
for {
select {
case <-state.FovRecompute: //глобальное состояние говорит что видимость изменилась
fovRecompute = true
case <-state.Redraw: // или что просто надо совежить картинку
redraw = true
case <-tr.animateTiles.C: // а вот внутренний тикер говорит нам что пора играть следующий кадр анимации
redraw = true
}
}
}
...
//А это собственно отрисовка ландшафта, вызывается в main thread
func (tr *TerrainRender) Render() {
if redraw {
redraw = false
//рисуем ландшафт на карте
...
}
...
}
```
Отдельный FPS для некоторых видов анимации (в моем случае анимиррованные тайлы)
#### Каналы состояний и их Listеner-ы
Каналы состояний и их Listеner-ы
Ну вобщем-то из кода выше видно, как это работает. Для состояния движка ок, для состояния игры - кмк больше подойдет обычная или timed queue.
Не делать
==
- Замыкания (не зная точно, где и как вы их уничтожите). Просто наберите в гугле golang closure memleak
- reflect в main loop. Лишь **только** выкинув рефлкесию и больше ничего не делая - я снизил потребление CPU приложением
**вдвое**. Это удобная штука, не спорю, но пользоваться ей надо при загрузке ресурсов, при сохранении/загрузке состояния
приложения - т.е. при разовых операциях. Как оказалось, она _очень_ дорогая по CPU. Кто пользоуется ей в main loop, ORM
и прочих нагруженных местах - да будет предан анафеме.

View File

@ -1,10 +1,45 @@
Автоматизация сборки и тестирование
===
- Почему это важно: мелочи сжирают кучу времени. Не позволяйте им это делать!
- настройка под Linux: все внешние либы собраны и включены в монорепо (дело вкуса)
- Go-специфичные вещи: glide, go mod
Мелочи сжирают кучу времени. Не позволяйте им это делать!
Почему это важно
---
Про сборку под разные ОС я даже уюеждать не буду - аудитория рогаликов мало того что крохотная, так еще и сильно
сегментирована по осям. Go почти бесплатно дает вам возможность сборки под все мажорные оси, пользуйтесь этим - и
потенциально в разы больше народа ознакомится с вашим творением.
Почему это очень важно
---
Запуск тестов и сборки руками отнимает вроде бы немного времени. Но это настолько частая операция (вы же собираете новые
версии после багфиксов, да?!), что даже жалкие 5 минут после 12 сборок в день превращаются в ЧАС потерянного времени. А
время вам никто, никогда, ни при каких обстоятельствах не вернет.
Подготовка
---
Дальше я буду рассказывать про настройку под Linux, поскольку это моя основаня рабочая ось. Но на самом деле настройка
под Windows/MinGW и Мак не так уж и сильно отличаются.
###### соберите внешние библиотеки под каждую целевую для сборки ось, одной версии, и положите их в репозиторий.
Каждый раз выкачивать скажем BLT от разработчика - ненадежно, менять версию библиотеки на новую и несовместимую вы точно не
будете, а готовая версия, уже собранная вас наверняка устраивает по фичам, правда ведь?
Вот и заморозьте версию и оставьте эталон там, где он не потеряется. Можно прям в системе контроля версий (Git).
###### Потратьте день на изучение вендоринга в Go
Прежде всего речь про go mod. Glide, godep и прочие - официально уже более не поддерживаются.
Только не просто пролистайте мануал, а прям создайте пакет с hello world, опубликуйте на гитхабе, попробуйте его в
другой проект импортировать, поймите зачем нужны теги и что такое семантическое версионирование.
Если вы профессионально программируете (или планируете это делать) на Go - этот потраченный день вернется вам отсуствием
в вашей жизни недель головной боли из-за того, что "сборка внезапно сломалась и не работает".
Как выжить при кросс-компиляции, краткий курс
---
- Кросскомпиляция, CGO для Mac и Linux. CGO_ENABLED=1, mingw, локальная видимость библиотек
- Makefile и нафига он нужен
- Таргеты: Убираем бардак за собой - distclean, build
- Автоматическое тестирование, testify. Не ленитесь писать тесты!