Ebitengine

repository·main·Indexed 12 days ago

https://github.com/hajimehoshi/ebiten

A simple 2D game engine for the Go programming language designed for rapid development and cross-platform deployment. It supports desktop (Windows, macOS, Linux, FreeBSD), mobile (Android, iOS), web (WebAssembly), and consoles (Nintendo Switch, Xbox). Core features include 2D graphics with custom shaders, input handling for mouse/keyboard/gamepads/touch, and audio support for Ogg/Vorbis, MP3, WAV, and PCM formats.

Tokens
19.3K
Snippets
62
Records
91
Agent score
96%

What's inside Ebitengine

  1. Overview of Ebitengine (v2)

    main

    Ebitengine (formerly Ebiten) is a dead simple 2D game engine for the Go programming language. It provides a simple API for developing 2D games that can be deployed across multiple platforms, including desktop, mobile, web, and consoles.

    Core Features

    • 2D Graphics: Supports geometry and color transformation via matrices, various composition modes, offscreen rendering, text rendering, automatic batching, automatic texture atlases, and custom shaders.
    • Input: Supports Mouse, Keyboard, Gamepads, and Touches.
    • Audio: Supports Ogg/Vorbis, MP3, WAV, and PCM formats.
  2. Understand the oksvg fork and its limitations

    main

    The oksvg package in this repository is a fork of github.com/srwiley/oksvg (v0.0.0-20221011165216-be6e8873101c). It is specifically modified to support OpenType SVG glyph documents used in color emoji fonts.

    Key Modifications

    • Gradient Rendering: Fixed gradient rendering under drawing transforms. SvgPath.DrawTransformed now uses Gradient.GetColorFunctionUS(opacity, objMatrix) to correctly handle gradientUnits="userSpaceOnUse" when a transform is applied.
    • Dependency Removal: Removed golang.org/x/net/html/charset to simplify UTF-8 handling via encoding/xml.
    • Modernization: Updated code to use modern Go idioms (e.g., any instead of interface{}).

    Important Limitations for OpenType SVG

    This package implements a subset of SVG 1.1. While it supports Noto Color Emoji fonts, other fonts may fail to render correctly if they use unsupported features. When using IgnoreErrorMode, unsupported constructs are skipped silently, which may result in missing shapes or incorrect colors.

    Unsupported features include:

    • <clipPath> elements and the clip-path property.
    • currentColor (gradient stops do not inherit it).
    • <image> elements with embedded base64 data.
    • The evenodd fill-rule (only nonzero is supported).
    • Gradient templating via xlink:href on gradients.
    • CSS var() functions and CPAL palette colors referenced as custom properties.
    • Inline style attributes on gradient <stop> elements (only presentation attributes like stop-color are supported).
  3. Run Ebitengine apps headlessly with vmhost

    main

    You can run an Ebitengine app (implementing ebiten.Game) headlessly and programmatically using the experimental exp/vmhost package. This allows you to test, debug, or capture screenshots without a visible window.

    Mental Model

    • Guest: The app under test. It connects to a host over a socket instead of opening a window, forwarding graphics commands.
    • Host: A small driver program that runs the guest. It uses ebiten.SetWindowVisible(false), replays the guest's draw commands on the real GPU, and provides a vmhost.GuestSession to control the guest.

    Setup via Endpoints

    The guest connects to the host via an endpoint URL (e.g., unix:///path/to/socket or tcp://127.0.0.1:PORT).

    • Option A (Recommended): Build the guest with -tags ebitenginevm. The guest will automatically read the EBITENGINE_VM_ENDPOINT environment variable provided by the driver.
    • Option B: Manually set RunGameOptions.VMGuestEndpoint in the app's code.
  4. How the driver controls the guest lifecycle

    main

    The host driver controls the guest's execution through a specific sequence of calls within the host's Update loop. This allows for deterministic, high-speed execution (faster than real-time).

    Execution Sequence

    1. d.guest.AdvanceTicks(n): Queues n ticks to run back-to-back. This compresses wall-clock time because the guest is not paced to the host's TPS.
    2. d.guest.AdvanceFrame(): Requests the final frame after the ticks have run.
    3. d.guest.WaitFrame(): Blocks until all queued ticks have run and the frame is rendered.
    4. d.guest.CompositeFrame(): Composites the guest's frame into the host's screen image.

    Error Handling

    • guest.Err(): Becomes non-nil if the guest terminates, crashes, or times out.
    • A panicking guest will print its stack trace to stderr and stop the driver.
  5. How ticks and TPS work in headless mode

    main

    In a headless session, the host controls the execution rate of the guest. A 'tick' corresponds to a single call to the guest's Update method. You can run a specific number of ticks using AdvanceTicks(n).

    Important considerations for timing:

    • Deterministic Ticks: The guest's TPS (Ticks Per Second) does not throttle or scale the execution. The host decides how many ticks to run and at what real-time rate.
    • Simulating Real Time: To simulate a specific duration of game time, you must convert seconds to ticks using the guest's requested TPS. Multiply the desired seconds by d.guest.RequestedTPS(). For example, in a 30-TPS game, one second is simulated by AdvanceTicks(30).
    • SyncWithFPS: If RequestedTPS() returns ebiten.SyncWithFPS (-1), the game ties its ticks to rendered frames. In this case, there is no fixed seconds-to-ticks conversion; you must treat each tick as a single frame.
    // To simulate 1 second in a 30-TPS game:
    d.guest.RequestedTPS() // returns 30
    d.AdvanceTicks(30)
  6. Supported Platforms for Ebitengine

    main

    Ebitengine supports a wide range of platforms. Note that some platforms require Cgo to be enabled.

    • Desktop: Windows, macOS, Linux, FreeBSD
    • Mobile: Android (Cgo required), iOS (Cgo required)
    • Web: WebAssembly
    • Consoles: Nintendo Switch (Cgo required), Xbox (Cgo required; support is limited and access is restricted)
  7. Capture a single frame from an Ebitengine app

    main

    To quickly capture a single screenshot of an Ebitengine app without writing custom input logic, use the provided driver template. Run the driver from the ebiten repository root, pointing to the package you want to test.

    Command:

    go run ./skills/run-ebitengine-app-headless/_driver \
      -pkg ./examples/rotate -ticks 60 -out /tmp/frame.png

    Flags:

    • -pkg: The path to the guest package.
    • -ticks: Number of ticks to run before dumping the frame and exiting.
    • -out: Path where the PNG will be saved.
    • -w/-h: Logical screen size (defaults to 320×240).
  8. Run Ebitengine apps as a guest in Go tests

    main

    To perform end-to-end testing of an application within a go test suite rather than using a standalone driver, you can use the internal testing helpers.

    Note: These helpers use internal/testing's MainWithRunLoop, which is only importable from within the Ebiten module itself. You can find implementation patterns in:

    • exp/vmhost/guest_test.go (using startGuest and tickAndFrame helpers)
    • exp/vmhost/readpixels_test.go
  9. Configure image blending with the Blend struct

    main

    The Blend struct defines how source colors (the image being drawn) and destination colors (the existing content on the target) are combined.

    To create a custom blend mode, you must specify both Blend Factors (multipliers for source/destination values) and Blend Operations (the mathematical operator used to combine them).

    The final color is calculated as:

    • c_out = BlendOperationRGB((BlendFactorSourceRGB) × c_src, (BlendFactorDestinationRGB) × c_dst)
    • α_out = BlendOperationAlpha((BlendFactorSourceAlpha) × α_src, (BlendFactorDestinationAlpha) × α_dst)

    Note: c_src, c_dst, and c_out represent alpha-premultiplied RGB values.

    blend := ebiten.Blend{
        BlendFactorSourceRGB:        ebiten.BlendFactorOne,
        BlendFactorDestinationRGB:   ebiten.BlendFactorZero,
        BlendOperationRGB:           ebiten.BlendOperationAdd,
        // ... other fields
    }
  10. Use ColorM for color matrix transformations

    main

    A ColorM represents a matrix used to transform colors when rendering an image. It operates on straight alpha colors, even though Ebitengine images use alpha-premultiplied formats. The engine automatically handles un-multiplying colors before applying the matrix and re-multiplying them afterward.

    Note: ColorM is deprecated as of v2.5. For new projects, use the colorm package or ColorScale instead.

    // Example of initializing a ColorM (though deprecated)
    var cm ebiten.ColorM
  11. How LayoutFer and Layout work together

    main

    Ebitengine provides two ways to handle screen scaling via the Layout method:

    • Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int): The standard method using integer pixel dimensions.
    • LayoutF(outsideWidth, outsideHeight float64) (screenWidth, screenHeight float64): A float-based version for higher precision.

    Note: If your Game implements LayoutFer, Ebitengine will call LayoutF and ignore the standard Layout method.