godosa-engine

git clone https://git.godosa.eu/godosa-engine

master

raw · 3497 bytes

Avalonia / Desktop gotchas

Each one cost debugging time once. The code has comments at each site; this file is the index.

  • The GL view isn't hit-testable by default. OpenGlControlBase draws through GL, not Avalonia's scene. Pointer events and Cursor go to the panel beneath unless the control implements ICustomHitTest (GameView.HitTest). Display-only overlays need IsHitTestVisible = false so the pointer reaches the game.
  • Deactivation ≠ lost focus. Clicking another app deactivates the window but keeps logical focus (IsFocused stays true), so OnLostFocus never fires. Release held keys on Window.Deactivated too, because OnKeyUp never arrives for a key released elsewhere.
  • The sim only advances while frames render. GameLoop.Frame runs inside OnOpenGlRender. Stopping RequestNextFrameRendering (paused) stops the sim, and "keep simulating while minimized" would need a timer.
  • The render callback runs on the UI thread (verified on this Linux/X11 setup). The overlay and FrameTimeStats share state without locks for that reason. Re-check if a platform moves rendering off-thread.
  • No pointer lock API in Avalonia 12.1.2. Free look reads deltas between pointer positions and stalls at the window edge. Recentering uses a platform call (CursorWarp, X11 XWarpPointer; off where it fails).
  • Escape vs focused controls. A clicked CheckBox takes keyboard focus, so Esc for the options menu is a window-level tunnel handler, not GameView.OnKeyDown.
  • Tests: float vs double. FixedStepClock.TickSeconds is float. Compare it against double arithmetic with a tolerance (~1e-6), not 9 places.
  • A clicked in-game Button steals the game's keys. It takes focus, so WASD stops reaching GameView and Space presses the button. HUD buttons are Focusable = false (Ui.Action).
  • Raising Button.ClickEvent doesn't toggle a CheckBox. Toggle logic lives in ToggleButton.OnClick; scripted/pad presses use Ui.Press.
  • KeyBindings inside a control resolves to Avalonia's InputElement.KeyBindings, not ours — qualify the game's own KeyBindings type fully.
  • Invisible controls aren't templated. IsVisible = false skips measure/template, so the first real open pays for it (first NPC dialog ~170 ms). Ui.WarmUp() shows a zero-opacity card briefly instead.
  • Routed key events only reach controls on the route. A KeyDown raised on the focused GameView tunnels window → GameScreen → GameView; a sibling panel (options) never sees it. Screen-wide key handling lives on GameScreen (e.g. key-rebind capture).
  • Scripted input vs X injection. On KDE Wayland xdotool keys don't reach a focused XWayland window (and may type into the user's terminal); drive smoke runs with smoke_run.sh --input FILE (routed events in-process).
  • The HUD is redrawn every frame over the GL view. The GL control changes every frame and covers the window, so the compositor re-draws every overlay each frame — text is the expensive part. CacheMode = new BitmapCache() on HUD cards (Ui.Card) makes them re-render only on change; set RenderAtScale to any scale transform above them or they blur. Merging text blocks into fewer visuals doesn't help.
  • Don't judge perf by CPU % on this machine (±50 % with other load). Use the instr: smoke step, interleaved runs, PauseWhenInactive off; see game-loop spec §7.