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"
"math"
"math/rand"
"runtime"
"time"
"github.com/golang/freetype/truetype"
@ -392,11 +391,6 @@ func (g *Game) drawGopher(screen *ebiten.Image) {
func main() {
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 {
panic(err)
}

View File

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

View File

@ -33,7 +33,6 @@ type UserInterface struct {
width int
height int
scale float64
fullscreen bool
runnableInBackground bool
vsync bool
@ -75,11 +74,11 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, 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) {
u.setScreenSize(u.width, u.height, scale, u.fullscreen)
u.scale = scale
}
func (u *UserInterface) ScreenScale() float64 {
@ -87,11 +86,11 @@ func (u *UserInterface) ScreenScale() float64 {
}
func (u *UserInterface) SetFullscreen(fullscreen bool) {
u.setScreenSize(u.width, u.height, u.scale, fullscreen)
// Do nothing
}
func (u *UserInterface) IsFullscreen() bool {
return u.fullscreen
return false
}
func (u *UserInterface) SetRunnableInBackground(runnableInBackground bool) {
@ -164,9 +163,6 @@ func (u *UserInterface) DeviceScaleFactor() float64 {
}
func (u *UserInterface) getScale() float64 {
if !u.fullscreen {
return u.scale
}
body := document.Get("body")
bw := body.Get("clientWidth").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 {
// scale is ignored.
document.Set("title", title)
u.setScreenSize(width, height, scale, u.fullscreen)
u.setScreenSize(width, height)
canvas.Call("focus")
ch := u.loop(context)
if runtime.GOARCH == "wasm" {
@ -454,15 +452,12 @@ func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, tit
panic("js: RunWithoutMainLoop is not implemented")
}
func (u *UserInterface) setScreenSize(width, height int, scale float64, fullscreen bool) bool {
if u.width == width && u.height == height &&
u.scale == scale && fullscreen == u.fullscreen {
func (u *UserInterface) setScreenSize(width, height int) bool {
if u.width == width && u.height == height {
return false
}
u.width = width
u.height = height
u.scale = scale
u.fullscreen = fullscreen
u.updateScreenSize()
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).
//
// 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
// by the device scale on high-DPI display.
// If you pass inverse of the device scale,
// you can disable this automatical device scaling as a result.
// 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.
// 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)
}
// 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
// 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 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.
func SetScreenScale(scale float64) {
if scale <= 0 {
@ -221,6 +236,8 @@ func SetScreenScale(scale float64) {
// 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.
//
// ScreenScale is concurrent-safe.
@ -255,14 +272,14 @@ func SetCursorVisibility(visible bool) {
// IsFullscreen returns a boolean value indicating whether
// 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.
func IsFullscreen() bool {
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
// 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
// your monitor's resolution.
//
// On browsers, the game screen is resized to fit with the body element (client) size.
// 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 does nothing on browsers or mobiles.
//
// SetFullscreen is concurrent-safe.
func SetFullscreen(fullscreen bool) {