mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-26 03:38:55 +01:00
ui: Add RunGame, WindowSize and SetWindowSize
This change introduces the new APIs RunGame, WindowSize and SetWindowSize. These new APIs hides the notion of 'scale', and is more flexible with the outside size change. This means that we can introduce a resizable window. This change also adds -legacy flag to examples/windowsize. If the flag is off, the new APIs are used. This change deprecates these functions since the notion of 'scale' is deprecated: * ScreenScale * ScreenSizeInFullscreen * SetScreenScale * SetScreenSize Fixes #943, #571 Updates #320
This commit is contained in:
parent
57d527bea2
commit
7d56e4335e
38
doc.go
38
doc.go
@ -14,7 +14,43 @@
|
|||||||
|
|
||||||
// Package ebiten provides graphics and input API to develop a 2D game.
|
// Package ebiten provides graphics and input API to develop a 2D game.
|
||||||
//
|
//
|
||||||
// You can start the game by calling the function Run.
|
// You can start the game by calling the function RunGame.
|
||||||
|
//
|
||||||
|
// type Game struct{}
|
||||||
|
//
|
||||||
|
// // Update is called every frame (1/60 [s]).
|
||||||
|
// func (g *Game) Update(screen *ebiten.Image) error {
|
||||||
|
//
|
||||||
|
// // Write your game's logical update.
|
||||||
|
//
|
||||||
|
// if ebiten.IsDrawingSkipped() {
|
||||||
|
// // When the game is running slowly, the rendering result
|
||||||
|
// // will not be adopted.
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Write your game's rendering.
|
||||||
|
//
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Layout takes the outside size (e.g., the window size) and returns the (logical) screen size.
|
||||||
|
// // If you don't have to adjust the screen size with the outside size, just return a fixed size.
|
||||||
|
// func (g *Game) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
|
||||||
|
// return 320, 240
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// func main() {
|
||||||
|
// // Sepcify the window size as you like. Here, a doulbed size is specified.
|
||||||
|
// ebiten.SetWindowSize(640, 480)
|
||||||
|
// ebiten.SetTitle("Your game's title")
|
||||||
|
// // Call ebiten.RunGame to start your game loop.
|
||||||
|
// if err := ebiten.Run(game); err != nil {
|
||||||
|
// log.Fatal(err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// For backward compatibility, you can use a shorthand style Run.
|
||||||
//
|
//
|
||||||
// // update is called every frame (1/60 [s]).
|
// // update is called every frame (1/60 [s]).
|
||||||
// func update(screen *ebiten.Image) error {
|
// func update(screen *ebiten.Image) error {
|
||||||
|
@ -37,6 +37,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// flagLegacy represents whether the legacy APIs are used or not.
|
||||||
|
// If flagLegacy is true, these legacy APIs are used:
|
||||||
|
//
|
||||||
|
// * ebiten.Run
|
||||||
|
// * ebiten.ScreenScale
|
||||||
|
// * ebiten.SetScreenScale
|
||||||
|
// * ebiten.SetScreenSize
|
||||||
|
//
|
||||||
|
// If flagLegacy is false, these APIs are used:
|
||||||
|
//
|
||||||
|
// * ebiten.RunGame
|
||||||
|
// * ebiten.SetWindowSize
|
||||||
|
// * ebiten.WindowSize
|
||||||
|
flagLegacy = flag.Bool("legacy", false, "use the legacy API")
|
||||||
|
|
||||||
|
flagFullscreen = flag.Bool("fullscreen", false, "fullscreen")
|
||||||
flagWindowPosition = flag.String("windowposition", "", "window position (e.g., 100,200)")
|
flagWindowPosition = flag.String("windowposition", "", "window position (e.g., 100,200)")
|
||||||
flagScreenTransparent = flag.Bool("screentransparent", false, "screen transparent")
|
flagScreenTransparent = flag.Bool("screentransparent", false, "screen transparent")
|
||||||
)
|
)
|
||||||
@ -76,10 +92,32 @@ func createRandomIconImage() image.Image {
|
|||||||
return img
|
return img
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(screen *ebiten.Image) error {
|
type game struct {
|
||||||
screenScale := ebiten.ScreenScale()
|
width int
|
||||||
const d = 16
|
height int
|
||||||
screenWidth, screenHeight := screen.Size()
|
}
|
||||||
|
|
||||||
|
func (g *game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||||
|
// Ignore the outside size. This means that the offscreen is not adjusted with the outside world.
|
||||||
|
return g.width, g.height
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *game) Update(screen *ebiten.Image) error {
|
||||||
|
var (
|
||||||
|
screenWidth int
|
||||||
|
screenHeight int
|
||||||
|
screenScale float64
|
||||||
|
)
|
||||||
|
if *flagLegacy {
|
||||||
|
screenWidth, screenHeight = screen.Size()
|
||||||
|
screenScale = ebiten.ScreenScale()
|
||||||
|
} else {
|
||||||
|
screenWidth = g.width
|
||||||
|
screenHeight = g.height
|
||||||
|
ww, _ := ebiten.WindowSize()
|
||||||
|
screenScale = float64(ww) / float64(g.width)
|
||||||
|
}
|
||||||
|
|
||||||
fullscreen := ebiten.IsFullscreen()
|
fullscreen := ebiten.IsFullscreen()
|
||||||
runnableInBackground := ebiten.IsRunnableInBackground()
|
runnableInBackground := ebiten.IsRunnableInBackground()
|
||||||
cursorVisible := ebiten.IsCursorVisible()
|
cursorVisible := ebiten.IsCursorVisible()
|
||||||
@ -89,6 +127,7 @@ func update(screen *ebiten.Image) error {
|
|||||||
positionX, positionY := ebiten.WindowPosition()
|
positionX, positionY := ebiten.WindowPosition()
|
||||||
transparent := ebiten.IsScreenTransparent()
|
transparent := ebiten.IsScreenTransparent()
|
||||||
|
|
||||||
|
const d = 16
|
||||||
if ebiten.IsKeyPressed(ebiten.KeyShift) {
|
if ebiten.IsKeyPressed(ebiten.KeyShift) {
|
||||||
if inpututil.IsKeyJustPressed(ebiten.KeyUp) {
|
if inpututil.IsKeyJustPressed(ebiten.KeyUp) {
|
||||||
screenHeight += d
|
screenHeight += d
|
||||||
@ -164,9 +203,14 @@ func update(screen *ebiten.Image) error {
|
|||||||
decorated = !decorated
|
decorated = !decorated
|
||||||
}
|
}
|
||||||
|
|
||||||
ebiten.SetScreenSize(screenWidth, screenHeight)
|
if *flagLegacy {
|
||||||
// TODO: Add a flag for compatibility mode and call SetScreenScale only when the flag is on.
|
ebiten.SetScreenSize(screenWidth, screenHeight)
|
||||||
ebiten.SetScreenScale(screenScale)
|
ebiten.SetScreenScale(screenScale)
|
||||||
|
} else {
|
||||||
|
g.width = screenWidth
|
||||||
|
g.height = screenHeight
|
||||||
|
ebiten.SetWindowSize(int(float64(screenWidth)*screenScale), int(float64(screenHeight)*screenScale))
|
||||||
|
}
|
||||||
ebiten.SetFullscreen(fullscreen)
|
ebiten.SetFullscreen(fullscreen)
|
||||||
ebiten.SetRunnableInBackground(runnableInBackground)
|
ebiten.SetRunnableInBackground(runnableInBackground)
|
||||||
ebiten.SetCursorVisible(cursorVisible)
|
ebiten.SetCursorVisible(cursorVisible)
|
||||||
@ -268,7 +312,27 @@ func main() {
|
|||||||
}
|
}
|
||||||
ebiten.SetScreenTransparent(*flagScreenTransparent)
|
ebiten.SetScreenTransparent(*flagScreenTransparent)
|
||||||
|
|
||||||
if err := ebiten.Run(update, initScreenWidth, initScreenHeight, initScreenScale, "Window Size (Ebiten Demo)"); err != nil {
|
g := &game{
|
||||||
log.Fatal(err)
|
width: initScreenWidth,
|
||||||
|
height: initScreenHeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flagFullscreen {
|
||||||
|
ebiten.SetFullscreen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const title = "Window Size (Ebiten Demo)"
|
||||||
|
if *flagLegacy {
|
||||||
|
if err := ebiten.Run(g.Update, g.width, g.height, initScreenScale, title); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w := int(float64(g.width) * initScreenScale)
|
||||||
|
h := int(float64(g.height) * initScreenScale)
|
||||||
|
ebiten.SetWindowSize(w, h)
|
||||||
|
ebiten.SetWindowTitle(title)
|
||||||
|
if err := ebiten.RunGame(g); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,7 @@ type UI interface {
|
|||||||
IsWindowResizable() bool
|
IsWindowResizable() bool
|
||||||
ScreenSizeInFullscreen() (int, int)
|
ScreenSizeInFullscreen() (int, int)
|
||||||
WindowPosition() (int, int)
|
WindowPosition() (int, int)
|
||||||
|
WindowSize() (int, int)
|
||||||
IsScreenTransparent() bool
|
IsScreenTransparent() bool
|
||||||
MonitorPosition() (int, int)
|
MonitorPosition() (int, int)
|
||||||
CanHaveWindow() bool // TODO: Create a 'Widnow' interface.
|
CanHaveWindow() bool // TODO: Create a 'Widnow' interface.
|
||||||
|
@ -64,6 +64,8 @@ type UserInterface struct {
|
|||||||
initWindowResizable bool
|
initWindowResizable bool
|
||||||
initWindowPositionXInDP int
|
initWindowPositionXInDP int
|
||||||
initWindowPositionYInDP int
|
initWindowPositionYInDP int
|
||||||
|
initWindowWidthInDP int
|
||||||
|
initWindowHeightInDP int
|
||||||
initScreenTransparent bool
|
initScreenTransparent bool
|
||||||
initIconImages []image.Image
|
initIconImages []image.Image
|
||||||
|
|
||||||
@ -80,17 +82,19 @@ type UserInterface struct {
|
|||||||
const (
|
const (
|
||||||
maxInt = int(^uint(0) >> 1)
|
maxInt = int(^uint(0) >> 1)
|
||||||
minInt = -maxInt - 1
|
minInt = -maxInt - 1
|
||||||
invalidPos = minInt
|
invalidVal = minInt
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
theUI = &UserInterface{
|
theUI = &UserInterface{
|
||||||
origPosX: invalidPos,
|
origPosX: invalidVal,
|
||||||
origPosY: invalidPos,
|
origPosY: invalidVal,
|
||||||
initCursorMode: driver.CursorModeVisible,
|
initCursorMode: driver.CursorModeVisible,
|
||||||
initWindowDecorated: true,
|
initWindowDecorated: true,
|
||||||
initWindowPositionXInDP: invalidPos,
|
initWindowPositionXInDP: invalidVal,
|
||||||
initWindowPositionYInDP: invalidPos,
|
initWindowPositionYInDP: invalidVal,
|
||||||
|
initWindowWidthInDP: invalidVal,
|
||||||
|
initWindowHeightInDP: invalidVal,
|
||||||
vsync: true,
|
vsync: true,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -302,10 +306,10 @@ func (u *UserInterface) setInitIconImages(iconImages []image.Image) {
|
|||||||
func (u *UserInterface) getInitWindowPosition() (int, int) {
|
func (u *UserInterface) getInitWindowPosition() (int, int) {
|
||||||
u.m.RLock()
|
u.m.RLock()
|
||||||
defer u.m.RUnlock()
|
defer u.m.RUnlock()
|
||||||
if u.initWindowPositionXInDP != invalidPos && u.initWindowPositionYInDP != invalidPos {
|
if u.initWindowPositionXInDP != invalidVal && u.initWindowPositionYInDP != invalidVal {
|
||||||
return u.initWindowPositionXInDP, u.initWindowPositionYInDP
|
return u.initWindowPositionXInDP, u.initWindowPositionYInDP
|
||||||
}
|
}
|
||||||
return invalidPos, invalidPos
|
return invalidVal, invalidVal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) setInitWindowPosition(x, y int) {
|
func (u *UserInterface) setInitWindowPosition(x, y int) {
|
||||||
@ -316,6 +320,19 @@ func (u *UserInterface) setInitWindowPosition(x, y int) {
|
|||||||
u.initWindowPositionYInDP = y
|
u.initWindowPositionYInDP = y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) getInitWindowSize() (int, int) {
|
||||||
|
u.m.Lock()
|
||||||
|
w, h := u.initWindowWidthInDP, u.initWindowHeightInDP
|
||||||
|
u.m.Unlock()
|
||||||
|
return w, h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) setInitWindowSize(width, height int) {
|
||||||
|
u.m.Lock()
|
||||||
|
u.initWindowWidthInDP, u.initWindowHeightInDP = width, height
|
||||||
|
u.m.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
// toDeviceIndependentPixel must be called from the main thread.
|
// toDeviceIndependentPixel must be called from the main thread.
|
||||||
func (u *UserInterface) toDeviceIndependentPixel(x float64) float64 {
|
func (u *UserInterface) toDeviceIndependentPixel(x float64) float64 {
|
||||||
return x / u.glfwScale()
|
return x / u.glfwScale()
|
||||||
@ -695,12 +712,16 @@ func (u *UserInterface) run(context driver.UIContext) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u.SetWindowPosition(u.getInitWindowPosition())
|
u.SetWindowPosition(u.getInitWindowPosition())
|
||||||
|
if w, h := u.getInitWindowSize(); w != invalidVal && h != invalidVal {
|
||||||
|
w = int(u.toDeviceDependentPixel(float64(w)))
|
||||||
|
h = int(u.toDeviceDependentPixel(float64(h)))
|
||||||
|
u.setWindowSize(w, h, u.isFullscreen(), u.vsync)
|
||||||
|
}
|
||||||
|
|
||||||
_ = u.t.Call(func() error {
|
_ = u.t.Call(func() error {
|
||||||
u.title = u.getInitTitle()
|
u.title = u.getInitTitle()
|
||||||
u.window.SetTitle(u.title)
|
u.window.SetTitle(u.title)
|
||||||
u.window.Show()
|
u.window.Show()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -892,7 +913,7 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
|
|||||||
u.swapBuffers()
|
u.swapBuffers()
|
||||||
|
|
||||||
if fullscreen {
|
if fullscreen {
|
||||||
if u.origPosX == invalidPos || u.origPosY == invalidPos {
|
if u.origPosX == invalidVal || u.origPosY == invalidVal {
|
||||||
u.origPosX, u.origPosY = u.window.GetPos()
|
u.origPosX, u.origPosY = u.window.GetPos()
|
||||||
}
|
}
|
||||||
m := u.currentMonitor()
|
m := u.currentMonitor()
|
||||||
@ -941,7 +962,7 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
|
|||||||
width = minWindowWidth
|
width = minWindowWidth
|
||||||
}
|
}
|
||||||
|
|
||||||
if u.origPosX != invalidPos && u.origPosY != invalidPos {
|
if u.origPosX != invalidVal && u.origPosY != invalidVal {
|
||||||
x := u.origPosX
|
x := u.origPosX
|
||||||
y := u.origPosY
|
y := u.origPosY
|
||||||
u.window.SetPos(x, y)
|
u.window.SetPos(x, y)
|
||||||
@ -951,8 +972,8 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool, vsync
|
|||||||
u.window.SetPos(x+1, y)
|
u.window.SetPos(x+1, y)
|
||||||
u.window.SetPos(x, y)
|
u.window.SetPos(x, y)
|
||||||
}
|
}
|
||||||
u.origPosX = invalidPos
|
u.origPosX = invalidVal
|
||||||
u.origPosY = invalidPos
|
u.origPosY = invalidVal
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the window size after the position. The order matters.
|
// Set the window size after the position. The order matters.
|
||||||
@ -1083,9 +1104,19 @@ func (u *UserInterface) IsScreenTransparent() bool {
|
|||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) WindowSize() (int, int) {
|
||||||
|
if !u.isRunning() {
|
||||||
|
return u.getInitWindowSize()
|
||||||
|
}
|
||||||
|
w := int(u.toDeviceIndependentPixel(float64(u.windowWidth)))
|
||||||
|
h := int(u.toDeviceIndependentPixel(float64(u.windowHeight)))
|
||||||
|
return w, h
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserInterface) SetWindowSize(width, height int) {
|
func (u *UserInterface) SetWindowSize(width, height int) {
|
||||||
if !u.isRunning() {
|
if !u.isRunning() {
|
||||||
panic("glfw: SetWindowSize can't be called before the main loop starts")
|
u.setInitWindowSize(width, height)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
w := int(u.toDeviceDependentPixel(float64(width)))
|
w := int(u.toDeviceDependentPixel(float64(width)))
|
||||||
h := int(u.toDeviceDependentPixel(float64(height)))
|
h := int(u.toDeviceDependentPixel(float64(height)))
|
||||||
|
@ -142,6 +142,10 @@ func (u *UserInterface) SetWindowResizable(decorated bool) {
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) WindowSize() (int, int) {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserInterface) SetWindowSize(width, height int) {
|
func (u *UserInterface) SetWindowSize(width, height int) {
|
||||||
// TODO: This is too tricky: Even though browsers don't have windows, SetWindowSize is called whenever the
|
// TODO: This is too tricky: Even though browsers don't have windows, SetWindowSize is called whenever the
|
||||||
// screen size is changed. Fix this hack.
|
// screen size is changed. Fix this hack.
|
||||||
|
@ -433,6 +433,10 @@ func (u *UserInterface) SetWindowSize(width, height int) {
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) WindowSize() (int, int) {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserInterface) CanHaveWindow() bool {
|
func (u *UserInterface) CanHaveWindow() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
141
run.go
141
run.go
@ -24,14 +24,12 @@ import (
|
|||||||
var _ = __EBITEN_REQUIRES_GO_VERSION_1_12_OR_LATER__
|
var _ = __EBITEN_REQUIRES_GO_VERSION_1_12_OR_LATER__
|
||||||
|
|
||||||
// Game defines necessary functions for a game.
|
// Game defines necessary functions for a game.
|
||||||
//
|
|
||||||
// Note: This interface is not used anywhere yet.
|
|
||||||
type Game interface {
|
type Game interface {
|
||||||
// Update updates a game by one frame.
|
// Update updates a game by one frame.
|
||||||
Update(*Image) error
|
Update(*Image) error
|
||||||
|
|
||||||
// Layout accepts a native outside size in DP (device-independent pixels) and returns the game's logical
|
// Layout accepts a native outside size in device-independent pixels and returns the game's logical screen
|
||||||
// screen size.
|
// size.
|
||||||
//
|
//
|
||||||
// The screen scale is automatically adjusted to fit the outside.
|
// The screen scale is automatically adjusted to fit the outside.
|
||||||
//
|
//
|
||||||
@ -104,11 +102,14 @@ func IsRunningSlowly() bool {
|
|||||||
return IsDrawingSkipped()
|
return IsDrawingSkipped()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run runs the game.
|
// Run starts the main loop and runs the game.
|
||||||
// f is a function which is called at every frame.
|
// f is a function which is called at every frame.
|
||||||
// The argument (*Image) is the render target that represents the screen.
|
// The argument (*Image) is the render target that represents the screen.
|
||||||
// The screen size is based on the given values (width and height).
|
// The screen size is based on the given values (width and height).
|
||||||
//
|
//
|
||||||
|
// Run is a shorthand for RunGame, but there are some restrictions.
|
||||||
|
// If you want to resize the window by dragging, use RunGame instead.
|
||||||
|
//
|
||||||
// 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 on desktops.
|
// scale is used to enlarge the screen on desktops.
|
||||||
@ -156,7 +157,9 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e
|
|||||||
}
|
}
|
||||||
|
|
||||||
theUIContext = newUIContext(game, scale)
|
theUIContext = newUIContext(game, scale)
|
||||||
fixWindowPosition(int(float64(width)*scale), int(float64(height)*scale))
|
ww, wh := int(float64(width)*scale), int(float64(height)*scale)
|
||||||
|
fixWindowPosition(ww, wh)
|
||||||
|
SetWindowSize(ww, wh)
|
||||||
SetWindowTitle(title)
|
SetWindowTitle(title)
|
||||||
if err := uiDriver().Run(theUIContext, graphicsDriver()); err != nil {
|
if err := uiDriver().Run(theUIContext, graphicsDriver()); err != nil {
|
||||||
if err == driver.RegularTermination {
|
if err == driver.RegularTermination {
|
||||||
@ -168,6 +171,58 @@ func Run(f func(*Image) error, width, height int, scale float64, title string) e
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunGame starts the main loop and runs the game.
|
||||||
|
// game's Update function is called every frame.
|
||||||
|
// game's Layout function is called when necessary, and you can specify the logical screen size in the function.
|
||||||
|
//
|
||||||
|
// RunGame is a more flexibile form of Run due to 'Layout' function.
|
||||||
|
// The window is resizable if you use RunGame, while you cannot if you use Run.
|
||||||
|
// RunGame is more sophisticated way than Run and hides the notion of 'scale'.
|
||||||
|
//
|
||||||
|
// On desktops, SetWindowSize must be called before RunGame is called.
|
||||||
|
//
|
||||||
|
// A window size is based on the given values (width, height and scale).
|
||||||
|
//
|
||||||
|
// RunGame must be called on the main thread.
|
||||||
|
// Note that Ebiten bounds the main goroutine to the main OS thread by runtime.LockOSThread.
|
||||||
|
//
|
||||||
|
// Ebiten tries to call game's Update function 60 times a second by default. In other words,
|
||||||
|
// TPS (ticks per second) is 60 by default.
|
||||||
|
// This is not related to framerate (display's refresh rate).
|
||||||
|
//
|
||||||
|
// game's Update is not called when the window is in background by default.
|
||||||
|
// This setting is configurable with SetRunnableInBackground.
|
||||||
|
//
|
||||||
|
// The given scale is ignored on fullscreen mode or gomobile-build mode.
|
||||||
|
//
|
||||||
|
// On non-GopherJS environments, RunGame returns error when 1) OpenGL error happens, 2) audio error happens or
|
||||||
|
// 3) f returns error. In the case of 3), RunGame returns the same error.
|
||||||
|
//
|
||||||
|
// On GopherJS, RunGame returns immediately.
|
||||||
|
// It is because the 'main' goroutine cannot be blocked on GopherJS due to the bug (gopherjs/gopherjs#826).
|
||||||
|
// When an error happens, this is shown as an error on the console.
|
||||||
|
//
|
||||||
|
// The size unit is device-independent pixel.
|
||||||
|
//
|
||||||
|
// Don't call RunGame twice or more in one process.
|
||||||
|
func RunGame(game Game) error {
|
||||||
|
if uiDriver().CanHaveWindow() {
|
||||||
|
w, h := WindowSize()
|
||||||
|
if w < 0 || h < 0 {
|
||||||
|
panic("ebiten: SetWindowSize must be called before RunGame on desktops")
|
||||||
|
}
|
||||||
|
fixWindowPosition(w, h)
|
||||||
|
}
|
||||||
|
theUIContext = newUIContext(game, 1)
|
||||||
|
if err := uiDriver().Run(theUIContext, graphicsDriver()); err != nil {
|
||||||
|
if err == driver.RegularTermination {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// RunWithoutMainLoop runs the game, but don't call the loop on the main (UI) thread.
|
// RunWithoutMainLoop runs the game, but don't call the loop on the main (UI) thread.
|
||||||
// Different from Run, RunWithoutMainLoop returns immediately.
|
// Different from Run, RunWithoutMainLoop returns immediately.
|
||||||
//
|
//
|
||||||
@ -184,49 +239,17 @@ func RunWithoutMainLoop(f func(*Image) error, width, height int, scale float64,
|
|||||||
return uiDriver().RunWithoutMainLoop(width, height, scale, title, theUIContext, graphicsDriver())
|
return uiDriver().RunWithoutMainLoop(width, height, scale, title, theUIContext, graphicsDriver())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScreenSizeInFullscreen returns the size in device-independent pixels when the game is fullscreen.
|
// ScreenSizeInFullscreen is deprecated as of 1.11.0-alpha.
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// On browsers, ScreenSizeInFullscreen returns the 'window' (global object) size, not 'screen' size since an Ebiten game
|
|
||||||
// should not know the outside of the window object.
|
|
||||||
// For more details, see SetFullscreen API comment.
|
|
||||||
//
|
|
||||||
// On mobiles, ScreenSizeInFullscreen returns (0, 0) so far.
|
|
||||||
//
|
|
||||||
// If you use this for screen size with SetFullscreen(true), you can get the fullscreen mode
|
|
||||||
// which size is well adjusted with the monitor.
|
|
||||||
//
|
|
||||||
// w, h := ScreenSizeInFullscreen()
|
|
||||||
// ebiten.SetFullscreen(true)
|
|
||||||
// ebiten.Run(update, w, h, 1, "title")
|
|
||||||
//
|
|
||||||
// Furthermore, you can use them with DeviceScaleFactor(), you can get the finest
|
|
||||||
// fullscreen mode.
|
|
||||||
//
|
|
||||||
// s := ebiten.DeviceScaleFactor()
|
|
||||||
// w, h := ScreenSizeInFullscreen()
|
|
||||||
// ebiten.SetFullscreen(true)
|
|
||||||
// ebiten.Run(update, int(float64(w) * s), int(float64(h) * s), 1/s, "title")
|
|
||||||
//
|
|
||||||
// For actual example, see examples/fullscreen
|
|
||||||
//
|
|
||||||
// ScreenSizeInFullscreen must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run.
|
|
||||||
func ScreenSizeInFullscreen() (int, int) {
|
func ScreenSizeInFullscreen() (int, int) {
|
||||||
return uiDriver().ScreenSizeInFullscreen()
|
return uiDriver().ScreenSizeInFullscreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MonitorSize is deprecated as of 1.8.0-alpha. Use ScreenSizeInFullscreen instead.
|
// MonitorSize is deprecated as of 1.8.0-alpha.
|
||||||
func MonitorSize() (int, int) {
|
func MonitorSize() (int, int) {
|
||||||
return ScreenSizeInFullscreen()
|
return ScreenSizeInFullscreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScreenSize changes the (logical) size of the screen.
|
// SetScreenSize is deprecated as of 1.11.0-alpha. Use SetWindowSize and RunGame (Game's Layout) instead.
|
||||||
// SetScreenSize adjusts the window size on desktops without changing its scale.
|
|
||||||
//
|
|
||||||
// The unit is device-independent pixel.
|
|
||||||
//
|
|
||||||
// SetScreenSize is concurrent-safe.
|
|
||||||
func SetScreenSize(width, height int) {
|
func SetScreenSize(width, height int) {
|
||||||
if width <= 0 || height <= 0 {
|
if width <= 0 || height <= 0 {
|
||||||
panic("ebiten: width and height must be positive")
|
panic("ebiten: width and height must be positive")
|
||||||
@ -237,27 +260,7 @@ func SetScreenSize(width, height int) {
|
|||||||
theUIContext.SetScreenSize(width, height)
|
theUIContext.SetScreenSize(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScreenScale changes the scale of the screen on desktops.
|
// SetScreenScale is deprecated as of 1.11.0-alpha. Use SetWindowSize instead.
|
||||||
//
|
|
||||||
// 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, SetScreenScale saves the given value and affects the returned value of ScreenScale,
|
|
||||||
// but does not affect actual rendering.
|
|
||||||
// SetScreenScale works as this as of 1.10.0-alpha.
|
|
||||||
// Before that, SetScreenScale affected the rendering scale.
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
//
|
|
||||||
// TODO: Deprecate this function.
|
|
||||||
func SetScreenScale(scale float64) {
|
func SetScreenScale(scale float64) {
|
||||||
if scale <= 0 {
|
if scale <= 0 {
|
||||||
panic("ebiten: scale must be positive")
|
panic("ebiten: scale must be positive")
|
||||||
@ -268,15 +271,7 @@ func SetScreenScale(scale float64) {
|
|||||||
theUIContext.setScaleForWindow(scale)
|
theUIContext.setScaleForWindow(scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScreenScale returns the current screen scale.
|
// ScreenScale is deprecated as of 1.11.0-alpha. Use WindowSize instead.
|
||||||
//
|
|
||||||
// On browsers, this value does not affect actual rendering.
|
|
||||||
//
|
|
||||||
// If Run is not called, this returns 0.
|
|
||||||
//
|
|
||||||
// ScreenScale is concurrent-safe.
|
|
||||||
//
|
|
||||||
// TODO: Deprecate this function.
|
|
||||||
func ScreenScale() float64 {
|
func ScreenScale() float64 {
|
||||||
if theUIContext == nil {
|
if theUIContext == nil {
|
||||||
panic("ebiten: ScreenScale can't be called before the main loop starts")
|
panic("ebiten: ScreenScale can't be called before the main loop starts")
|
||||||
@ -391,7 +386,7 @@ func SetRunnableInBackground(runnableInBackground bool) {
|
|||||||
// DeviceScaleFactor might panic on init function on some devices like Android.
|
// DeviceScaleFactor might panic on init function on some devices like Android.
|
||||||
// Then, it is not recommended to call DeviceScaleFactor from init functions.
|
// Then, it is not recommended to call DeviceScaleFactor from init functions.
|
||||||
//
|
//
|
||||||
// DeviceScaleFactor must be called on the main thread before ebiten.Run, and is concurrent-safe after ebiten.Run.
|
// DeviceScaleFactor must be called on the main thread before the main loop, and is concurrent-safe after the main loop.
|
||||||
func DeviceScaleFactor() float64 {
|
func DeviceScaleFactor() float64 {
|
||||||
return uiDriver().DeviceScaleFactor()
|
return uiDriver().DeviceScaleFactor()
|
||||||
}
|
}
|
||||||
@ -462,7 +457,7 @@ func IsScreenTransparent() bool {
|
|||||||
|
|
||||||
// SetScreenTransparent sets the state if the window is transparent.
|
// SetScreenTransparent sets the state if the window is transparent.
|
||||||
//
|
//
|
||||||
// SetScreenTransparent panics if SetScreenTransparent is called after Run.
|
// SetScreenTransparent panics if SetScreenTransparent is called after the main loop.
|
||||||
//
|
//
|
||||||
// SetScreenTransparent does nothing on mobiles.
|
// SetScreenTransparent does nothing on mobiles.
|
||||||
func SetScreenTransparent(transparent bool) {
|
func SetScreenTransparent(transparent bool) {
|
||||||
|
12
uicontext.go
12
uicontext.go
@ -86,7 +86,7 @@ var theUIContext *uiContext
|
|||||||
func (c *uiContext) setScaleForWindow(scale float64) {
|
func (c *uiContext) setScaleForWindow(scale float64) {
|
||||||
g, ok := c.game.(*defaultGame)
|
g, ok := c.game.(*defaultGame)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("ebiten: setScaleForWindow must be called when Run is used")
|
panic("ebiten: setScaleForWindow can be called only when Run is used")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.m.Lock()
|
c.m.Lock()
|
||||||
@ -99,7 +99,7 @@ func (c *uiContext) setScaleForWindow(scale float64) {
|
|||||||
|
|
||||||
func (c *uiContext) getScaleForWindow() float64 {
|
func (c *uiContext) getScaleForWindow() float64 {
|
||||||
if _, ok := c.game.(*defaultGame); !ok {
|
if _, ok := c.game.(*defaultGame); !ok {
|
||||||
panic("ebiten: getScaleForWindow must be called when Run is used")
|
panic("ebiten: getScaleForWindow can be called only when Run is used")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.m.Lock()
|
c.m.Lock()
|
||||||
@ -115,7 +115,7 @@ func (c *uiContext) getScaleForWindow() float64 {
|
|||||||
func (c *uiContext) SetScreenSize(width, height int) {
|
func (c *uiContext) SetScreenSize(width, height int) {
|
||||||
g, ok := c.game.(*defaultGame)
|
g, ok := c.game.(*defaultGame)
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("ebiten: SetScreenSize must be called when Run is used")
|
panic("ebiten: SetScreenSize can be called only when Run is used")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.m.Lock()
|
c.m.Lock()
|
||||||
@ -157,7 +157,11 @@ func (c *uiContext) updateOffscreen() {
|
|||||||
if c.offscreen == nil {
|
if c.offscreen == nil {
|
||||||
c.offscreen = newImage(sw, sh, FilterDefault, true)
|
c.offscreen = newImage(sw, sh, FilterDefault, true)
|
||||||
}
|
}
|
||||||
c.SetScreenSize(sw, sh)
|
|
||||||
|
// The window size is automatically adjusted when Run is used.
|
||||||
|
if _, ok := c.game.(*defaultGame); ok {
|
||||||
|
c.SetScreenSize(sw, sh)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
|
// TODO: This is duplicated with mobile/ebitenmobileview/funcs.go. Refactor this.
|
||||||
d := uiDriver().DeviceScaleFactor()
|
d := uiDriver().DeviceScaleFactor()
|
||||||
|
20
window.go
20
window.go
@ -55,7 +55,7 @@ func IsWindowDecorated() bool {
|
|||||||
// setWindowResizable works only on desktops.
|
// setWindowResizable works only on desktops.
|
||||||
// setWindowResizable does nothing on other platforms.
|
// setWindowResizable does nothing on other platforms.
|
||||||
//
|
//
|
||||||
// setWindowResizable panics if setWindowResizable is called after Run.
|
// setWindowResizable panics if setWindowResizable is called after the main loop.
|
||||||
//
|
//
|
||||||
// setWindowResizable is concurrent-safe.
|
// setWindowResizable is concurrent-safe.
|
||||||
func setWindowResizable(resizable bool) {
|
func setWindowResizable(resizable bool) {
|
||||||
@ -104,7 +104,7 @@ func SetWindowIcon(iconImages []image.Image) {
|
|||||||
|
|
||||||
// WindowPosition returns the window position.
|
// WindowPosition returns the window position.
|
||||||
//
|
//
|
||||||
// WindowPosition panics before Run is called.
|
// WindowPosition panics if the main loop does not start yet.
|
||||||
//
|
//
|
||||||
// WindowPosition returns the last window position on fullscreen mode.
|
// WindowPosition returns the last window position on fullscreen mode.
|
||||||
//
|
//
|
||||||
@ -120,8 +120,6 @@ func WindowPosition() (x, y int) {
|
|||||||
|
|
||||||
// SetWindowPosition sets the window position.
|
// SetWindowPosition sets the window position.
|
||||||
//
|
//
|
||||||
// SetWindowPosition works before and after Run is called.
|
|
||||||
//
|
|
||||||
// SetWindowPosition does nothing on fullscreen mode.
|
// SetWindowPosition does nothing on fullscreen mode.
|
||||||
//
|
//
|
||||||
// SetWindowPosition does nothing on browsers and mobiles.
|
// SetWindowPosition does nothing on browsers and mobiles.
|
||||||
@ -185,3 +183,17 @@ func fixWindowPosition(width, height int) {
|
|||||||
uiDriver().SetWindowPosition(initWindowPositionX, initWindowPositionY)
|
uiDriver().SetWindowPosition(initWindowPositionX, initWindowPositionY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WindowSize returns the window size. On fullscreen mode, WindowSize returns the original window size.
|
||||||
|
//
|
||||||
|
// WindowSize is concurrent-safe.
|
||||||
|
func WindowSize() (int, int) {
|
||||||
|
return uiDriver().WindowSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWindowSize sets the window size. On fullscreen mode, SetWindowSize sets the original window size.
|
||||||
|
//
|
||||||
|
// SetWindowSize is concurrent-safe.
|
||||||
|
func SetWindowSize(width, height int) {
|
||||||
|
uiDriver().SetWindowSize(width, height)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user