ui: Add IsRunnableInBackground / SetRunnableInBackground (#272)

This commit is contained in:
Hajime Hoshi 2017-08-02 23:37:50 +09:00
parent bb5036b3e1
commit f0d47312c4
6 changed files with 105 additions and 25 deletions

View File

@ -58,7 +58,7 @@ type Input struct {
} }
func (i *Input) update() { func (i *Input) update() {
for _, key := range []ebiten.Key{ebiten.KeyP, ebiten.KeyS, ebiten.KeyX, ebiten.KeyZ} { for _, key := range []ebiten.Key{ebiten.KeyP, ebiten.KeyS, ebiten.KeyX, ebiten.KeyZ, ebiten.KeyB} {
if !ebiten.IsKeyPressed(key) { if !ebiten.IsKeyPressed(key) {
i.keyStates[key] = 0 i.keyStates[key] = 0
} else { } else {
@ -170,6 +170,10 @@ func (p *Player) update() error {
p.updatePlayPause() p.updatePlayPause()
p.updateSE() p.updateSE()
p.updateVolume() p.updateVolume()
if p.input.isKeyTriggered(ebiten.KeyB) {
b := ebiten.IsRunnableInBackground()
ebiten.SetRunnableInBackground(!b)
}
if err := p.audioContext.Update(); err != nil { if err := p.audioContext.Update(); err != nil {
return err return err
} }
@ -262,6 +266,7 @@ func (p *Player) draw(screen *ebiten.Image) {
Press S to toggle Play/Pause Press S to toggle Play/Pause
Press P to play SE Press P to play SE
Press Z or X to change volume of the music Press Z or X to change volume of the music
Press B to switch the run-in-background state
%s`, ebiten.CurrentFPS(), currentTimeStr) %s`, ebiten.CurrentFPS(), currentTimeStr)
ebitenutil.DebugPrint(screen, msg) ebitenutil.DebugPrint(screen, msg)
} }

View File

@ -21,6 +21,7 @@ import (
"image/color" "image/color"
_ "image/jpeg" _ "image/jpeg"
"log" "log"
"math"
"github.com/hajimehoshi/ebiten" "github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil" "github.com/hajimehoshi/ebiten/ebitenutil"
@ -41,7 +42,9 @@ var (
ebiten.KeyRight: 0, ebiten.KeyRight: 0,
ebiten.KeyS: 0, ebiten.KeyS: 0,
ebiten.KeyF: 0, ebiten.KeyF: 0,
ebiten.KeyB: 0,
} }
count = 0
) )
func update(screen *ebiten.Image) error { func update(screen *ebiten.Image) error {
@ -56,6 +59,7 @@ func update(screen *ebiten.Image) error {
d := int(32 / screenScale) d := int(32 / screenScale)
screenWidth, screenHeight := screen.Size() screenWidth, screenHeight := screen.Size()
fullscreen := ebiten.IsFullscreen() fullscreen := ebiten.IsFullscreen()
runnableInBackground := ebiten.IsRunnableInBackground()
if keyStates[ebiten.KeyUp] == 1 { if keyStates[ebiten.KeyUp] == 1 {
screenHeight += d screenHeight += d
@ -88,9 +92,15 @@ func update(screen *ebiten.Image) error {
if keyStates[ebiten.KeyF] == 1 { if keyStates[ebiten.KeyF] == 1 {
fullscreen = !fullscreen fullscreen = !fullscreen
} }
if keyStates[ebiten.KeyB] == 1 {
runnableInBackground = !runnableInBackground
}
ebiten.SetScreenSize(screenWidth, screenHeight) ebiten.SetScreenSize(screenWidth, screenHeight)
ebiten.SetScreenScale(screenScale) ebiten.SetScreenScale(screenScale)
ebiten.SetFullscreen(fullscreen) ebiten.SetFullscreen(fullscreen)
ebiten.SetRunnableInBackground(runnableInBackground)
count++
if ebiten.IsRunningSlowly() { if ebiten.IsRunningSlowly() {
return nil return nil
@ -101,12 +111,16 @@ func update(screen *ebiten.Image) error {
w2, h2 := screen.Size() w2, h2 := screen.Size()
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(-w+w2)/2, float64(-h+h2)/2) op.GeoM.Translate(float64(-w+w2)/2, float64(-h+h2)/2)
dx := math.Cos(2*math.Pi*float64(count)/360) * 10
dy := math.Sin(2*math.Pi*float64(count)/360) * 10
op.GeoM.Translate(dx, dy)
screen.DrawImage(gophersImage, op) screen.DrawImage(gophersImage, op)
x, y := ebiten.CursorPosition() x, y := ebiten.CursorPosition()
msg := fmt.Sprintf(`Press arrow keys to change the window size msg := fmt.Sprintf(`Press arrow keys to change the window size
Press S key to change the window scale Press S key to change the window scale
Press F key to change the fullscreen state Press F key to change the fullscreen state
Press B key to change the run-in-background state
Cursor: (%d, %d) Cursor: (%d, %d)
FPS: %0.2f`, x, y, ebiten.CurrentFPS()) FPS: %0.2f`, x, y, ebiten.CurrentFPS())
ebitenutil.DebugPrint(screen, msg) ebitenutil.DebugPrint(screen, msg)

View File

@ -30,22 +30,23 @@ import (
) )
type userInterface struct { type userInterface struct {
title string title string
window *glfw.Window window *glfw.Window
width int width int
height int height int
scale float64 scale float64
deviceScale float64 deviceScale float64
glfwScale float64 glfwScale float64
fullscreen bool fullscreen bool
fullscreenScale float64 fullscreenScale float64
funcs chan func() funcs chan func()
running bool running bool
sizeChanged bool sizeChanged bool
origPosX int origPosX int
origPosY int origPosY int
initFullscreen bool initFullscreen bool
m sync.Mutex runnableInBackground bool
m sync.Mutex
} }
var ( var (
@ -133,6 +134,19 @@ func (u *userInterface) setInitFullscreen(initFullscreen bool) {
u.m.Unlock() u.m.Unlock()
} }
func (u *userInterface) isRunnableInBackground() bool {
u.m.Lock()
v := u.runnableInBackground
u.m.Unlock()
return v
}
func (u *userInterface) setRunnableInBackground(runnableInBackground bool) {
u.m.Lock()
u.runnableInBackground = runnableInBackground
u.m.Unlock()
}
func (u *userInterface) runOnMainThread(f func() error) error { func (u *userInterface) runOnMainThread(f func() error) error {
if u.funcs == nil { if u.funcs == nil {
// already closed // already closed
@ -213,6 +227,14 @@ func SetFullscreen(fullscreen bool) {
}) })
} }
func SetRunnableInBackground(runnableInBackground bool) {
currentUI.setRunnableInBackground(runnableInBackground)
}
func IsRunnableInBackground() bool {
return currentUI.isRunnableInBackground()
}
func ScreenOffset() (float64, float64) { func ScreenOffset() (float64, float64) {
u := currentUI u := currentUI
if !u.isRunning() { if !u.isRunning() {
@ -372,7 +394,7 @@ func (u *userInterface) update(g GraphicsContext) error {
_ = u.runOnMainThread(func() error { _ = u.runOnMainThread(func() error {
u.pollEvents() u.pollEvents()
for u.window.GetAttrib(glfw.Focused) == 0 { for !u.isRunnableInBackground() && u.window.GetAttrib(glfw.Focused) == 0 {
// Wait for an arbitrary period to avoid busy loop. // Wait for an arbitrary period to avoid busy loop.
time.Sleep(time.Second / 60) time.Sleep(time.Second / 60)
u.pollEvents() u.pollEvents()

View File

@ -27,10 +27,11 @@ import (
var canvas *js.Object var canvas *js.Object
type userInterface struct { type userInterface struct {
width int width int
height int height int
scale float64 scale float64
fullscreen bool fullscreen bool
runnableInBackground bool
deviceScale float64 deviceScale float64
sizeChanged bool sizeChanged bool
@ -67,6 +68,14 @@ func IsFullscreen() bool {
return currentUI.fullscreen return currentUI.fullscreen
} }
func SetRunnableInBackground(runnableInBackground bool) {
currentUI.runnableInBackground = runnableInBackground
}
func IsRunnableInBackground() bool {
return currentUI.runnableInBackground
}
func ScreenOffset() (float64, float64) { func ScreenOffset() (float64, float64) {
return 0, 0 return 0, 0
} }
@ -104,7 +113,7 @@ func (u *userInterface) actualScreenScale() float64 {
} }
func (u *userInterface) update(g GraphicsContext) error { func (u *userInterface) update(g GraphicsContext) error {
if !u.windowFocus { if !u.runnableInBackground && !u.windowFocus {
return nil return nil
} }
if opengl.GetContext().IsContextLost() { if opengl.GetContext().IsContextLost() {

View File

@ -118,12 +118,20 @@ func SetCursorVisibility(visibility bool) {
// Do nothing // Do nothing
} }
func SetFullscreen(fullscreen bool) bool { func SetFullscreen(fullscreen bool) {
// Do nothing
}
func IsFullscreen() bool {
// Do nothing // Do nothing
return false return false
} }
func IsFullscreen() bool { func SetRunnableInBackground(runnableInBackground bool) {
// Do nothing
}
func IsRunnableInBackground() bool {
// Do nothing // Do nothing
return false return false
} }

22
run.go
View File

@ -178,3 +178,25 @@ func IsFullscreen() bool {
func SetFullscreen(fullscreen bool) { func SetFullscreen(fullscreen bool) {
ui.SetFullscreen(fullscreen) ui.SetFullscreen(fullscreen)
} }
// IsRunnableInBackground returns a boolean value indicating whether the game runs even in background.
//
// This function is concurrent-safe.
func IsRunnableInBackground() bool {
return ui.IsRunnableInBackground()
}
// SetRunnableInBackground sets the state if the game runs even in background.
//
// If the given value is true, the game runs in background e.g. when losing focus.
// The initial state is false.
//
// Known issue: On browsers, even if the state is on, the game doesn't run in background tabs.
// This is because browsers throttles background tabs not to often update.
//
// SetRunnableInBackground doesn't work on mobiles so far.
//
// This function is concurrent-safe.
func SetRunnableInBackground(runnableInBackground bool) {
ui.SetRunnableInBackground(runnableInBackground)
}