diff --git a/README.md b/README.md index 44a0ba3..187b513 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,8 @@ While it runs, there are a couple of commands you can use: * page down: slow down * Mouse wheel: zoom in or out * move mouse while middle mouse button pressed: move canvas -* escape: reset to 1:1 zoom +* r: reset to 1:1 zoom +* escape: open menu * s: save game state to file (can be loaded with -l) * c: enter copy mode. Mark a rectangle with the mouse, when you release the mous button it is being saved to an RLE file diff --git a/TODO.md b/TODO.md index a7a9c00..b947af4 100644 --- a/TODO.md +++ b/TODO.md @@ -1,2 +1,5 @@ -- gird lines option has no effect - add all other options like size etc +- clear screen disabled leads to zoom artifacts when zooming out and + canvas is smaller than screen workaround: using screen copy in + game.Screen. Play copies screen to it and Menu draws it to screen + before drawing the menus diff --git a/config.go b/config.go index 948aef6..ecdb97d 100644 --- a/config.go +++ b/config.go @@ -19,7 +19,7 @@ type Config struct { ScreenWidth, ScreenHeight int TPG int // ticks per generation/game speed, 1==max Debug, Empty, Invert, Paused, Markmode bool // game modi - ShowEvolution, NoGrid, RunOneStep bool // flags + ShowEvolution, ShowGrid, RunOneStep bool // flags Rule *Rule // which rule to use, default: B3/S23 RLE *rle.RLE // loaded GOL pattern from RLE file Statefile string // load game state from it if non-nil @@ -27,8 +27,9 @@ type Config struct { Wrap bool // wether wraparound mode is in place or not ShowVersion bool UseShader bool // to use a shader to render alife cells - Restart bool + Restart, RestartGrid, RestartCache bool StartWithMenu bool + Zoomfactor int // for internal profiling ProfileFile string @@ -40,6 +41,12 @@ const ( VERSION = "v0.0.7" Alive = 1 Dead = 0 + + DEFAULT_WIDTH = 600 + DEFAULT_HEIGHT = 400 + DEFAULT_CELLSIZE = 4 + DEFAULT_ZOOMFACTOR = 150 // FIXME, doesn't work? + DEFAULT_GEOM = "640x384" ) // parse given window geometry and adjust game settings according to it @@ -47,6 +54,7 @@ func (config *Config) ParseGeom(geom string) error { if geom == "" { config.ScreenWidth = config.Cellsize * config.Width config.ScreenHeight = config.Cellsize * config.Height + config.Zoomfactor = 0 return nil } @@ -66,16 +74,23 @@ func (config *Config) ParseGeom(geom string) error { return errors.New("failed to parse height, expecting integer") } - // adjust dimensions, account for grid width+height so that cells - // fit into window - config.ScreenWidth = width - (width % config.Width) - config.ScreenHeight = height - (height % config.Height) + /* + // adjust dimensions, account for grid width+height so that cells + // fit into window + config.ScreenWidth = width - (width % config.Width) + config.ScreenHeight = height - (height % config.Height) - if config.ScreenWidth == 0 || config.ScreenHeight == 0 { - return errors.New("the number of requested cells don't fit into the requested window size") - } + if config.ScreenWidth == 0 || config.ScreenHeight == 0 { + return errors.New("the number of requested cells don't fit into the requested window size") + } + */ - config.Cellsize = config.ScreenWidth / config.Width + config.ScreenWidth = width + config.ScreenHeight = height + + //config.Cellsize = config.ScreenWidth / config.Width + config.Cellsize = DEFAULT_CELLSIZE + config.Zoomfactor = DEFAULT_ZOOMFACTOR repr.Println(config) return nil @@ -160,10 +175,10 @@ func ParseCommandline() (*Config, error) { ) // commandline params, most configure directly config flags - pflag.IntVarP(&config.Width, "width", "W", 40, "grid width in cells") - pflag.IntVarP(&config.Height, "height", "H", 40, "grid height in cells") + pflag.IntVarP(&config.Width, "width", "W", DEFAULT_WIDTH, "grid width in cells") + pflag.IntVarP(&config.Height, "height", "H", DEFAULT_HEIGHT, "grid height in cells") pflag.IntVarP(&config.Cellsize, "cellsize", "c", 8, "cell size in pixels") - pflag.StringVarP(&geom, "geom", "g", "", "window geometry in WxH in pixels, overturns -c") + pflag.StringVarP(&geom, "geom", "G", DEFAULT_GEOM, "window geometry in WxH in pixels, overturns -c") pflag.IntVarP(&config.Density, "density", "D", 10, "density of random cells") pflag.IntVarP(&config.TPG, "ticks-per-generation", "t", 10, @@ -176,7 +191,7 @@ func ParseCommandline() (*Config, error) { pflag.BoolVarP(&config.ShowVersion, "version", "v", false, "show version") pflag.BoolVarP(&config.Paused, "paused", "p", false, "do not start simulation (use space to start)") pflag.BoolVarP(&config.Debug, "debug", "d", false, "show debug info") - pflag.BoolVarP(&config.NoGrid, "nogrid", "n", false, "do not draw grid lines") + pflag.BoolVarP(&config.ShowGrid, "show-grid", "g", true, "draw grid lines") pflag.BoolVarP(&config.Empty, "empty", "e", false, "start with an empty screen") pflag.BoolVarP(&config.Invert, "invert", "i", false, "invert colors (dead cell: black)") pflag.BoolVarP(&config.ShowEvolution, "show-evolution", "s", false, "show evolution traces") @@ -219,8 +234,10 @@ func (config *Config) ToggleDebugging() { func (config *Config) ToggleInvert() { config.Invert = !config.Invert + config.RestartCache = true } func (config *Config) ToggleGridlines() { - config.NoGrid = !config.NoGrid + config.ShowGrid = !config.ShowGrid + config.RestartCache = true } diff --git a/game.go b/game.go index 80cf691..38d97d0 100644 --- a/game.go +++ b/game.go @@ -10,6 +10,7 @@ type Game struct { CurrentScene SceneName Config *Config Scale float32 + Screen *ebiten.Image } func NewGame(config *Config, startscene SceneName) *Game { @@ -30,7 +31,8 @@ func NewGame(config *Config, startscene SceneName) *Game { ebiten.SetWindowSize(game.ScreenWidth, game.ScreenHeight) ebiten.SetWindowTitle("golsky - conway's game of life") ebiten.SetWindowResizingMode(ebiten.WindowResizingModeEnabled) - ebiten.SetScreenClearedEveryFrame(false) + + game.Screen = ebiten.NewImage(game.ScreenWidth, game.ScreenHeight) return game } @@ -48,6 +50,8 @@ func (game *Game) Update() error { scene := game.GetCurrentScene() scene.Update() + //ebiten.SetScreenClearedEveryFrame(scene.Clearscreen()) + next := scene.GetNext() if next != game.CurrentScene { @@ -63,5 +67,6 @@ func (game *Game) Update() error { func (game *Game) Draw(screen *ebiten.Image) { scene := game.GetCurrentScene() + ebiten.SetScreenClearedEveryFrame(scene.Clearscreen()) scene.Draw(screen) } diff --git a/scene-menu.go b/scene-menu.go index 4c03b58..2732f76 100644 --- a/scene-menu.go +++ b/scene-menu.go @@ -62,6 +62,12 @@ func (scene *SceneMenu) Update() error { } func (scene *SceneMenu) Draw(screen *ebiten.Image) { + op := &ebiten.DrawImageOptions{} + op.GeoM.Reset() + op.GeoM.Translate(0, 0) + + screen.DrawImage(scene.Game.Screen, op) + scene.Ui.Draw(screen) } @@ -95,13 +101,14 @@ func (scene *SceneMenu) Init() { separator1 := NewSeparator() separator2 := NewSeparator() + separator3 := NewSeparator() - cancel := NewMenuButton("Close", + cancel := NewMenuButton("Close Window", func(args *widget.ButtonClickedEventArgs) { scene.SetNext(Play) }) - quit := NewMenuButton("Quit", + quit := NewMenuButton("Exit Golsky", func(args *widget.ButtonClickedEventArgs) { os.Exit(0) }) @@ -113,6 +120,7 @@ func (scene *SceneMenu) Init() { rowContainer.AddChild(copy) rowContainer.AddChild(separator2) rowContainer.AddChild(cancel) + rowContainer.AddChild(separator3) rowContainer.AddChild(quit) scene.Ui = &ebitenui.UI{ diff --git a/scene-options.go b/scene-options.go index 0b6ca67..5ee1a8b 100644 --- a/scene-options.go +++ b/scene-options.go @@ -63,6 +63,17 @@ func (scene *SceneOptions) Draw(screen *ebiten.Image) { scene.Ui.Draw(screen) } +func (scene *SceneOptions) SetInitialValue(w *widget.LabeledCheckbox, value bool) { + var intval int + if value { + intval = 1 + } + + w.SetState( + widget.WidgetState(intval), + ) +} + func (scene *SceneOptions) Init() { rowContainer := NewRowContainer("Options") @@ -75,23 +86,19 @@ func (scene *SceneOptions) Init() { func(args *widget.CheckboxChangedEventArgs) { scene.Config.ToggleDebugging() }) - var debug int - if scene.Config.Debug { - debug = 1 - } - debugging.SetState( - widget.WidgetState(debug), - ) + scene.SetInitialValue(debugging, scene.Config.Debug) invert := NewCheckbox("Invert", func(args *widget.CheckboxChangedEventArgs) { scene.Config.Invert = true }) + scene.SetInitialValue(invert, scene.Config.Invert) gridlines := NewCheckbox("Show grid lines", func(args *widget.CheckboxChangedEventArgs) { scene.Config.ToggleGridlines() }) + scene.SetInitialValue(gridlines, scene.Config.ShowGrid) separator := NewSeparator() diff --git a/scene-play.go b/scene-play.go index ffa5938..65992be 100644 --- a/scene-play.go +++ b/scene-play.go @@ -200,10 +200,6 @@ func (scene *ScenePlay) CheckInput() { scene.SaveState() } - if inpututil.IsKeyJustPressed(ebiten.KeyR) { - scene.Reset() - } - if inpututil.IsKeyJustPressed(ebiten.KeyD) { scene.Config.Debug = !scene.Config.Debug } @@ -267,7 +263,7 @@ func (scene *ScenePlay) CheckDraggingInput() { scene.Camera.ZoomFactor += (int(dy) * 5) } - if inpututil.IsKeyJustPressed(ebiten.KeyEscape) { + if inpututil.IsKeyJustPressed(ebiten.KeyR) { scene.Camera.Reset() } @@ -372,6 +368,13 @@ func (scene *ScenePlay) Update() error { return nil } + if scene.Config.RestartCache { + scene.Config.RestartCache = false + scene.InitTiles() + scene.InitCache() + return nil + } + scene.CheckInput() scene.CheckDraggingInput() scene.CheckMarkInput() @@ -390,8 +393,6 @@ func (scene *ScenePlay) ToggleCellOnCursorPos(alive int64) { x := int(worldX) / scene.Config.Cellsize y := int(worldY) / scene.Config.Cellsize - //fmt.Printf("cell at %d,%d\n", x, y) - if x > -1 && y > -1 { scene.Grids[scene.Index].Data[y][x] = alive scene.History.Data[y][x] = 1 @@ -451,6 +452,11 @@ func (scene *ScenePlay) Draw(screen *ebiten.Image) { scene.Camera.Render(scene.World, screen) scene.DrawDebug(screen) + + op.GeoM.Reset() + op.GeoM.Translate(0, 0) + + scene.Game.Screen.DrawImage(screen, op) } func (scene *ScenePlay) DrawMark(screen *ebiten.Image) { @@ -477,9 +483,9 @@ func (scene *ScenePlay) DrawDebug(screen *ebiten.Image) { } debug := fmt.Sprintf( - "FPS: %0.2f, TPG: %d, Mem: %0.2fMB, Gen: %d, Scale: %.02f %s", + "FPS: %0.2f, TPG: %d, Mem: %0.2fMB, Gen: %d, Scale: %.02f, Z: %d %s", ebiten.ActualTPS(), scene.TPG, GetMem(), scene.Generations, - scene.Game.Scale, paused) + scene.Game.Scale, scene.Camera.ZoomFactor, paused) FontRenderer.Renderer.SetSizePx(10 + int(scene.Game.Scale*10)) FontRenderer.Renderer.SetTarget(screen) @@ -504,10 +510,10 @@ func (scene *ScenePlay) InitPattern() { func (scene *ScenePlay) InitCache() { op := &ebiten.DrawImageOptions{} - if scene.Config.NoGrid { - scene.Cache.Fill(scene.White) - } else { + if scene.Config.ShowGrid { scene.Cache.Fill(scene.Grey) + } else { + scene.Cache.Fill(scene.White) } for y := 0; y < scene.Config.Height; y++ { @@ -621,6 +627,10 @@ func (scene *ScenePlay) Init() { scene.TicksElapsed = 0 scene.LastCursorPos = make([]int, 2) + + if scene.Config.Zoomfactor < 0 || scene.Config.Zoomfactor > 0 { + scene.Camera.ZoomFactor = scene.Config.Zoomfactor + } } // count the living neighbors of a cell