update story
This commit is contained in:
parent
913110508c
commit
49768295f2
@ -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. В целом картина именно такая, но больше подробностей можно
|
||||
найти по ссылкам в комментариях.
|
||||
|
Loading…
x
Reference in New Issue
Block a user