Merge branch 'master' of lab.zaar.be:thefish/alchemyst-go
This commit is contained in:
commit
4ba69bfe75
@ -1,10 +1,10 @@
|
||||
{
|
||||
"version": "v0.0.1.4-2-g20fa78a",
|
||||
"version": "v0.0.1.5",
|
||||
"title": "Alchemyst",
|
||||
"sizeX": 100,
|
||||
"sizeY": 47,
|
||||
"fpsLimit": 60,
|
||||
"font": ".\/resources\/fonts-ttf\/LiberationMono-Bold.ttf",
|
||||
"fontSize": "8x12",
|
||||
"font": "./resources/fonts-ttf/LiberationMono-Bold.ttf",
|
||||
"fontSize": "9x14",
|
||||
"verbosity": "debug"
|
||||
}
|
@ -78,6 +78,9 @@ Pre-Computed Visiblity Trees on RogueBasin
|
||||
Adam Milazzo's FOV Method Roundup where a similar method described as 'permissive' is detailed
|
||||
*/
|
||||
|
||||
const MIN_LIT_TO_BE_VISIBLE = 1
|
||||
const MIN_WALL_LIT_TO_BE_VISIBLE = 4
|
||||
|
||||
var errNotFoundCell = errors.New("Cell not found")
|
||||
var errOutOfBounds = errors.New("Cell out of bounds")
|
||||
|
||||
@ -163,9 +166,9 @@ func (ps *precomputedShade) PrecomputeFovMap() {
|
||||
|
||||
//Bresanham lines / Raycast
|
||||
var lineX, lineY float64
|
||||
for i := 0; i < 360; i++ {
|
||||
dx := math.Sin(float64(i) / (float64(180) / math.Pi))
|
||||
dy := math.Cos(float64(i) / (float64(180) / math.Pi))
|
||||
for i := 0; i < 720; i++ { // 1/2 of angles
|
||||
dx := math.Sin(float64(i) / (float64(360) / math.Pi)) //1/2 of angles
|
||||
dy := math.Cos(float64(i) / (float64(360) / math.Pi))
|
||||
|
||||
lineX = 0
|
||||
lineY = 0
|
||||
@ -204,11 +207,11 @@ func (ps *precomputedShade) recalc(level *gamemap.Level, initCoords types.Coords
|
||||
|
||||
level.GetTile(initCoords).Visible = true
|
||||
|
||||
var fullShade = make([]byte, 360)
|
||||
var fullShade = make([]byte, 720) // 1/2 of angles
|
||||
for i := range fullShade {
|
||||
fullShade[i] = 1
|
||||
}
|
||||
var emptyShade = make([]byte, 360)
|
||||
var emptyShade = make([]byte, 720) // 1/2 of angles
|
||||
currentShade := emptyShade
|
||||
nextShade := emptyShade
|
||||
|
||||
@ -236,7 +239,11 @@ func (ps *precomputedShade) recalc(level *gamemap.Level, initCoords types.Coords
|
||||
//fmt.Printf("\n level coords: %v", lc)
|
||||
for _, angle := range cell.occludedAngles {
|
||||
|
||||
if level.GetTile(lc).BlocksSight {
|
||||
if level.GetTile(lc).BlocksSight && ps.LightWalls {
|
||||
if (nextShade[angle] == 0 && currentShade[angle] == 0) {
|
||||
level.GetTile(lc).Visible = true
|
||||
level.GetTile(lc).Explored = true
|
||||
}
|
||||
nextShade[angle] = 1
|
||||
}
|
||||
|
||||
@ -256,7 +263,7 @@ func (ps *precomputedShade) ComputeFov(level *gamemap.Level, initCoords types.Co
|
||||
for _, cell := range ps.CellList {
|
||||
//fmt.Printf("\n coords: %v, distance: %f, lit: %d", cell.Coords, cell.distance, cell.lit)
|
||||
cs, err := ps.toLevelCoords(level, initCoords, cell.Coords)
|
||||
if cell.lit > 2 {
|
||||
if cell.lit > MIN_LIT_TO_BE_VISIBLE {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -265,21 +272,25 @@ func (ps *precomputedShade) ComputeFov(level *gamemap.Level, initCoords types.Co
|
||||
}
|
||||
|
||||
//light walls, crutch
|
||||
if level.GetTile(cs).BlocksSight && ps.LightWalls {
|
||||
if cell.IsAdjacentTo(&types.Coords{0,0}) {
|
||||
level.GetTile(cs).Visible = true
|
||||
} else {
|
||||
for _, maybeNb := range ps.CellList {
|
||||
if //int(maybeNb.distance) == int(cell.distance-1) &&
|
||||
maybeNb.IsAdjacentTo(&cell.Coords) &&
|
||||
//(maybeNb.X == cell.X || maybeNb.Y == cell.Y) &&
|
||||
maybeNb.lit > 5 { //magic constant!
|
||||
level.GetTile(cs).Visible = true
|
||||
level.GetTile(cs).Explored = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//if level.GetTile(cs).BlocksSight && ps.LightWalls {
|
||||
// if cell.IsAdjacentTo(&types.Coords{0,0}) {
|
||||
// level.GetTile(cs).Visible = true
|
||||
// } else {
|
||||
// maybeLit := false
|
||||
// for _, maybeNb := range ps.CellList {
|
||||
// if //int(maybeNb.distance) == int(cell.distance-1) &&
|
||||
// maybeNb.IsAdjacentTo(&cell.Coords) &&
|
||||
// (maybeNb.X == cell.X || maybeNb.Y == cell.Y) &&
|
||||
// maybeNb.lit > MIN_WALL_LIT_TO_BE_VISIBLE { //magic constant!
|
||||
// maybeLit = true
|
||||
// }
|
||||
// }
|
||||
// if maybeLit {
|
||||
// level.GetTile(cs).Visible = true
|
||||
// level.GetTile(cs).Explored = true
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,35 +31,35 @@ func (ts *GameScreen) Enter() {
|
||||
Print(1, ts.mw.H-2, "Press [color=white]?[/color] for help")
|
||||
}
|
||||
func (ts *GameScreen) Exit() {
|
||||
ts.mw.GetLayer("base").ClearArea(1, ts.mw.H-2, 30, 1)
|
||||
ts.mw.GetLayer("overlay").ClearArea(0, ts.mw.H-3, 30, 3)
|
||||
//remove what we dont need
|
||||
}
|
||||
|
||||
func (ts *GameScreen) HandleInput(input string) {
|
||||
//ts.state.Do(func(){
|
||||
switch input {
|
||||
case "Up", "k", "8":
|
||||
case "Up", "k", "KP_8":
|
||||
ts.walk(ts.state, 0, -1)
|
||||
break
|
||||
case "Down", "j", "2":
|
||||
case "Down", "j", "KP_2":
|
||||
ts.walk(ts.state, 0, 1)
|
||||
break
|
||||
case "Left", "h", "4":
|
||||
case "Left", "h", "KP_4":
|
||||
ts.walk(ts.state, -1, 0)
|
||||
break
|
||||
case "Right", "l", "6":
|
||||
case "Right", "l", "KP_6":
|
||||
ts.walk(ts.state, 1, 0)
|
||||
break
|
||||
case "y", "7":
|
||||
case "y", "KP_7":
|
||||
ts.walk(ts.state, -1, -1)
|
||||
break
|
||||
case "u", "9":
|
||||
case "u", "KP_9":
|
||||
ts.walk(ts.state, 1, -1)
|
||||
break
|
||||
case "b", "1":
|
||||
case "b", "KP_1":
|
||||
ts.walk(ts.state, -1, 1)
|
||||
break
|
||||
case "n", "3":
|
||||
case "n", "KP_3":
|
||||
ts.walk(ts.state, 1, 1)
|
||||
break
|
||||
case "Shift+/":
|
||||
|
@ -1,4 +1,6 @@
|
||||
|
||||
|
||||
|
||||
декодирование ввода в отдельном потоке
|
||||
|
||||
Реализация FPS и троттлинг
|
@ -1,5 +1,11 @@
|
||||
RLG и Golang - полезные советы
|
||||
RLG и Golang - некоторые полезные советы
|
||||
===
|
||||
1. [Установка и некоторые особенности работы](./linux_go_blt.md) связки BLT + Go на Linux
|
||||
2. Реализация [некоторых возможностей](./go_chans_for_game.md) Go - chans, tickers, throttling
|
||||
3. [Система типов](./static_types_vs_ecs.md) - нативная или ECS?
|
||||
0. [Выбираем инструменты](./choose_your_pill.md)
|
||||
1. [Установка и некоторые особенности работы](linux_go_blt_install_quickstart.md) связки BLT + Go на Linux
|
||||
2. Что [стоит и НЕ стоит](go_game_dos_and_donts.md) делать с возможностями Go - +chans, +tickers, +throttling, -closures
|
||||
3. [Система типов](./static_types_vs_ecs.md) - нативная или ECS? На самом деле и то, и то
|
||||
|
||||
Дополнения
|
||||
---
|
||||
|
||||
1. Как [не делать лишнюю работу](./makefile_and_crosscompiling.md) и почему это **важно**.
|
@ -1,47 +0,0 @@
|
||||
Установка и работа с BLT на Linux
|
||||
==
|
||||
|
||||
Для Windows и Mac проблем с Go + BLT, насколько мне известно нет.
|
||||
С Linux, которая моя основная рабочая ось - другая история, здесь вносят свой шарм особенности работы линкера.
|
||||
Дело в том, что в составе BLT есть готовые биндинги для Go, НО! В Terminal/Include/Go по умолчанию
|
||||
указаны такие флаги линкера CGO (стр 25)
|
||||
```go
|
||||
// #cgo LDFLAGS: -lBearLibTerminal
|
||||
```
|
||||
Что подразумевает глобальную вивдимость библиотеки. Увы, пока пакета с BLT для распространенных дистрибутивов Linux нет.
|
||||
Поэтому беде нужно помочь руками. Сначала вручную показать линтеру, что такая библиотека есть, и потом перезагрузить
|
||||
кеш путей к библиотекам::
|
||||
```bash
|
||||
$ sudo echo "/path/to/libbearterminal.so" > /etc/ld.so.conf.d/libbearterminal.conf && sudo ldconfig
|
||||
```
|
||||
|
||||
Проблема тут в том, что эту же операцию придется проделать всем, кто захочет запустить ваше приложение с BLT. Вопреки
|
||||
распространенному стереотипу - доля красноглазых пользователей Linux с каждым годом падает, и эта консольная магия для
|
||||
большинства уже некомильфо.
|
||||
|
||||
Способ второй, которым воспользовался я, намного проще для пользователя.
|
||||
Редактируем файл с биндингами примерно следующим образом:
|
||||
```go
|
||||
// #cgo LDFLAGS: -L. -Wl,-rpath -Wl,./ -lBearLibTerminal
|
||||
// #include <stdlib.h>
|
||||
// #include <BearLibTerminal.h>
|
||||
import "C"
|
||||
```
|
||||
(знатоки С, простите, я этими флагами вообще
|
||||
пользоваться не умею)
|
||||
|
||||
Далее - собираем приложение с libtcod ```go build -o test```.
|
||||
|
||||
Проверяем, что относительные пути записались в бинарник:
|
||||
```bash
|
||||
objdump -p test | grep RPATH
|
||||
```
|
||||
Результат должен быть таким:
|
||||
```bash
|
||||
RPATH ./
|
||||
```
|
||||
|
||||
Ура! Теперь кладем libBearLibTerminal.so прямо в папку с main.go и запускам go run (или скомпилированный бинарник) прям
|
||||
оттуда. Собранные таким образом бинарники будут искать библиотеку в той же папке, где находятся они сами.
|
||||
|
||||
Теперь при дистрибуции приложения можно просто положить .so файл библиотеки рядом, и все будет работать!
|
153
story/linux_go_blt_install_quickstart.md
Normal file
153
story/linux_go_blt_install_quickstart.md
Normal file
@ -0,0 +1,153 @@
|
||||
Установка и работа с BLT на Linux
|
||||
==
|
||||
|
||||
Про Windows и Mac в контексте связки Go + BLT, я говорить не буду, поскольку не ел устриц.
|
||||
С Linux, которая моя основная рабочая ось - другая история, здесь вносят свой шарм особенности работы линкера.
|
||||
Дело в том, что BLT написана на C. Но! есть готовые биндинги для Go, НО! В Terminal/Include/Go по умолчанию
|
||||
указаны такие флаги линкера CGO (стр 25)
|
||||
```go
|
||||
// #cgo LDFLAGS: -lBearLibTerminal
|
||||
```
|
||||
Что подразумевает глобальную вивдимость библиотеки. Увы, пока пакета с BLT для
|
||||
распространенных дистрибутивов Linux нет.
|
||||
Поэтому беде нужно помочь руками.
|
||||
|
||||
#####Первый метод
|
||||
|
||||
Сначала вручную показать линтеру, что такая библиотека есть, и потом перезагрузить
|
||||
кеш путей к библиотекам (пример для Ubuntu):
|
||||
```bash
|
||||
$ sudo echo "/path/to/libbearterminal.so" > /etc/ld.so.conf.d/libbearterminal.conf && sudo ldconfig
|
||||
```
|
||||
|
||||
Проблема тут в том, что эту же операцию придется проделать всем, кто захочет запустить ваше приложение с BLT. Вопреки
|
||||
распространенному стереотипу - доля красноглазых пользователей Linux с каждым годом падает, и эта консольная магия для
|
||||
большинства уже некомильфо.
|
||||
|
||||
#####Второй метод
|
||||
|
||||
Способ второй, которым воспользовался я, с которым намного проще жить.
|
||||
Редактируем файл с биндингами (BearLibTerminal.go) примерно следующим образом:
|
||||
```go
|
||||
// #cgo LDFLAGS: -L. -Wl,-rpath -Wl,./ -lBearLibTerminal
|
||||
// #include <stdlib.h>
|
||||
// #include <BearLibTerminal.h>
|
||||
import "C"
|
||||
```
|
||||
(знатоки С, простите если что не так, я этими флагами вообще
|
||||
пользоваться не умею)
|
||||
|
||||
Далее - собираем минимальное приложение с blt ```go build -o test```.
|
||||
|
||||
Проверяем, что относительные пути записались в бинарник:
|
||||
```bash
|
||||
objdump -p test | grep RPATH
|
||||
```
|
||||
Результат должен быть таким:
|
||||
```bash
|
||||
RPATH ./
|
||||
```
|
||||
|
||||
Ура! Теперь кладем libBearLibTerminal.so прямо в папку с main.go и запускам go run
|
||||
(или скомпилированный бинарник) прям оттуда. Собранные таким образом бинарники будут
|
||||
искать библиотеку в той же папке, где находятся они сами.
|
||||
|
||||
Теперь при дистрибуции приложения можно просто положить .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. Я его использую как
|
||||
контейнер для важных для игры данных - географии уровня, состояния объектов и мобов,
|
||||
разных тикеров, каналов для рендера и ввода-вывода итп[1]. Так как он глобальный
|
||||
(или просто передается всюду по аргументам), то именно в него встроен метод Do.
|
||||
|
||||
Если нам скажем в пакете, где описывается некий предмет, надо нарисовать при его
|
||||
поднятии какой-то супер-эффект на экране - мы поступаем вот так:
|
||||
|
||||
```go
|
||||
package item
|
||||
|
||||
import "main"
|
||||
import blt "some.repo.ru/user/bearlibterminal"
|
||||
|
||||
var State main.GameState
|
||||
|
||||
//функция скажем поднятия особенного предмета...
|
||||
func (item *SpecialItem) Pickup() {
|
||||
|
||||
....
|
||||
//выполняем строго в main thread
|
||||
State.Do(func() {
|
||||
renderSuperEffect()
|
||||
})
|
||||
}
|
||||
...
|
||||
//тут собственно отрисовка эффекта
|
||||
func renderSuperEffect() {
|
||||
...
|
||||
blt.Layer(0)
|
||||
blt.Print(x,y, "WAAAGH")
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Здесь renderSuperEffect - непосредственная реализация эффекта, doSuperEffect -
|
||||
запихивает в очередь на выполнения в main thread эту самую реализацию. Которая с успехом
|
||||
выполняется в main loop. В целом картина именно такая, но больше подробностей можно
|
||||
найти по ссылкам в комментариях.
|
||||
|
||||
[1]: Если такой контейнер аккуратно сериализовать (рекурсивно вместе со всем содержимым) и записать на диск... То потом можно его прочитать и десериализовать. Получив тем самым почти бесплатно Save / Load.
|
11
story/makefile_and_crosscompiling.md
Normal file
11
story/makefile_and_crosscompiling.md
Normal file
@ -0,0 +1,11 @@
|
||||
Автоматизация сборки и тестирование
|
||||
===
|
||||
|
||||
- Почему это важно: мелочи сжирают кучу времени. Не позволяйте им это делать!
|
||||
- настройка под Linux: все внешние либы собраны и включены в монорепо (дело вкуса)
|
||||
- Go-специфичные вещи: glide, go mod
|
||||
- Кросскомпиляция, CGO для Mac и Linux. CGO_ENABLED=1, mingw, локальная видимость библиотек
|
||||
- Makefile и нафига он нужен
|
||||
- Таргеты: Убираем бардак за собой - distclean, build
|
||||
- Автоматическое тестирование, testify. Не ленитесь писать тесты!
|
||||
- Деплой/публикация после сборки
|
@ -2,8 +2,20 @@
|
||||
|
||||
Плюсы использования нативной системы типов
|
||||
|
||||
- Примитивы типа Coords, Rect
|
||||
- Интерфейсы, type casting и переиспользование Blit
|
||||
|
||||
Минусы использования нативной системы типы
|
||||
|
||||
ECS - меняем бойлерплейт на относительное снижение связности
|
||||
- Структура структуры (эм..) иммутабельна
|
||||
|
||||
Минутка рекламы gogue
|
||||
ECS - достигаем относительного снижения связности ценой чудовищного бойлерплейта
|
||||
|
||||
- Делаем динамическую систему типов там и только там где нам надо
|
||||
- Возможность физически впихнуть в кеш проца все актуальные данные
|
||||
- Да, это много копипасты. Но мы используем статически типизированный язык, а не питон, что вы хотели?
|
||||
|
||||
Минутка рекламы gogue
|
||||
|
||||
- Толковый туториал, неплохой код
|
||||
- Пользуйтесь и продвигайте
|
Loading…
x
Reference in New Issue
Block a user