update story

This commit is contained in:
anton.gurov 2019-11-08 14:23:27 +03:00
parent 913110508c
commit 49768295f2

View File

@ -46,4 +46,96 @@ RPATH ./
Теперь при дистрибуции приложения можно просто положить .so файл библиотеки рядом, и все будет работать!
Вторая кочка, на котрой
Вторая кочка, на которой мне пришлось столкнуться -
то то, что вызов любой своей функции не из main thread BLT воспринимает крайне нервно,
и сыпет фатальными ошибками. Справедливости ради точно так же ведет себя и большинство
других библиотек связанных с рендером и вводом-выводом, тот же SDL например. Так что
воспринимайте это как милую особенность использования CGO.
Лекарство тут ровно одно - вызывать сишные foreign functions из main thread.
Для реализации этого требования мне показалась полезной следующая конструкция
```go
package main
import "runtime"
...
// Рецепт чтобы убежать от [fatal] 'refresh' was not called from the main thread
// https://github.com/golang/go/wiki/LockOSThread
func init() {
runtime.LockOSThread()
}
type GameState struct {
Mainfunc chan func()
}
// do запускает функцию f в контексте main thread.
func (g *GameState) Do(f func()) {
done := make(chan struct{}, 1)
g.Mainfunc <- func() {
f()
f = nil //zero pointer в замыкание
done <- struct{}{}
}
<-done
}
var State = GameState{
Mainfunc: make(chan func()), //блокирующий канал(!)
}
// И где-то в Main Loop делаем примерно так:
func MainLoop(state GameState) {
...
//В этом select обработка ввода, рендер, пеерколючение состояний интерфейса итп
for f := range state.Mainfunc {
f()
}}
...
```
State - это обычный Value Object, экземпляр типа GameState. Я его использую как
контейнер для важных для игры данных - уровня, состояния рендера, разных тикеров,
каналов для рендера и ввода-вывода итп. Так как он глобальный (или просто передается
по аргументам), то именно в него встроен метод Do. Если нам скажем в пакете где
описывается некий предмет надо нарисовать при его поднятии какой-то супер-эффект
на экране - мы поступаем вот так:
```go
package item
import "main"
import blt "some.repo.ru/user/bearlibterminal"
var State main.GameState
//функция скажем поднятия описание предмета...
func (item *Item) Pickup() {
....
doSuperEffect(State)
}
//and there we go like this:
func doSuperEffect(State main.GameState) {
...
State.Do(func() {
renderSuperEffect()
// ...do stuff in main thread
})
...
}
...
func renderSuperEffect() {
blt.Layer(0)
blt.Print("WAAAGH")
...
}
```
Здесь renderSuperEffect - непосредственная реализация эффекта, doSuperEffect -
запихивает в очередь на выполнения в main thread эту самую реализацию. Которая с успехом
выполняется в main loop. В целом картина именно такая, но больше подробностей можно
найти по ссылкам в комментариях.