mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-24 18:58:54 +01:00
parent
4676fd26f0
commit
ff51c4a2c7
28
event/event.go
Normal file
28
event/event.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2023 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.
|
||||
|
||||
package event
|
||||
|
||||
type MouseMoveEvent struct {
|
||||
X float64
|
||||
Y float64
|
||||
}
|
||||
|
||||
type MouseDownEvent struct {
|
||||
// TODO
|
||||
}
|
||||
|
||||
type MouseUpEvent struct {
|
||||
// TODO
|
||||
}
|
@ -16,6 +16,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"image"
|
||||
"image/color"
|
||||
_ "image/png"
|
||||
@ -25,6 +26,7 @@ import (
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
"github.com/hajimehoshi/ebiten/v2/event"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/images"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
)
|
||||
@ -38,6 +40,10 @@ const (
|
||||
screenHeight = 480
|
||||
)
|
||||
|
||||
var (
|
||||
flagEvent = flag.Bool("event", false, "use HandleEvent")
|
||||
)
|
||||
|
||||
// Sprite represents an image.
|
||||
type Sprite struct {
|
||||
image *ebiten.Image
|
||||
@ -117,6 +123,21 @@ func (t *TouchStrokeSource) IsJustReleased() bool {
|
||||
return inpututil.IsTouchJustReleased(t.ID)
|
||||
}
|
||||
|
||||
type MouseEventStrokeSource struct {
|
||||
pressed bool
|
||||
x float64
|
||||
y float64
|
||||
isJustReleased bool
|
||||
}
|
||||
|
||||
func (m *MouseEventStrokeSource) Position() (int, int) {
|
||||
return int(m.x), int(m.y)
|
||||
}
|
||||
|
||||
func (m *MouseEventStrokeSource) IsJustReleased() bool {
|
||||
return m.isJustReleased
|
||||
}
|
||||
|
||||
// Stroke manages the current drag state by mouse.
|
||||
type Stroke struct {
|
||||
source StrokeSource
|
||||
@ -160,9 +181,12 @@ func (s *Stroke) Sprite() *Sprite {
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
touchIDs []ebiten.TouchID
|
||||
strokes map[*Stroke]struct{}
|
||||
sprites []*Sprite
|
||||
touchIDs []ebiten.TouchID
|
||||
tickStrokes map[*Stroke]struct{}
|
||||
eventStroke *Stroke
|
||||
sprites []*Sprite
|
||||
|
||||
mouseEventStrokeSource MouseEventStrokeSource
|
||||
}
|
||||
|
||||
var (
|
||||
@ -205,8 +229,8 @@ func NewGame() *Game {
|
||||
|
||||
// Initialize the game.
|
||||
return &Game{
|
||||
strokes: map[*Stroke]struct{}{},
|
||||
sprites: sprites,
|
||||
tickStrokes: map[*Stroke]struct{}{},
|
||||
sprites: sprites,
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,11 +258,47 @@ func (g *Game) moveSpriteToFront(sprite *Sprite) {
|
||||
g.sprites = append(g.sprites, sprite)
|
||||
}
|
||||
|
||||
func (g *Game) HandleEvent(e any) {
|
||||
if !*flagEvent {
|
||||
return
|
||||
}
|
||||
|
||||
switch e := e.(type) {
|
||||
case event.MouseDownEvent:
|
||||
g.mouseEventStrokeSource.pressed = true
|
||||
g.mouseEventStrokeSource.isJustReleased = false
|
||||
case event.MouseMoveEvent:
|
||||
if g.mouseEventStrokeSource.pressed {
|
||||
g.mouseEventStrokeSource.x = e.X
|
||||
g.mouseEventStrokeSource.y = e.Y
|
||||
if g.eventStroke == nil {
|
||||
if sp := g.spriteAt(int(e.X), int(e.Y)); sp != nil {
|
||||
g.eventStroke = NewStroke(&g.mouseEventStrokeSource, sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
case event.MouseUpEvent:
|
||||
g.mouseEventStrokeSource.pressed = false
|
||||
g.mouseEventStrokeSource.isJustReleased = true
|
||||
}
|
||||
|
||||
if g.eventStroke != nil {
|
||||
g.eventStroke.Update()
|
||||
if !g.eventStroke.sprite.dragged {
|
||||
g.eventStroke = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
if *flagEvent {
|
||||
return nil
|
||||
}
|
||||
|
||||
if inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) {
|
||||
if sp := g.spriteAt(ebiten.CursorPosition()); sp != nil {
|
||||
s := NewStroke(&MouseStrokeSource{}, sp)
|
||||
g.strokes[s] = struct{}{}
|
||||
g.tickStrokes[s] = struct{}{}
|
||||
g.moveSpriteToFront(sp)
|
||||
}
|
||||
}
|
||||
@ -246,15 +306,15 @@ func (g *Game) Update() error {
|
||||
for _, id := range g.touchIDs {
|
||||
if sp := g.spriteAt(ebiten.TouchPosition(id)); sp != nil {
|
||||
s := NewStroke(&TouchStrokeSource{id}, sp)
|
||||
g.strokes[s] = struct{}{}
|
||||
g.tickStrokes[s] = struct{}{}
|
||||
g.moveSpriteToFront(sp)
|
||||
}
|
||||
}
|
||||
|
||||
for s := range g.strokes {
|
||||
for s := range g.tickStrokes {
|
||||
s.Update()
|
||||
if !s.sprite.dragged {
|
||||
delete(g.strokes, s)
|
||||
delete(g.tickStrokes, s)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -277,6 +337,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||
ebiten.SetWindowTitle("Drag & Drop (Ebitengine Demo)")
|
||||
if err := ebiten.RunGame(NewGame()); err != nil {
|
||||
|
@ -15,6 +15,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
@ -24,6 +25,7 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/colorm"
|
||||
"github.com/hajimehoshi/ebiten/v2/ebitenutil"
|
||||
"github.com/hajimehoshi/ebiten/v2/event"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -31,6 +33,10 @@ const (
|
||||
screenHeight = 480
|
||||
)
|
||||
|
||||
var (
|
||||
flagEvent = flag.Bool("event", false, "use HandleEvent")
|
||||
)
|
||||
|
||||
var (
|
||||
brushImage *ebiten.Image
|
||||
)
|
||||
@ -70,6 +76,8 @@ type Game struct {
|
||||
count int
|
||||
|
||||
canvasImage *ebiten.Image
|
||||
|
||||
mousePressedByEvent bool
|
||||
}
|
||||
|
||||
func NewGame() *Game {
|
||||
@ -80,14 +88,34 @@ func NewGame() *Game {
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *Game) HandleEvent(e any) {
|
||||
if !*flagEvent {
|
||||
return
|
||||
}
|
||||
|
||||
switch e := e.(type) {
|
||||
case event.MouseDownEvent:
|
||||
g.mousePressedByEvent = true
|
||||
case event.MouseMoveEvent:
|
||||
if g.mousePressedByEvent {
|
||||
g.paint(g.canvasImage, int(e.X), int(e.Y))
|
||||
g.count++
|
||||
}
|
||||
case event.MouseUpEvent:
|
||||
g.mousePressedByEvent = false
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
drawn := false
|
||||
var drawn bool
|
||||
|
||||
// Paint the brush by mouse dragging
|
||||
mx, my := ebiten.CursorPosition()
|
||||
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||
g.paint(g.canvasImage, mx, my)
|
||||
drawn = true
|
||||
if !*flagEvent {
|
||||
if ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) {
|
||||
g.paint(g.canvasImage, mx, my)
|
||||
drawn = true
|
||||
}
|
||||
}
|
||||
g.cursor = pos{
|
||||
x: mx,
|
||||
@ -142,6 +170,7 @@ func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||
ebiten.SetWindowTitle("Paint (Ebitengine Demo)")
|
||||
if err := ebiten.RunGame(NewGame()); err != nil {
|
||||
|
@ -114,6 +114,12 @@ func (g *gameForUI) NewScreenImage(width, height int) *ui.Image {
|
||||
return g.screen.image
|
||||
}
|
||||
|
||||
func (g *gameForUI) HandleEvent(event any) {
|
||||
if h, ok := g.game.(EventHandler); ok {
|
||||
h.HandleEvent(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *gameForUI) Layout(outsideWidth, outsideHeight float64) (float64, float64) {
|
||||
if l, ok := g.game.(LayoutFer); ok {
|
||||
return l.LayoutF(outsideWidth, outsideHeight)
|
||||
|
@ -192,6 +192,9 @@ func (q *commandQueue) Flush(graphicsDriver graphicsdriver.Graphics, endFrame bo
|
||||
}
|
||||
}
|
||||
}
|
||||
/*if endFrame {
|
||||
sync = true
|
||||
}*/
|
||||
|
||||
logger := debug.SwitchLogger()
|
||||
|
||||
|
@ -16,6 +16,7 @@ package ui
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/atlas"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/clock"
|
||||
@ -33,6 +34,7 @@ type Game interface {
|
||||
NewOffscreenImage(width, height int) *Image
|
||||
NewScreenImage(width, height int) *Image
|
||||
Layout(outsideWidth, outsideHeight float64) (screenWidth, screenHeight float64)
|
||||
HandleEvent(event any)
|
||||
UpdateInputState(fn func(*InputState))
|
||||
Update() error
|
||||
DrawOffscreen() error
|
||||
@ -40,7 +42,8 @@ type Game interface {
|
||||
}
|
||||
|
||||
type context struct {
|
||||
game Game
|
||||
game Game
|
||||
inputCh chan any
|
||||
|
||||
updateCalled bool
|
||||
|
||||
@ -55,12 +58,34 @@ type context struct {
|
||||
isOffscreenModified bool
|
||||
|
||||
skipCount int
|
||||
|
||||
gameM sync.Mutex
|
||||
}
|
||||
|
||||
func newContext(game Game) *context {
|
||||
return &context{
|
||||
game: game,
|
||||
c := &context{
|
||||
game: game,
|
||||
inputCh: make(chan any, 128),
|
||||
}
|
||||
|
||||
// Create an independent goroutine from Update/Draw not to cause deadlock at (*UserInterface).readPixels.
|
||||
go func() {
|
||||
// TODO: Abort this loop when the game terminates.
|
||||
for e := range c.inputCh {
|
||||
e := e
|
||||
func() {
|
||||
c.gameM.Lock()
|
||||
defer c.gameM.Unlock()
|
||||
c.game.HandleEvent(e)
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *context) sendInputEvent(event any) {
|
||||
c.inputCh <- event
|
||||
}
|
||||
|
||||
func (c *context) updateFrame(graphicsDriver graphicsdriver.Graphics, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface, swapBuffersForGL func()) error {
|
||||
@ -90,6 +115,18 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.updateGameInFrame(graphicsDriver, updateCount, outsideWidth, outsideHeight, deviceScaleFactor, ui, forceDraw, swapBuffersForGL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := atlas.SwapBuffers(graphicsDriver, swapBuffersForGL); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *context) updateGameInFrame(graphicsDriver graphicsdriver.Graphics, updateCount int, outsideWidth, outsideHeight float64, deviceScaleFactor float64, ui *UserInterface, forceDraw bool, swapBuffersForGL func()) (err error) {
|
||||
debug.Logf("----\n")
|
||||
|
||||
if err := atlas.BeginFrame(graphicsDriver); err != nil {
|
||||
@ -101,13 +138,11 @@ func (c *context) updateFrameImpl(graphicsDriver graphicsdriver.Graphics, update
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
|
||||
if err1 := atlas.SwapBuffers(graphicsDriver, swapBuffersForGL); err1 != nil && err == nil {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
c.gameM.Lock()
|
||||
defer c.gameM.Unlock()
|
||||
|
||||
// ForceUpdate can be invoked even if the context is not initialized yet (#1591).
|
||||
if w, h := c.layoutGame(outsideWidth, outsideHeight, deviceScaleFactor); w == 0 || h == 0 {
|
||||
return nil
|
||||
|
@ -19,6 +19,7 @@ package ui
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/event"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/gamepad"
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/glfw"
|
||||
)
|
||||
@ -51,6 +52,35 @@ func (u *UserInterface) registerInputCallbacks() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// For HandleEvent
|
||||
if _, err := u.window.SetCursorPosCallback(func(w *glfw.Window, xpos float64, ypos float64) {
|
||||
m, err := u.currentMonitor()
|
||||
if err != nil {
|
||||
u.setError(err)
|
||||
return
|
||||
}
|
||||
x := dipFromGLFWPixel(xpos, m)
|
||||
y := dipFromGLFWPixel(ypos, m)
|
||||
x, y = u.context.clientPositionToLogicalPosition(x, y, m.deviceScaleFactor())
|
||||
u.context.sendInputEvent(event.MouseMoveEvent{
|
||||
X: x,
|
||||
Y: y,
|
||||
})
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := u.window.SetMouseButtonCallback(func(w *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
|
||||
// TODO: Use button and mods
|
||||
switch action {
|
||||
case glfw.Release:
|
||||
u.context.sendInputEvent(event.MouseUpEvent{})
|
||||
case glfw.Press:
|
||||
u.context.sendInputEvent(event.MouseDownEvent{})
|
||||
}
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
4
run.go
4
run.go
@ -93,6 +93,10 @@ type LayoutFer interface {
|
||||
LayoutF(outsideWidth, outsideHeight float64) (screenWidth, screenHeight float64)
|
||||
}
|
||||
|
||||
type EventHandler interface {
|
||||
HandleEvent(event any)
|
||||
}
|
||||
|
||||
// FinalScreen represents the final screen image.
|
||||
// FinalScreen implements a part of Image functions.
|
||||
type FinalScreen interface {
|
||||
|
Loading…
Reference in New Issue
Block a user