ebiten: add Termination for a regular termination (#2272)

Closes #2266
This commit is contained in:
Terra Brown 2022-08-27 08:33:40 -04:00 committed by GitHub
parent 0587a45c61
commit de35a5a6f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 48 additions and 88 deletions

View File

@ -19,7 +19,6 @@ package main
import (
"bytes"
"errors"
"fmt"
"image"
"image/color"
@ -41,7 +40,6 @@ import (
var (
gophersImage *ebiten.Image
mplusFont font.Face
regularTermination = errors.New("regular termination")
)
func init() {
@ -86,7 +84,7 @@ func (g *Game) Update() error {
}
}
if runtime.GOOS != "js" && ebiten.IsKeyPressed(ebiten.KeyQ) {
return regularTermination
return ebiten.Termination
}
return nil
}
@ -130,7 +128,7 @@ func main() {
ebiten.SetFullscreen(true)
ebiten.SetWindowTitle("Fullscreen (Ebiten Demo)")
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
log.Fatal(err)
}
}

View File

@ -317,8 +317,6 @@ func outputKeyRectsGo(k map[ebiten.Key]image.Rectangle) error {
})
}
var regularTermination = errors.New("regular termination")
type game struct {
rects map[ebiten.Key]image.Rectangle
}
@ -329,7 +327,7 @@ func (g *game) Update() error {
if err != nil {
return err
}
return regularTermination
return ebiten.Termination
}
func (g *game) Draw(_ *ebiten.Image) {
@ -341,7 +339,7 @@ func (g *game) Layout(outw, outh int) (int, int) {
func main() {
g := &game{}
if err := ebiten.RunGame(g); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(g); err != nil {
log.Fatal(err)
}
if err := outputKeyRectsGo(g.rects); err != nil {

View File

@ -19,7 +19,6 @@ package main
import (
"bytes"
"errors"
"fmt"
"image"
_ "image/png"
@ -139,15 +138,13 @@ func (g *Game) init() {
}
}
var regularTermination = errors.New("regular termination")
func (g *Game) Update() error {
if !g.inited {
g.init()
}
if inpututil.IsKeyJustPressed(ebiten.KeyQ) {
return regularTermination
return ebiten.Termination
}
// Decrease the number of the sprites.
@ -202,7 +199,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
func main() {
ebiten.SetFullscreen(true)
ebiten.SetWindowTitle("Sprites HD (Ebiten Demo)")
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
log.Fatal(err)
}
}

View File

@ -18,7 +18,6 @@
package main
import (
"errors"
"log"
"github.com/hajimehoshi/ebiten/v2"
@ -26,8 +25,6 @@ import (
"github.com/hajimehoshi/ebiten/v2/inpututil"
)
var regularTermination = errors.New("regular termination")
type Game struct {
windowClosingHandled bool
}
@ -38,7 +35,7 @@ func (g *Game) Update() error {
}
if g.windowClosingHandled {
if inpututil.IsKeyJustPressed(ebiten.KeyY) {
return regularTermination
return ebiten.Termination
}
if inpututil.IsKeyJustPressed(ebiten.KeyN) {
g.windowClosingHandled = false
@ -62,7 +59,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
func main() {
ebiten.SetWindowClosingHandled(true)
ebiten.SetWindowTitle("Window Closing (Ebiten Demo)")
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
log.Fatal(err)
}
}

View File

@ -15,7 +15,6 @@
package buffered_test
import (
"errors"
"image/color"
"os"
"testing"
@ -34,8 +33,6 @@ func runOnMainThread(f func()) {
<-ch
}
var regularTermination = errors.New("regular termination")
type game struct {
m *testing.M
endCh chan struct{}
@ -47,7 +44,7 @@ func (g *game) Update() error {
case f := <-mainCh:
f()
case <-g.endCh:
return regularTermination
return ebiten.Termination
}
return nil
}
@ -73,7 +70,7 @@ func TestMain(m *testing.M) {
m: m,
endCh: endCh,
}
if err := ebiten.RunGame(g); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(g); err != nil {
panic(err)
}

View File

@ -18,15 +18,12 @@
package main
import (
"errors"
"fmt"
"image/color"
"github.com/hajimehoshi/ebiten/v2"
)
var regularTermination = errors.New("regular termination")
type Game struct {
dst *ebiten.Image
phase int
@ -94,7 +91,7 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
return fmt.Errorf("phase: %d, got: %v, want: %v", g.phase, got, want)
}
return regularTermination
return ebiten.Termination
}
return nil
@ -108,7 +105,7 @@ func (g *Game) Layout(width, height int) (int, int) {
}
func main() {
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}

View File

@ -18,15 +18,12 @@
package main
import (
"errors"
"image/color"
"time"
"github.com/hajimehoshi/ebiten/v2"
)
var regularTermination = errors.New("regular termination")
type Game struct {
count int
}
@ -34,7 +31,7 @@ type Game struct {
func (g *Game) Update() error {
g.count++
if g.count >= 2 {
return regularTermination
return ebiten.Termination
}
return nil
}
@ -62,7 +59,7 @@ func (g *Game) Layout(width, height int) (int, int) {
}
func main() {
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}

View File

@ -18,15 +18,12 @@
package main
import (
"errors"
"fmt"
"image/color"
"github.com/hajimehoshi/ebiten/v2"
)
var regularTermination = errors.New("regular termination")
type Game struct {
count int
}
@ -34,7 +31,7 @@ type Game struct {
func (g *Game) Update() error {
g.count++
if g.count == 16 {
return regularTermination
return ebiten.Termination
}
w, h := 256+g.count, 256+g.count
@ -72,7 +69,7 @@ func (g *Game) Layout(width, height int) (int, int) {
}
func main() {
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}

View File

@ -17,13 +17,7 @@
package main
import (
"errors"
"github.com/hajimehoshi/ebiten/v2"
)
var regularTermination = errors.New("regular termination")
import "github.com/hajimehoshi/ebiten/v2"
type Game struct {
count int
@ -39,7 +33,7 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
`
g.count++
if g.count == 16 {
return regularTermination
return ebiten.Termination
}
if g.count < 8 {
@ -64,7 +58,7 @@ func (g *Game) Layout(width, height int) (int, int) {
}
func main() {
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}

View File

@ -18,7 +18,6 @@
package main
import (
"errors"
"image"
"image/color"
"math"
@ -31,8 +30,6 @@ import (
"golang.org/x/image/font/opentype"
)
var regularTermination = errors.New("regular termination")
var (
emptyImage = ebiten.NewImage(3, 3)
debugCircleImage *ebiten.Image
@ -62,7 +59,7 @@ type Game struct {
func (g *Game) Update() error {
g.counter++
if g.counter > 16 {
return regularTermination
return ebiten.Termination
}
return nil
}
@ -87,7 +84,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeigh
}
func main() {
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}

View File

@ -18,15 +18,12 @@
package main
import (
"errors"
"fmt"
"image/color"
"github.com/hajimehoshi/ebiten/v2"
)
var regularTermination = errors.New("regular termination")
var srcInit *ebiten.Image
func init() {
@ -59,7 +56,7 @@ type Game struct {
func (g *Game) Update() error {
g.count++
if g.count == 16 {
return regularTermination
return ebiten.Termination
}
return nil
}
@ -115,7 +112,7 @@ func (g *Game) Layout(width, height int) (int, int) {
}
func main() {
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}

View File

@ -18,7 +18,6 @@
package main
import (
"errors"
"fmt"
"image/color"
@ -26,8 +25,6 @@ import (
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
var regularTermination = errors.New("regular termination")
var (
baseImage *ebiten.Image
derivedImage *ebiten.Image
@ -58,7 +55,7 @@ type Game struct {
func (g *Game) Update() error {
g.count++
if g.count == 16 {
return regularTermination
return ebiten.Termination
}
return nil
}
@ -86,7 +83,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
func main() {
ebiten.SetWindowTitle("Test")
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}

View File

@ -17,11 +17,7 @@
package main
import (
"errors"
"github.com/hajimehoshi/ebiten/v2"
)
import "github.com/hajimehoshi/ebiten/v2"
func init() {
s, err := ebiten.NewShader([]byte(`
@ -36,8 +32,6 @@ func Fragment(position vec4, texCoord vec2, color vec4) vec4 {
s.Dispose()
}
var regularTermination = errors.New("regular termination")
type Game struct {
counter int
}
@ -45,7 +39,7 @@ type Game struct {
func (g *Game) Update() error {
g.counter++
if g.counter > 1 {
return regularTermination
return ebiten.Termination
}
return nil
}
@ -59,7 +53,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
func main() {
// Run a game loop at least for one frame to ensure the shader disposed.
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}

View File

@ -18,7 +18,6 @@
package main
import (
"errors"
"fmt"
"image"
"image/color"
@ -27,8 +26,6 @@ import (
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)
var regularTermination = errors.New("regular termination")
var (
baseImage *ebiten.Image
derivedImage *ebiten.Image
@ -59,7 +56,7 @@ type Game struct {
func (g *Game) Update() error {
g.count++
if g.count == 16 {
return regularTermination
return ebiten.Termination
}
return nil
}
@ -87,7 +84,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
func main() {
ebiten.SetWindowTitle("Test")
if err := ebiten.RunGame(&Game{}); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(&Game{}); err != nil {
panic(err)
}
}

View File

@ -15,15 +15,12 @@
package testing
import (
"errors"
"os"
"testing"
"github.com/hajimehoshi/ebiten/v2"
)
var regularTermination = errors.New("regular termination")
type game struct {
m *testing.M
code int
@ -31,7 +28,7 @@ type game struct {
func (g *game) Update() error {
g.code = g.m.Run()
return regularTermination
return ebiten.Termination
}
func (*game) Draw(*ebiten.Image) {
@ -46,7 +43,7 @@ func MainWithRunLoop(m *testing.M) {
g := &game{
m: m,
}
if err := ebiten.RunGame(g); err != nil && !errors.Is(err, regularTermination) {
if err := ebiten.RunGame(g); err != nil {
panic(err)
}
os.Exit(g.code)

23
run.go
View File

@ -15,6 +15,7 @@
package ebiten
import (
"errors"
"sync/atomic"
"github.com/hajimehoshi/ebiten/v2/internal/clock"
@ -45,6 +46,10 @@ type Game interface {
//
// After the first frame, Update might not be called or might be called once
// or more for one frame. The frequency is determined by the current TPS (tick-per-second).
//
// If the error returned is nil, game execution proceeds normally.
// If the error returned is Termination, game execution halts, but does not return an error from RunGame.
// If the error returned is any other non-nil value, game execution halts and the error is returned from RunGame.
Update() error
// Draw draws the game screen by one frame.
@ -167,6 +172,9 @@ func (i *imageDumperGame) Layout(outsideWidth, outsideHeight int) (screenWidth,
return i.game.Layout(outsideWidth, outsideHeight)
}
// Termination is a special error which indicates Game termination without error.
var Termination = ui.RegularTermination
// RunGame starts the main loop and runs the game.
// game's Update function is called every tick to update the game logic.
// game's Draw function is called every frame to draw the screen.
@ -184,12 +192,12 @@ func (i *imageDumperGame) Layout(outsideWidth, outsideHeight int) (screenWidth,
// This is not related to framerate (display's refresh rate).
//
// RunGame returns error when 1) an error happens in the underlying graphics driver, 2) an audio error happens
// or 3) f returns an error. In the case of 3), RunGame returns the same error so far, but it is recommended to
// or 3) Update returns an error. In the case of 3), RunGame returns the same error so far, but it is recommended to
// use errors.Is when you check the returned error is the error you want, rather than comparing the values
// with == or != directly.
//
// If you want to terminate a game on desktops, it is totally fine to define your own error value, return it at
// Update, and check whether the returned error value from RunGame is the same as the value you defined.
// If you want to terminate a game on desktops, it is recommended to return Termination at Update, which will halt
// execution without returning an error value from RunGame.
//
// The size unit is device-independent pixel.
//
@ -202,9 +210,10 @@ func RunGame(game Game) error {
game: game,
})
if err := ui.Get().Run(g); err != nil {
if err == ui.RegularTermination {
if errors.Is(err, Termination) {
return nil
}
return err
}
return nil
@ -216,7 +225,7 @@ func isRunGameEnded() bool {
// ScreenSizeInFullscreen returns the size in device-independent pixels when the game is fullscreen.
// The adopted monitor is the 'current' monitor which the window belongs to.
// The returned value can be given to Run or SetSize function if the perfectly fit fullscreen is needed.
// The returned value can be given to SetSize function if the perfectly fit fullscreen is needed.
//
// On browsers, ScreenSizeInFullscreen returns the 'window' (global object) size, not 'screen' size.
// ScreenSizeInFullscreen's returning value is different from the actual screen size and this is a known issue (#2145).
@ -228,8 +237,8 @@ func isRunGameEnded() bool {
// the Game interface's Layout function instead. If you are making a not-fullscreen application but the application's
// behavior depends on the monitor size, ScreenSizeInFullscreen is useful.
//
// ScreenSizeInFullscreen must be called on the main thread before ebiten.Run, and is concurrent-safe after
// ebiten.Run.
// ScreenSizeInFullscreen must be called on the main thread before ebiten.RunGame, and is concurrent-safe after
// ebiten.RunGame.
func ScreenSizeInFullscreen() (int, int) {
return ui.Get().ScreenSizeInFullscreen()
}