diff --git a/examples/dragmascot/main.go b/examples/dragmascot/main.go new file mode 100644 index 000000000..8371e3a2d --- /dev/null +++ b/examples/dragmascot/main.go @@ -0,0 +1,130 @@ +// Copyright 2024 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. + +// Mascot is a desktop mascot on cross platforms. +// This is inspired by mattn's gopher (https://github.com/mattn/gopher). +package main + +import ( + "bytes" + "image" + _ "image/png" + "log" + + "github.com/hajimehoshi/ebiten/v2" + rmascot "github.com/hajimehoshi/ebiten/v2/examples/resources/images/mascot" + "github.com/hajimehoshi/ebiten/v2/inpututil" +) + +const ( + width = 200 + height = 200 +) + +var ( + gopher1 *ebiten.Image + gopher2 *ebiten.Image + gopher3 *ebiten.Image +) + +func init() { + // Decode an image from the image file's byte slice. + img1, _, err := image.Decode(bytes.NewReader(rmascot.Out01_png)) + if err != nil { + log.Fatal(err) + } + gopher1 = ebiten.NewImageFromImage(img1) + + img2, _, err := image.Decode(bytes.NewReader(rmascot.Out02_png)) + if err != nil { + log.Fatal(err) + } + gopher2 = ebiten.NewImageFromImage(img2) + + img3, _, err := image.Decode(bytes.NewReader(rmascot.Out03_png)) + if err != nil { + log.Fatal(err) + } + gopher3 = ebiten.NewImageFromImage(img3) +} + +type mascot struct { + count int + + dragging bool + dragStartWindowX int + dragStartWindowY int + dragStartCursorX int + dragStartCursorY int + + cursorToWindowX float64 + cursorToWindowY float64 +} + +func (m *mascot) Layout(outsideWidth, outsideHeight int) (int, int) { + // The cursor position is in a "logical" coordinate, which is determined by the game width and height. + // Calculate the factors to convert a cursor position to a window position. + m.cursorToWindowX = float64(outsideWidth) / float64(width) + m.cursorToWindowY = float64(outsideHeight) / float64(height) + return width, height +} + +func (m *mascot) Update() error { + if !ebiten.IsMouseButtonPressed(ebiten.MouseButtonLeft) { + m.dragging = false + } + if !m.dragging && inpututil.IsMouseButtonJustPressed(ebiten.MouseButtonLeft) { + m.dragging = true + m.dragStartWindowX, m.dragStartWindowY = ebiten.WindowPosition() + m.dragStartCursorX, m.dragStartCursorY = ebiten.CursorPosition() + } + if m.dragging { + cx, cy := ebiten.CursorPosition() + dx := int(float64(cx-m.dragStartCursorX) * m.cursorToWindowX) + dy := int(float64(cy-m.dragStartCursorY) * m.cursorToWindowY) + wx, wy := ebiten.WindowPosition() + ebiten.SetWindowPosition(wx+dx, wy+dy) + m.count++ + } + + return nil +} + +func (m *mascot) Draw(screen *ebiten.Image) { + img := gopher1 + if m.dragging { + switch (m.count / 3) % 4 { + case 0: + img = gopher1 + case 1, 3: + img = gopher2 + case 2: + img = gopher3 + } + } + screen.DrawImage(img, nil) +} + +func main() { + ebiten.SetWindowDecorated(false) + ebiten.SetWindowFloating(true) + ebiten.SetWindowSize(width, height) + + op := &ebiten.RunGameOptions{} + op.ScreenTransparent = true + op.SkipTaskbar = true + if err := ebiten.RunGameWithOptions(&mascot{}, op); err != nil { + log.Fatal(err) + } +}