update story
This commit is contained in:
parent
913110508c
commit
49768295f2
@ -46,4 +46,96 @@ RPATH ./
|
|||||||
|
|
||||||
Теперь при дистрибуции приложения можно просто положить .so файл библиотеки рядом, и все будет работать!
|
Теперь при дистрибуции приложения можно просто положить .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