mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2025-01-27 03:02:49 +01:00
ebiten: bug fix: use DrawTriangles as an implementation of Set
Closes #2154
This commit is contained in:
parent
7d725f3e58
commit
5411e8136b
126
image.go
126
image.go
@ -38,6 +38,16 @@ type Image struct {
|
|||||||
|
|
||||||
bounds image.Rectangle
|
bounds image.Rectangle
|
||||||
original *Image
|
original *Image
|
||||||
|
|
||||||
|
setVerticesCache map[[2]int][4]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var emptyImage *Image
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
img := NewImage(3, 3)
|
||||||
|
img.Fill(color.White)
|
||||||
|
emptyImage = img.SubImage(image.Rect(1, 1, 2, 2)).(*Image)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) copyCheck() {
|
func (i *Image) copyCheck() {
|
||||||
@ -46,6 +56,81 @@ func (i *Image) copyCheck() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *Image) resolveSetVerticesCacheIfNeeded() {
|
||||||
|
if i.isSubImage() {
|
||||||
|
i = i.original
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(i.setVerticesCache) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l := len(i.setVerticesCache)
|
||||||
|
vs := make([]float32, l*graphics.VertexFloatNum*4)
|
||||||
|
is := make([]uint16, l*6)
|
||||||
|
sx, sy := emptyImage.adjustPositionF32(1, 1)
|
||||||
|
var idx uint16
|
||||||
|
for p, c := range i.setVerticesCache {
|
||||||
|
dx := float32(p[0])
|
||||||
|
dy := float32(p[1])
|
||||||
|
|
||||||
|
var crf, cgf, cbf, caf float32
|
||||||
|
if c[3] != 0 {
|
||||||
|
crf = float32(c[0]) / float32(c[3])
|
||||||
|
cgf = float32(c[1]) / float32(c[3])
|
||||||
|
cbf = float32(c[2]) / float32(c[3])
|
||||||
|
caf = float32(c[3]) / 0xff
|
||||||
|
}
|
||||||
|
|
||||||
|
vs[graphics.VertexFloatNum*4*idx] = dx
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+1] = dy
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+2] = sx
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+3] = sy
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+4] = crf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+5] = cgf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+6] = cbf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+7] = caf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+8] = dx + 1
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+9] = dy
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+10] = sx + 1
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+11] = sy
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+12] = crf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+13] = cgf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+14] = cbf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+15] = caf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+16] = dx
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+17] = dy + 1
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+18] = sx
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+19] = sy + 1
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+20] = crf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+21] = cgf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+22] = cbf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+23] = caf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+24] = dx + 1
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+25] = dy + 1
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+26] = sx + 1
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+27] = sy + 1
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+28] = crf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+29] = cgf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+30] = cbf
|
||||||
|
vs[graphics.VertexFloatNum*4*idx+31] = caf
|
||||||
|
|
||||||
|
is[6*idx] = 4 * idx
|
||||||
|
is[6*idx+1] = 4*idx + 1
|
||||||
|
is[6*idx+2] = 4*idx + 2
|
||||||
|
is[6*idx+3] = 4*idx + 1
|
||||||
|
is[6*idx+4] = 4*idx + 2
|
||||||
|
is[6*idx+5] = 4*idx + 3
|
||||||
|
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
|
||||||
|
srcs := [graphics.ShaderImageNum]*ui.Image{emptyImage.image}
|
||||||
|
i.image.DrawTriangles(srcs, vs, is, affine.ColorMIdentity{}, graphicsdriver.CompositeModeSourceOver, graphicsdriver.FilterNearest, graphicsdriver.AddressUnsafe, i.adjustedRegion(), graphicsdriver.Region{}, [graphics.ShaderImageNum - 1][2]float32{}, nil, nil, false, true)
|
||||||
|
|
||||||
|
i.setVerticesCache = nil
|
||||||
|
}
|
||||||
|
|
||||||
// Size returns the size of the image.
|
// Size returns the size of the image.
|
||||||
func (i *Image) Size() (width, height int) {
|
func (i *Image) Size() (width, height int) {
|
||||||
s := i.Bounds().Size()
|
s := i.Bounds().Size()
|
||||||
@ -72,6 +157,10 @@ func (i *Image) Clear() {
|
|||||||
// When the image is disposed, Fill does nothing.
|
// When the image is disposed, Fill does nothing.
|
||||||
func (i *Image) Fill(clr color.Color) {
|
func (i *Image) Fill(clr color.Color) {
|
||||||
i.copyCheck()
|
i.copyCheck()
|
||||||
|
if i.isDisposed() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i.setVerticesCache = nil
|
||||||
|
|
||||||
var crf, cgf, cbf, caf float32
|
var crf, cgf, cbf, caf float32
|
||||||
cr, cg, cb, ca := clr.RGBA()
|
cr, cg, cb, ca := clr.RGBA()
|
||||||
@ -198,6 +287,9 @@ func (i *Image) DrawImage(img *Image, options *DrawImageOptions) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
img.resolveSetVerticesCacheIfNeeded()
|
||||||
|
i.resolveSetVerticesCacheIfNeeded()
|
||||||
|
|
||||||
// Calculate vertices before locking because the user can do anything in
|
// Calculate vertices before locking because the user can do anything in
|
||||||
// options.ImageParts interface without deadlock (e.g. Call Image functions).
|
// options.ImageParts interface without deadlock (e.g. Call Image functions).
|
||||||
if options == nil {
|
if options == nil {
|
||||||
@ -346,6 +438,9 @@ func (i *Image) DrawTriangles(vertices []Vertex, indices []uint16, img *Image, o
|
|||||||
}
|
}
|
||||||
// TODO: Check the maximum value of indices and len(vertices)?
|
// TODO: Check the maximum value of indices and len(vertices)?
|
||||||
|
|
||||||
|
img.resolveSetVerticesCacheIfNeeded()
|
||||||
|
i.resolveSetVerticesCacheIfNeeded()
|
||||||
|
|
||||||
if options == nil {
|
if options == nil {
|
||||||
options = &DrawTrianglesOptions{}
|
options = &DrawTrianglesOptions{}
|
||||||
}
|
}
|
||||||
@ -451,6 +546,8 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
|||||||
}
|
}
|
||||||
// TODO: Check the maximum value of indices and len(vertices)?
|
// TODO: Check the maximum value of indices and len(vertices)?
|
||||||
|
|
||||||
|
i.resolveSetVerticesCacheIfNeeded()
|
||||||
|
|
||||||
if options == nil {
|
if options == nil {
|
||||||
options = &DrawTrianglesShaderOptions{}
|
options = &DrawTrianglesShaderOptions{}
|
||||||
}
|
}
|
||||||
@ -495,6 +592,7 @@ func (i *Image) DrawTrianglesShader(vertices []Vertex, indices []uint16, shader
|
|||||||
panic("ebiten: all the source images must be the same size with the rectangle")
|
panic("ebiten: all the source images must be the same size with the rectangle")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
img.resolveSetVerticesCacheIfNeeded()
|
||||||
imgs[i] = img.image
|
imgs[i] = img.image
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -570,6 +668,8 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.resolveSetVerticesCacheIfNeeded()
|
||||||
|
|
||||||
if options == nil {
|
if options == nil {
|
||||||
options = &DrawRectShaderOptions{}
|
options = &DrawRectShaderOptions{}
|
||||||
}
|
}
|
||||||
@ -587,6 +687,7 @@ func (i *Image) DrawRectShader(width, height int, shader *Shader, options *DrawR
|
|||||||
if w, h := img.Size(); width != w || height != h {
|
if w, h := img.Size(); width != w || height != h {
|
||||||
panic("ebiten: all the source images must be the same size with the rectangle")
|
panic("ebiten: all the source images must be the same size with the rectangle")
|
||||||
}
|
}
|
||||||
|
img.resolveSetVerticesCacheIfNeeded()
|
||||||
imgs[i] = img.image
|
imgs[i] = img.image
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -703,14 +804,18 @@ func (i *Image) RGBA64At(x, y int) color.RGBA64 {
|
|||||||
return color.RGBA64{uint16(r) * 0x101, uint16(g) * 0x101, uint16(b) * 0x101, uint16(a) * 0x101}
|
return color.RGBA64{uint16(r) * 0x101, uint16(g) * 0x101, uint16(b) * 0x101, uint16(a) * 0x101}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Image) at(x, y int) (r, g, b, a uint8) {
|
func (i *Image) at(x, y int) (r, g, b, a byte) {
|
||||||
if i.isDisposed() {
|
if i.isDisposed() {
|
||||||
return 0, 0, 0, 0
|
return 0, 0, 0, 0
|
||||||
}
|
}
|
||||||
if !image.Pt(x, y).In(i.Bounds()) {
|
if !image.Pt(x, y).In(i.Bounds()) {
|
||||||
return 0, 0, 0, 0
|
return 0, 0, 0, 0
|
||||||
}
|
}
|
||||||
return i.image.At(i.adjustPosition(x, y))
|
x, y = i.adjustPosition(x, y)
|
||||||
|
if c, ok := i.setVerticesCache[[2]int{x, y}]; ok {
|
||||||
|
return c[0], c[1], c[2], c[3]
|
||||||
|
}
|
||||||
|
return i.image.At(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set sets the color at (x, y).
|
// Set sets the color at (x, y).
|
||||||
@ -732,9 +837,17 @@ func (i *Image) Set(x, y int, clr color.Color) {
|
|||||||
i = i.original
|
i = i.original
|
||||||
}
|
}
|
||||||
|
|
||||||
r, g, b, a := clr.RGBA()
|
if i.setVerticesCache == nil {
|
||||||
x, y = i.adjustPosition(x, y)
|
i.setVerticesCache = map[[2]int][4]byte{}
|
||||||
i.image.ReplacePixels([]byte{byte(r >> 8), byte(g >> 8), byte(b >> 8), byte(a >> 8)}, x, y, 1, 1)
|
}
|
||||||
|
dx, dy := i.adjustPosition(x, y)
|
||||||
|
cr, cg, cb, ca := clr.RGBA()
|
||||||
|
i.setVerticesCache[[2]int{dx, dy}] = [4]byte{byte(cr / 0x101), byte(cg / 0x101), byte(cb / 0x101), byte(ca / 0x101)}
|
||||||
|
// TODO: This fails with 2049 or more. In theory this should work with 10000. Investigate why (#2178).
|
||||||
|
// Probably this is because the vertices number exceeds 65536 (=2048*32), but why is there such a limitation?
|
||||||
|
if len(i.setVerticesCache) >= 2048 {
|
||||||
|
i.resolveSetVerticesCacheIfNeeded()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispose disposes the image data.
|
// Dispose disposes the image data.
|
||||||
@ -757,6 +870,7 @@ func (i *Image) Dispose() {
|
|||||||
}
|
}
|
||||||
i.image.MarkDisposed()
|
i.image.MarkDisposed()
|
||||||
i.image = nil
|
i.image = nil
|
||||||
|
i.setVerticesCache = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplacePixels replaces the pixels of the image with p.
|
// ReplacePixels replaces the pixels of the image with p.
|
||||||
@ -776,6 +890,8 @@ func (i *Image) ReplacePixels(pixels []byte) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i.resolveSetVerticesCacheIfNeeded()
|
||||||
|
|
||||||
r := i.Bounds()
|
r := i.Bounds()
|
||||||
x, y := i.adjustPosition(r.Min.X, r.Min.Y)
|
x, y := i.adjustPosition(r.Min.X, r.Min.Y)
|
||||||
// Do not need to copy pixels here.
|
// Do not need to copy pixels here.
|
||||||
|
121
internal/processtest/testdata/issue2154_1.go
vendored
Normal file
121
internal/processtest/testdata/issue2154_1.go
vendored
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright 2022 The Ebitengine Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build ignore
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var regularTermination = errors.New("regular termination")
|
||||||
|
|
||||||
|
var srcInit *ebiten.Image
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
const (
|
||||||
|
w = 2
|
||||||
|
h = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
//src2 := ebiten.NewImage(1, 1)
|
||||||
|
|
||||||
|
src0 := ebiten.NewImage(w, h)
|
||||||
|
src0.Fill(color.RGBA{0xff, 0xff, 0xff, 0xff})
|
||||||
|
src0.Set(0, 0, color.RGBA{0, 0, 0, 0xff})
|
||||||
|
src0.Set(0, 1, color.RGBA{0, 0, 0, 0xff})
|
||||||
|
src0.Set(1, 0, color.RGBA{0, 0, 0, 0xff})
|
||||||
|
|
||||||
|
src1 := ebiten.NewImage(w, h)
|
||||||
|
// Using the image as a source just after Set caused troubles on Metal.
|
||||||
|
// For example, inserting src1.Fill(color.RGBA{0, 0xff, 0, 0xff}) here hid the error.
|
||||||
|
src1.DrawImage(src0, nil)
|
||||||
|
|
||||||
|
srcInit = src1
|
||||||
|
}
|
||||||
|
|
||||||
|
type Game struct {
|
||||||
|
count int
|
||||||
|
dst *ebiten.Image
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Update() error {
|
||||||
|
g.count++
|
||||||
|
if g.count == 16 {
|
||||||
|
return regularTermination
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
|
screen.Fill(color.RGBA{0xff, 0xff, 0xff, 0xff})
|
||||||
|
screen.DrawImage(srcInit, nil)
|
||||||
|
|
||||||
|
if g.dst == nil {
|
||||||
|
g.dst = ebiten.NewImage(screen.Size())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.dst.DrawImage(screen, nil)
|
||||||
|
if got, want := g.dst.At(0, 0), (color.RGBA{0, 0, 0, 0xff}); got != want {
|
||||||
|
panic(fmt.Sprintf("count: %d, got: %v, want: %v", g.count, got, want))
|
||||||
|
}
|
||||||
|
if got, want := g.dst.At(1, 1), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want {
|
||||||
|
panic(fmt.Sprintf("count: %d, got: %v, want: %v", g.count, got, want))
|
||||||
|
}
|
||||||
|
g.dst.Clear()
|
||||||
|
|
||||||
|
const (
|
||||||
|
w = 2
|
||||||
|
h = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
src0 := ebiten.NewImage(w, h)
|
||||||
|
defer src0.Dispose()
|
||||||
|
src0.Fill(color.RGBA{0xff, 0xff, 0xff, 0xff})
|
||||||
|
src0.Set(0, 0, color.RGBA{0, 0, 0, 0xff})
|
||||||
|
src0.Set(0, 1, color.RGBA{0, 0, 0, 0xff})
|
||||||
|
src0.Set(1, 0, color.RGBA{0, 0, 0, 0xff})
|
||||||
|
|
||||||
|
src1 := ebiten.NewImage(w, h)
|
||||||
|
defer src1.Dispose()
|
||||||
|
src1.DrawImage(src0, nil)
|
||||||
|
|
||||||
|
screen.Fill(color.RGBA{0xff, 0xff, 0xff, 0xff})
|
||||||
|
screen.DrawImage(src1, nil)
|
||||||
|
|
||||||
|
g.dst.DrawImage(screen, nil)
|
||||||
|
if got, want := g.dst.At(0, 0), (color.RGBA{0, 0, 0, 0xff}); got != want {
|
||||||
|
panic(fmt.Sprintf("count: %d, got: %v, want: %v", g.count, got, want))
|
||||||
|
}
|
||||||
|
if got, want := g.dst.At(1, 1), (color.RGBA{0xff, 0xff, 0xff, 0xff}); got != want {
|
||||||
|
panic(fmt.Sprintf("count: %d, got: %v, want: %v", g.count, got, want))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Layout(width, height int) (int, int) {
|
||||||
|
return width, height
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := ebiten.RunGame(&Game{}); err != nil && err != regularTermination {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
92
internal/processtest/testdata/issue2154_2.go
vendored
Normal file
92
internal/processtest/testdata/issue2154_2.go
vendored
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Copyright 2022 The Ebitengine Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
//go:build ignore
|
||||||
|
// +build ignore
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"image/color"
|
||||||
|
|
||||||
|
"github.com/hajimehoshi/ebiten/v2"
|
||||||
|
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var regularTermination = errors.New("regular termination")
|
||||||
|
|
||||||
|
var (
|
||||||
|
baseImage *ebiten.Image
|
||||||
|
derivedImage *ebiten.Image
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
const (
|
||||||
|
w = 36
|
||||||
|
h = 40
|
||||||
|
)
|
||||||
|
|
||||||
|
baseImage = ebiten.NewImage(w, h)
|
||||||
|
derivedImage = ebiten.NewImage(w, h)
|
||||||
|
|
||||||
|
baseImage.Fill(color.White)
|
||||||
|
for j := 0; j < h; j++ {
|
||||||
|
for i := 0; i < w; i++ {
|
||||||
|
baseImage.Set(j, i, color.Black)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
derivedImage.DrawImage(baseImage, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Game struct {
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Update() error {
|
||||||
|
g.count++
|
||||||
|
if g.count == 16 {
|
||||||
|
return regularTermination
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Draw(screen *ebiten.Image) {
|
||||||
|
screen.Fill(color.White)
|
||||||
|
|
||||||
|
screen.DrawImage(derivedImage, nil)
|
||||||
|
if g.count >= 8 {
|
||||||
|
if got, want := screen.At(0, 0), (color.RGBA{0, 0, 0, 0xff}); got != want {
|
||||||
|
panic(fmt.Sprintf("got: %v, want: %v", got, want))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The blow 3 line matters to reproduce #2154.
|
||||||
|
mx, my := ebiten.CursorPosition()
|
||||||
|
msg := fmt.Sprintf("TPS: %.01f; FPS: %.01f; cursor: (%d, %d)", ebiten.CurrentTPS(), ebiten.CurrentFPS(), mx, my)
|
||||||
|
ebitenutil.DebugPrint(screen, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||||
|
return 640, 480
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ebiten.SetWindowTitle("Test")
|
||||||
|
|
||||||
|
if err := ebiten.RunGame(&Game{}); err != nil && err != regularTermination {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user