mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-27 03:02:49 +01:00
mobile/ebitenmobileview: Use the common uiContext for layouting
This means that the whole offscreen is cleared correctly. This change is a little breaking change: SetScreenSize or other functions no longer works on ebitenmobile. Use Layout instead. Fixes #1019
This commit is contained in:
parent
1b4c9f4e4d
commit
c927d33457
@ -220,16 +220,7 @@ const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
|
|||||||
[super viewDidLayoutSubviews];
|
[super viewDidLayoutSubviews];
|
||||||
CGRect viewRect = [[self view] frame];
|
CGRect viewRect = [[self view] frame];
|
||||||
|
|
||||||
EbitenmobileviewLayout(viewRect.size.width, viewRect.size.height, (id<EbitenmobileviewViewRectSetter>)self);
|
EbitenmobileviewLayout(viewRect.size.width, viewRect.size.height);
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setViewRect:(long)x y:(long)y width:(long)width height:(long)height {
|
|
||||||
CGRect viewRect = CGRectMake(x, y, width, height);
|
|
||||||
#if EBITEN_METAL
|
|
||||||
[[self metalView] setFrame:viewRect];
|
|
||||||
#else
|
|
||||||
[[self glkView] setFrame:viewRect];
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)didReceiveMemoryWarning {
|
- (void)didReceiveMemoryWarning {
|
||||||
@ -341,7 +332,6 @@ import android.util.Log;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import {{.JavaPkg}}.ebitenmobileview.Ebitenmobileview;
|
import {{.JavaPkg}}.ebitenmobileview.Ebitenmobileview;
|
||||||
import {{.JavaPkg}}.ebitenmobileview.ViewRectSetter;
|
|
||||||
|
|
||||||
public class EbitenView extends ViewGroup {
|
public class EbitenView extends ViewGroup {
|
||||||
private double getDeviceScale() {
|
private double getDeviceScale() {
|
||||||
@ -373,30 +363,16 @@ public class EbitenView extends ViewGroup {
|
|||||||
|
|
||||||
private void initialize() {
|
private void initialize() {
|
||||||
ebitenSurfaceView_ = new EbitenSurfaceView(getContext());
|
ebitenSurfaceView_ = new EbitenSurfaceView(getContext());
|
||||||
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||||
addView(ebitenSurfaceView_, params);
|
addView(ebitenSurfaceView_, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||||
int widthInDp = (int)Math.ceil(pxToDp(right - left));
|
ebitenSurfaceView_.layout(0, 0, right - left, bottom - top);
|
||||||
int heightInDp = (int)Math.ceil(pxToDp(bottom - top));
|
double widthInDp = pxToDp(right - left);
|
||||||
Ebitenmobileview.layout(widthInDp, heightInDp, new ViewRectSetter() {
|
double heightInDp = pxToDp(bottom - top);
|
||||||
@Override
|
Ebitenmobileview.layout(widthInDp, heightInDp);
|
||||||
public void setViewRect(long xInDp, long yInDp, long widthInDp, long heightInDp) {
|
|
||||||
// Use Math.floor to use smaller and safer values, or glitches can appear (#956).
|
|
||||||
final int widthInPx = (int)Math.floor(dpToPx(widthInDp));
|
|
||||||
final int heightInPx = (int)Math.floor(dpToPx(heightInDp));
|
|
||||||
final int xInPx = (int)Math.floor(dpToPx(xInDp));
|
|
||||||
final int yInPx = (int)Math.floor(dpToPx(yInDp));
|
|
||||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
ebitenSurfaceView_.layout(xInPx, yInPx, xInPx + widthInPx, yInPx + heightInPx);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// suspendGame suspends the game.
|
// suspendGame suspends the game.
|
||||||
|
File diff suppressed because one or more lines are too long
@ -32,7 +32,7 @@ var RegularTermination = errors.New("regular termination")
|
|||||||
|
|
||||||
type UI interface {
|
type UI interface {
|
||||||
Run(context UIContext) error
|
Run(context UIContext) error
|
||||||
RunWithoutMainLoop(width, height int, scale float64, title string, context UIContext) <-chan error
|
RunWithoutMainLoop(context UIContext) <-chan error
|
||||||
|
|
||||||
DeviceScaleFactor() float64
|
DeviceScaleFactor() float64
|
||||||
CursorMode() CursorMode
|
CursorMode() CursorMode
|
||||||
|
@ -543,7 +543,7 @@ func (u *UserInterface) Run(uicontext driver.UIContext) error {
|
|||||||
return <-ch
|
return <-ch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext) <-chan error {
|
func (u *UserInterface) RunWithoutMainLoop(context driver.UIContext) <-chan error {
|
||||||
panic("glfw: RunWithoutMainLoop is not implemented")
|
panic("glfw: RunWithoutMainLoop is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +425,7 @@ func (u *UserInterface) Run(context driver.UIContext) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext) <-chan error {
|
func (u *UserInterface) RunWithoutMainLoop(context driver.UIContext) <-chan error {
|
||||||
panic("js: RunWithoutMainLoop is not implemented")
|
panic("js: RunWithoutMainLoop is not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,10 +102,8 @@ func (u *UserInterface) Update() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserInterface struct {
|
type UserInterface struct {
|
||||||
// TODO: Remove these members: the driver layer should not care about the game screen size.
|
outsideWidth float64
|
||||||
width int
|
outsideHeight float64
|
||||||
height int
|
|
||||||
scale float64
|
|
||||||
|
|
||||||
sizeChanged bool
|
sizeChanged bool
|
||||||
foreground bool
|
foreground bool
|
||||||
@ -207,11 +205,9 @@ func (u *UserInterface) SetForeground(foreground bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) Run(context driver.UIContext) error {
|
func (u *UserInterface) Run(context driver.UIContext) error {
|
||||||
// TODO: Remove width/height/scale arguments. They are not used from gomobile-build.
|
|
||||||
|
|
||||||
u.setGBuildSizeCh = make(chan struct{})
|
u.setGBuildSizeCh = make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
if err := u.run(16, 16, 1, context, true); err != nil {
|
if err := u.run(context, true); err != nil {
|
||||||
// As mobile apps never ends, Loop can't return. Just panic here.
|
// As mobile apps never ends, Loop can't return. Just panic here.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -220,12 +216,12 @@ func (u *UserInterface) Run(context driver.UIContext) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, title string, context driver.UIContext) <-chan error {
|
func (u *UserInterface) RunWithoutMainLoop(context driver.UIContext) <-chan error {
|
||||||
ch := make(chan error)
|
ch := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
// title is ignored?
|
// title is ignored?
|
||||||
if err := u.run(width, height, scale, context, false); err != nil {
|
if err := u.run(context, false); err != nil {
|
||||||
ch <- err
|
ch <- err
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -233,7 +229,7 @@ func (u *UserInterface) RunWithoutMainLoop(width, height int, scale float64, tit
|
|||||||
return ch
|
return ch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) run(width, height int, scale float64, context driver.UIContext, mainloop bool) (err error) {
|
func (u *UserInterface) run(context driver.UIContext, mainloop bool) (err error) {
|
||||||
// Convert the panic to a regular error so that Java/Objective-C layer can treat this easily e.g., for
|
// Convert the panic to a regular error so that Java/Objective-C layer can treat this easily e.g., for
|
||||||
// Crashlytics. A panic is treated as SIGABRT, and there is no way to handle this on Java/Objective-C layer
|
// Crashlytics. A panic is treated as SIGABRT, and there is no way to handle this on Java/Objective-C layer
|
||||||
// unfortunately.
|
// unfortunately.
|
||||||
@ -245,9 +241,6 @@ func (u *UserInterface) run(width, height int, scale float64, context driver.UIC
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
u.m.Lock()
|
u.m.Lock()
|
||||||
u.width = width
|
|
||||||
u.height = height
|
|
||||||
u.scale = scale
|
|
||||||
u.sizeChanged = true
|
u.sizeChanged = true
|
||||||
u.context = context
|
u.context = context
|
||||||
u.m.Unlock()
|
u.m.Unlock()
|
||||||
@ -280,36 +273,26 @@ func (u *UserInterface) run(width, height int, scale float64, context driver.UIC
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) updateSize(context driver.UIContext) {
|
func (u *UserInterface) updateSize(context driver.UIContext) {
|
||||||
var width, height float64
|
var outsideWidth, outsideHeight float64
|
||||||
|
|
||||||
u.m.Lock()
|
u.m.Lock()
|
||||||
sizeChanged := u.sizeChanged
|
sizeChanged := u.sizeChanged
|
||||||
if sizeChanged {
|
if sizeChanged {
|
||||||
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
||||||
s := u.scale
|
outsideWidth = u.outsideWidth
|
||||||
width = float64(u.width) * s
|
outsideHeight = u.outsideHeight
|
||||||
height = float64(u.height) * s
|
|
||||||
} else {
|
} else {
|
||||||
// gomobile build
|
// gomobile build
|
||||||
d := deviceScale()
|
d := deviceScale()
|
||||||
width = float64(u.gbuildWidthPx) / d
|
outsideWidth = float64(u.gbuildWidthPx) / d
|
||||||
height = float64(u.gbuildHeightPx) / d
|
outsideHeight = float64(u.gbuildHeightPx) / d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
u.sizeChanged = false
|
u.sizeChanged = false
|
||||||
u.m.Unlock()
|
u.m.Unlock()
|
||||||
|
|
||||||
if sizeChanged {
|
if sizeChanged {
|
||||||
// Dirty hack to set the offscreen size for gomobile-bind.
|
context.Layout(outsideWidth, outsideHeight)
|
||||||
// TODO: Remove this. The layouting logic must be in the package ebiten, not here.
|
|
||||||
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
|
||||||
context.(interface {
|
|
||||||
SetScreenSize(width, height int)
|
|
||||||
}).SetScreenSize(u.width, u.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sizing also calls GL functions
|
|
||||||
context.Layout(width, height)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,14 +316,13 @@ func (u *UserInterface) ScreenSizeInFullscreen() (int, int) {
|
|||||||
return 0, 0
|
return 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScreenSizeAndScale is called from mobile/ebitenmobileview.
|
// SetOutsideSize is called from mobile/ebitenmobileview.
|
||||||
func (u *UserInterface) SetScreenSizeAndScale(width, height int, scale float64) {
|
func (u *UserInterface) SetOutsideSize(outsideWidth, outsideHeight float64) {
|
||||||
// Called from ebitenmobileview.
|
// Called from ebitenmobileview.
|
||||||
u.m.Lock()
|
u.m.Lock()
|
||||||
if u.width != width || u.height != height || u.scale != scale {
|
if u.outsideWidth != outsideWidth || u.outsideHeight != outsideHeight {
|
||||||
u.width = width
|
u.outsideWidth = outsideWidth
|
||||||
u.height = height
|
u.outsideHeight = outsideHeight
|
||||||
u.scale = scale
|
|
||||||
u.sizeChanged = true
|
u.sizeChanged = true
|
||||||
}
|
}
|
||||||
u.m.Unlock()
|
u.m.Unlock()
|
||||||
@ -358,11 +340,6 @@ func (u *UserInterface) setGBuildSize(widthPx, heightPx int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserInterface) adjustPosition(x, y int) (int, int) {
|
func (u *UserInterface) adjustPosition(x, y int) (int, int) {
|
||||||
// This function's caller already protects this function by the mutex.
|
|
||||||
if u.gbuildWidthPx == 0 || u.gbuildHeightPx == 0 {
|
|
||||||
s := u.scale
|
|
||||||
return int(float64(x) / s), int(float64(y) / s)
|
|
||||||
}
|
|
||||||
xf, yf := u.context.AdjustPosition(float64(x), float64(y))
|
xf, yf := u.context.AdjustPosition(float64(x), float64(y))
|
||||||
return int(xf), int(yf)
|
return int(xf), int(yf)
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
package mobile
|
package mobile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
"github.com/hajimehoshi/ebiten/mobile/ebitenmobileview"
|
"github.com/hajimehoshi/ebiten/mobile/ebitenmobileview"
|
||||||
)
|
)
|
||||||
@ -57,9 +55,9 @@ func Start(f func(*ebiten.Image) error, width, height int, scale float64, title
|
|||||||
height: height,
|
height: height,
|
||||||
})
|
})
|
||||||
// As the view layout is already determined, ignore the layout calculation at ebitenmobileview.
|
// As the view layout is already determined, ignore the layout calculation at ebitenmobileview.
|
||||||
w := int(math.Ceil((float64(width) * scale)))
|
w := float64(width) * scale
|
||||||
h := int(math.Ceil((float64(height) * scale)))
|
h := float64(height) * scale
|
||||||
ebitenmobileview.Layout(w, h, nil)
|
ebitenmobileview.Layout(w, h)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,54 +26,30 @@ package ebitenmobileview
|
|||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/hajimehoshi/ebiten"
|
"github.com/hajimehoshi/ebiten"
|
||||||
"github.com/hajimehoshi/ebiten/internal/uidriver/mobile"
|
"github.com/hajimehoshi/ebiten/internal/uidriver/mobile"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ViewRectSetter interface {
|
func Layout(viewWidth, viewHeight float64) {
|
||||||
SetViewRect(x, y, width, height int)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Layout(viewWidth, viewHeight int, viewRectSetter ViewRectSetter) {
|
|
||||||
theState.m.Lock()
|
theState.m.Lock()
|
||||||
defer theState.m.Unlock()
|
defer theState.m.Unlock()
|
||||||
layout(viewWidth, viewHeight, viewRectSetter)
|
layout(viewWidth, viewHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
func layout(viewWidth, viewHeight int, viewRectSetter ViewRectSetter) {
|
func layout(viewWidth, viewHeight float64) {
|
||||||
if theState.game == nil {
|
if theState.game == nil {
|
||||||
// It is fine to override the existing function since only the last layout result matters.
|
// It is fine to override the existing function since only the last layout result matters.
|
||||||
theState.delayedLayout = func() {
|
theState.delayedLayout = func() {
|
||||||
layout(viewWidth, viewHeight, viewRectSetter)
|
layout(viewWidth, viewHeight)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Layout must be called every frame like uiContext already did.
|
mobile.Get().SetOutsideSize(viewWidth, viewHeight)
|
||||||
w, h := theState.game.Layout(int(viewWidth), int(viewHeight))
|
if !theState.isRunning() {
|
||||||
scaleX := float64(viewWidth) / float64(w)
|
theState.errorCh = ebiten.RunGameWithoutMainLoop(theState.game)
|
||||||
scaleY := float64(viewHeight) / float64(h)
|
|
||||||
scale := math.Min(scaleX, scaleY)
|
|
||||||
|
|
||||||
// To convert a logical offscreen size to the actual screen size, use Math.floor to use smaller and safer
|
|
||||||
// values, or glitches can appear (#956).
|
|
||||||
width := int(math.Floor(float64(w) * scale))
|
|
||||||
height := int(math.Floor(float64(h) * scale))
|
|
||||||
x := (viewWidth - width) / 2
|
|
||||||
y := (viewHeight - height) / 2
|
|
||||||
|
|
||||||
if theState.isRunning() {
|
|
||||||
mobile.Get().SetScreenSizeAndScale(w, h, scale)
|
|
||||||
} else {
|
|
||||||
// The last argument 'title' is not used on mobile platforms, so just pass an empty string.
|
|
||||||
theState.errorCh = ebiten.RunWithoutMainLoop(theState.game.Update, w, h, scale, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
if viewRectSetter != nil {
|
|
||||||
viewRectSetter.SetViewRect(x, y, width, height)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
21
run.go
21
run.go
@ -238,19 +238,16 @@ func runGame(game Game, scale float64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunWithoutMainLoop runs the game, but don't call the loop on the main (UI) thread.
|
// RunGameWithoutMainLoop runs the game, but don't call the loop on the main (UI) thread.
|
||||||
// Different from Run, RunWithoutMainLoop returns immediately.
|
// Different from Run, RunGameWithoutMainLoop returns immediately.
|
||||||
//
|
//
|
||||||
// Ebiten users should NOT call RunWithoutMainLoop.
|
// Ebiten users should NOT call RunGameWithoutMainLoop.
|
||||||
// Instead, functions in github.com/hajimehoshi/ebiten/mobile package calls this.
|
// Instead, functions in github.com/hajimehoshi/ebiten/mobile package calls this.
|
||||||
func RunWithoutMainLoop(f func(*Image) error, width, height int, scale float64, title string) <-chan error {
|
func RunGameWithoutMainLoop(game Game) <-chan error {
|
||||||
game := &defaultGame{
|
game = &imageDumperGame{game: game}
|
||||||
update: (&imageDumper{f: f}).update,
|
fixWindowPosition(WindowSize())
|
||||||
width: width,
|
theUIContext.set(game, 0)
|
||||||
height: height,
|
return uiDriver().RunWithoutMainLoop(theUIContext)
|
||||||
}
|
|
||||||
theUIContext.set(game, scale)
|
|
||||||
return uiDriver().RunWithoutMainLoop(width, height, scale, title, theUIContext)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScreenSizeInFullscreen is deprecated as of 1.11.0-alpha.
|
// ScreenSizeInFullscreen is deprecated as of 1.11.0-alpha.
|
||||||
@ -270,7 +267,7 @@ 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")
|
||||||
}
|
}
|
||||||
theUIContext.SetScreenSize(width, height)
|
theUIContext.setScreenSize(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScreenScale is deprecated as of 1.11.0-alpha. Use SetWindowSize instead.
|
// SetScreenScale is deprecated as of 1.11.0-alpha. Use SetWindowSize instead.
|
||||||
|
@ -118,11 +118,10 @@ func (c *uiContext) getScaleForWindow() float64 {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetScreenSize sets the (logical) screen size and adjusts the window size.
|
// setScreenSize sets the (logical) screen size and adjusts the window size.
|
||||||
//
|
//
|
||||||
// SetScreenSize is for backward compatibility. This is called from ebiten.SetScreenSize and
|
// setScreenSize is for backward compatibility. This is called from ebiten.SetScreenSize.
|
||||||
// uidriver/mobile.UserInterface.
|
func (c *uiContext) setScreenSize(width, height int) {
|
||||||
func (c *uiContext) SetScreenSize(width, height int) {
|
|
||||||
c.m.Lock()
|
c.m.Lock()
|
||||||
defer c.m.Unlock()
|
defer c.m.Unlock()
|
||||||
|
|
||||||
@ -179,7 +178,7 @@ func (c *uiContext) updateOffscreen() {
|
|||||||
|
|
||||||
// The window size is automatically adjusted when Run is used.
|
// The window size is automatically adjusted when Run is used.
|
||||||
if _, ok := c.game.(*defaultGame); ok {
|
if _, ok := c.game.(*defaultGame); ok {
|
||||||
c.SetScreenSize(sw, sh)
|
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.
|
||||||
|
Loading…
Reference in New Issue
Block a user