diff --git a/examples/capture/main.go b/examples/capture/main.go new file mode 100644 index 000000000..20bd1e443 --- /dev/null +++ b/examples/capture/main.go @@ -0,0 +1,132 @@ +// 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 main + +import ( + "fmt" + "image/color" + "log" + "runtime" + "sync" + + "github.com/hajimehoshi/ebiten/v2" + "github.com/hajimehoshi/ebiten/v2/ebitenutil" + "github.com/hajimehoshi/ebiten/v2/inpututil" +) + +var pointerImage = ebiten.NewImage(8, 8) + +func init() { + pointerImage.Fill(color.RGBA{0xff, 0, 0, 0xff}) +} + +const ( + screenWidth = 640 + screenHeight = 480 +) + +type Game struct { + initOnce sync.Once + mouseX int + mouseY int + x int + y int +} + +func (g *Game) Update() error { + g.initOnce.Do(func() { + // initialize cursor position on first update + g.mouseX, g.mouseY = ebiten.CursorPosition() + }) + + if ebiten.CursorMode() == ebiten.CursorModeCaptured { + cursorX, cursorY := ebiten.CursorPosition() + deltaX, deltaY := cursorX-g.mouseX, cursorY-g.mouseY + g.mouseX, g.mouseY = cursorX, cursorY + + if deltaX != 0 { + g.x += deltaX + } + + if deltaY != 0 { + g.y += deltaY + } + + // Constrain red dot within screen view. + if g.x < 0 { + g.x = 0 + } else if g.x > screenWidth-pointerImage.Bounds().Dx() { + g.x = screenWidth - pointerImage.Bounds().Dx() + } + + if g.y < 0 { + g.y = 0 + } else if g.y > screenHeight-pointerImage.Bounds().Dy() { + g.y = screenHeight - pointerImage.Bounds().Dy() + } + } + + if inpututil.IsKeyJustPressed(ebiten.KeySpace) { + if ebiten.CursorMode() == ebiten.CursorModeCaptured { + // Release mouse cursor capture. + ebiten.SetCursorMode(ebiten.CursorModeVisible) + } else { + // Recapture mouse cursor. + ebiten.SetCursorMode(ebiten.CursorModeCaptured) + + // Reset mouse cursor position for its return. + g.mouseX, g.mouseY = ebiten.CursorPosition() + } + } + + return nil +} + +func (g *Game) Draw(screen *ebiten.Image) { + op := &ebiten.DrawImageOptions{} + op.GeoM.Translate(float64(g.x), float64(g.y)) + screen.DrawImage(pointerImage, op) + + var message string + if ebiten.CursorMode() == ebiten.CursorModeCaptured { + message = fmt.Sprintf("Move the red point with mouse captured\nPress Space to release mouse capture\n(%d, %d)", g.x, g.y) + } else { + message = fmt.Sprintf("The red point can only move when mouse captured\nPress Space to capture mouse\n(%d, %d)", g.x, g.y) + } + ebitenutil.DebugPrint(screen, message) +} + +func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) { + return screenWidth, screenHeight +} + +func main() { + ebiten.SetWindowTitle("Mouse Capture (Ebitengine Demo)") + ebiten.SetWindowSize(screenWidth, screenHeight) + + // Web browsers allow cursor mode capture only with user interaction. + // Start without cursor captured with message indicating how to capture. + if runtime.GOOS != "js" { + ebiten.SetCursorMode(ebiten.CursorModeCaptured) + } + + g := &Game{ + x: screenWidth / 2, + y: screenHeight / 2, + } + if err := ebiten.RunGame(g); err != nil { + log.Fatal(err) + } +}