ui: Auto scaling on browsers

This change forces Ebiten apps on browsers 'fullscreen' mode.
After this change, 'scale' value is no longer used on browsers.

Note that this breaks backward compatibility, but as long as the
game works in an iframe, this should not be problematic.

Fixes #960
This commit is contained in:
Hajime Hoshi 2019-10-14 23:32:38 +09:00
parent 726de29f36
commit 75721e6fc1
4 changed files with 46 additions and 42 deletions

View File

@ -25,7 +25,6 @@ import (
"log" "log"
"math" "math"
"math/rand" "math/rand"
"runtime"
"time" "time"
"github.com/golang/freetype/truetype" "github.com/golang/freetype/truetype"
@ -392,11 +391,6 @@ func (g *Game) drawGopher(screen *ebiten.Image) {
func main() { func main() {
g := NewGame() g := NewGame()
// On browsers, let's use fullscreen so that this is playable on any browsers.
// It is planned to ignore the given 'scale' apply fullscreen automatically on browsers (#571).
if runtime.GOARCH == "js" || runtime.GOOS == "js" {
ebiten.SetFullscreen(true)
}
if err := ebiten.Run(g.Update, screenWidth, screenHeight, 1, "Flappy Gopher (Ebiten Demo)"); err != nil { if err := ebiten.Run(g.Update, screenWidth, screenHeight, 1, "Flappy Gopher (Ebiten Demo)"); err != nil {
panic(err) panic(err)
} }

View File

@ -43,9 +43,9 @@ func init() {
} }
const ( const (
initScreenWidth = 320 initScreenWidth = 480
initScreenHeight = 240 initScreenHeight = 360
initScreenScale = 2 initScreenScale = 1
) )
var ( var (
@ -56,16 +56,16 @@ var (
func createRandomIconImage() image.Image { func createRandomIconImage() image.Image {
const size = 32 const size = 32
r := uint8(rand.Intn(0x100)) r := byte(rand.Intn(0x100))
g := uint8(rand.Intn(0x100)) g := byte(rand.Intn(0x100))
b := uint8(rand.Intn(0x100)) b := byte(rand.Intn(0x100))
img := image.NewNRGBA(image.Rect(0, 0, size, size)) img := image.NewNRGBA(image.Rect(0, 0, size, size))
for j := 0; j < size; j++ { for j := 0; j < size; j++ {
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
img.Pix[j*img.Stride+4*i] = r img.Pix[j*img.Stride+4*i] = r
img.Pix[j*img.Stride+4*i+1] = g img.Pix[j*img.Stride+4*i+1] = g
img.Pix[j*img.Stride+4*i+2] = b img.Pix[j*img.Stride+4*i+2] = b
img.Pix[j*img.Stride+4*i+3] = uint8(float64(i+j) / float64(2*size) * 0xff) img.Pix[j*img.Stride+4*i+3] = byte(float64(i+j) / float64(2*size) * 0xff)
} }
} }
@ -108,6 +108,8 @@ func update(screen *ebiten.Image) error {
screenScale = 2 screenScale = 2
case 2: case 2:
screenScale = 0.75 screenScale = 0.75
case 0:
// screenScale can be 0 on browsers or mobiles. Ignore this.
default: default:
panic("not reached") panic("not reached")
} }
@ -140,7 +142,9 @@ func update(screen *ebiten.Image) error {
} }
ebiten.SetScreenSize(screenWidth, screenHeight) ebiten.SetScreenSize(screenWidth, screenHeight)
ebiten.SetScreenScale(screenScale) if screenScale > 0 {
ebiten.SetScreenScale(screenScale)
}
ebiten.SetFullscreen(fullscreen) ebiten.SetFullscreen(fullscreen)
ebiten.SetRunnableInBackground(runnableInBackground) ebiten.SetRunnableInBackground(runnableInBackground)
ebiten.SetCursorVisible(cursorVisible) ebiten.SetCursorVisible(cursorVisible)
@ -173,11 +177,11 @@ func update(screen *ebiten.Image) error {
tpsStr = fmt.Sprintf("%d", t) tpsStr = fmt.Sprintf("%d", t)
} }
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 (only for desktops)
Press F key to switch the fullscreen state Press F key to switch the fullscreen state (only for desktops)
Press B key to switch the run-in-background state Press B key to switch the run-in-background state
Press C key to switch the cursor visibility Press C key to switch the cursor visibility
Press I key to change the window icon Press I key to change the window icon (only for desktops)
Press V key to switch vsync Press V key to switch vsync
Press T key to switch TPS (ticks per second) Press T key to switch TPS (ticks per second)
Cursor: (%d, %d) Cursor: (%d, %d)

View File

@ -33,7 +33,6 @@ type UserInterface struct {
width int width int
height int height int
scale float64 scale float64
fullscreen bool
runnableInBackground bool runnableInBackground bool
vsync bool vsync bool
@ -75,11 +74,11 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
} }
func (u *UserInterface) SetScreenSize(width, height int) { func (u *UserInterface) SetScreenSize(width, height int) {
u.setScreenSize(width, height, u.scale, u.fullscreen) u.setScreenSize(width, height)
} }
func (u *UserInterface) SetScreenScale(scale float64) { func (u *UserInterface) SetScreenScale(scale float64) {
u.setScreenSize(u.width, u.height, scale, u.fullscreen) u.scale = scale
} }
func (u *UserInterface) ScreenScale() float64 { func (u *UserInterface) ScreenScale() float64 {
@ -87,11 +86,11 @@ func (u *UserInterface) ScreenScale() float64 {
} }
func (u *UserInterface) SetFullscreen(fullscreen bool) { func (u *UserInterface) SetFullscreen(fullscreen bool) {
u.setScreenSize(u.width, u.height, u.scale, fullscreen) // Do nothing
} }
func (u *UserInterface) IsFullscreen() bool { func (u *UserInterface) IsFullscreen() bool {
return u.fullscreen return false
} }
func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) { func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) {
@ -164,9 +163,6 @@ func (u *UserInterface) DeviceScaleFactor() float64 {
} }
func (u *UserInterface) getScale() float64 { func (u *UserInterface) getScale() float64 {
if !u.fullscreen {
return u.scale
}
body := document.Get("body") body := document.Get("body")
bw := body.Get("clientWidth").Float() bw := body.Get("clientWidth").Float()
bh := body.Get("clientHeight").Float() bh := body.Get("clientHeight").Float()
@ -432,8 +428,10 @@ func init() {
} }
func (u *UserInterface) Run(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) error { func (u *UserInterface) Run(width, height int, scale float64, title string, context driver.UIContext, graphics driver.Graphics) error {
// scale is ignored.
document.Set("title", title) document.Set("title", title)
u.setScreenSize(width, height, scale, u.fullscreen) u.setScreenSize(width, height)
canvas.Call("focus") canvas.Call("focus")
ch := u.loop(context) ch := u.loop(context)
if runtime.GOARCH == "wasm" { if runtime.GOARCH == "wasm" {
@ -454,15 +452,12 @@ func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, tit
panic("js: RunWithoutMainLoop is not implemented") panic("js: RunWithoutMainLoop is not implemented")
} }
func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool { func (u *UserInterface) setScreenSize(width, height int) bool {
if u.width == width && u.height == height && if u.width == width && u.height == height {
u.scale == scale && fullscreen == u.fullscreen {
return false return false
} }
u.width = width u.width = width
u.height = height u.height = height
u.scale = scale
u.fullscreen = fullscreen
u.updateScreenSize() u.updateScreenSize()
return true return true
} }

33
run.go
View File

@ -94,13 +94,20 @@ var theUIContext atomic.Value
// The screen size is based on the given values (width and height). // The screen size is based on the given values (width and height).
// //
// A window size is based on the given values (width, height and scale). // A window size is based on the given values (width, height and scale).
// scale is used to enlarge the screen. //
// scale is used to enlarge the screen on desktops.
// scale is ignored on browsers or mobiles.
// Note that the actual screen is multiplied not only by the given scale but also // Note that the actual screen is multiplied not only by the given scale but also
// by the device scale on high-DPI display. // by the device scale on high-DPI display.
// If you pass inverse of the device scale, // If you pass inverse of the device scale,
// you can disable this automatical device scaling as a result. // you can disable this automatical device scaling as a result.
// You can get the device scale by DeviceScaleFactor function. // You can get the device scale by DeviceScaleFactor function.
// //
// On browsers, the scale is automatically adjusted.
// It is strongly recommended to use iframe if you embed an Ebiten application in your website.
//
// On mobiles, if you use ebitenmobile command, the scale is automatically adjusted.
//
// Run must be called on the main thread. // Run must be called on the main thread.
// Note that Ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread. // Note that Ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread.
// //
@ -203,7 +210,7 @@ func SetScreenSize(width, height int) {
uiDriver().SetScreenSize(width, height) uiDriver().SetScreenSize(width, height)
} }
// SetScreenScale changes the scale of the screen. // SetScreenScale changes the scale of the screen on desktops.
// //
// Note that the actual screen is multiplied not only by the given scale but also // Note that the actual screen is multiplied not only by the given scale but also
// by the device scale on high-DPI display. // by the device scale on high-DPI display.
@ -211,6 +218,14 @@ func SetScreenSize(width, height int) {
// you can disable this automatical device scaling as a result. // you can disable this automatical device scaling as a result.
// You can get the device scale by DeviceScaleFactor function. // You can get the device scale by DeviceScaleFactor function.
// //
// On browsers, SetScreenScale saves the given value and affects the returned value of ScreenScale,
// but does not affect actual rendering.
//
// On mobiles, SetScreenScale works, but usually the user doesn't have to call this.
// Instead, ebitenmobile calls this automatically.
//
// SetScreenScale panics if scale is not a positive number.
//
// SetScreenScale is concurrent-safe. // SetScreenScale is concurrent-safe.
func SetScreenScale(scale float64) { func SetScreenScale(scale float64) {
if scale <= 0 { if scale <= 0 {
@ -221,6 +236,8 @@ func SetScreenScale(scale float64) {
// ScreenScale returns the current screen scale. // ScreenScale returns the current screen scale.
// //
// ScreenScale returns a value set by SetScreenScale or Run on browsers.
//
// If Run is not called, this returns 0. // If Run is not called, this returns 0.
// //
// ScreenScale is concurrent-safe. // ScreenScale is concurrent-safe.
@ -255,14 +272,14 @@ func SetCursorVisibility(visible bool) {
// IsFullscreen returns a boolean value indicating whether // IsFullscreen returns a boolean value indicating whether
// the current mode is fullscreen or not. // the current mode is fullscreen or not.
// //
// IsFullscreen always returns false on mobiles. // IsFullscreen always returns false on browsers and mobiles.
// //
// IsFullscreen is concurrent-safe. // IsFullscreen is concurrent-safe.
func IsFullscreen() bool { func IsFullscreen() bool {
return uiDriver().IsFullscreen() return uiDriver().IsFullscreen()
} }
// SetFullscreen changes the current mode to fullscreen or not. // SetFullscreen changes the current mode to fullscreen or not on desktops.
// //
// On fullscreen mode, the game screen is automatically enlarged // On fullscreen mode, the game screen is automatically enlarged
// to fit with the monitor. The current scale value is ignored. // to fit with the monitor. The current scale value is ignored.
@ -270,13 +287,7 @@ func IsFullscreen() bool {
// On desktops, Ebiten uses 'windowed' fullscreen mode, which doesn't change // On desktops, Ebiten uses 'windowed' fullscreen mode, which doesn't change
// your monitor's resolution. // your monitor's resolution.
// //
// On browsers, the game screen is resized to fit with the body element (client) size. // SetFullscreen does nothing on browsers or mobiles.
// Additionally, the game screen is automatically resized when the body element is resized.
// Note that this has nothing to do with 'screen' which is outside of 'window'.
// It is recommended to put Ebiten game in an iframe, and if you want to make the game 'fullscreen'
// on browsers with Fullscreen API, you can do this by applying the API to the iframe.
//
// SetFullscreen does nothing on mobiles.
// //
// SetFullscreen is concurrent-safe. // SetFullscreen is concurrent-safe.
func SetFullscreen(fullscreen bool) { func SetFullscreen(fullscreen bool) {