Skip to content

Game Builder: visual level editor + streaming/region gaming API#5253

Open
shai-almog wants to merge 28 commits into
masterfrom
game-builder
Open

Game Builder: visual level editor + streaming/region gaming API#5253
shai-almog wants to merge 28 commits into
masterfrom
game-builder

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

Overview

Adds a visual game level/map editor for the com.codename1.gaming API, the mode-aware authoring data model it builds on, and a Maven workflow to launch it — plus a new streaming/region game-space engine for large/open worlds.

What's included

Core gaming data model (com.codename1.gaming.level)

  • GameLevel / GameElement / Layer / TileLayer / AssetCatalog / IsoProjection with JSON load/save across 2D, 3D and board modes, realized into Sprite/Model.
  • Streaming/region engine for large worlds: pluggable Material + MaterialRegistry; chunked StreamingTerrain (ChunkProvider, LRU paging, negative coordinates); variable-size TerrainFeature (walls/ramps/platforms); loadable Region + RegionProvider + GameWorld with seamless neighbour streaming. GameLevel persists an optional GameWorld.

Editor (scripts/gamebuilder, Java-17 CN1 app)

  • Multi-panel editor (hierarchy, asset palette, inspector) for 2D/3D/board authoring.
  • Terrain sculpting: height, holes, walls, pluggable surface materials, smooth (interpolated) slopes.
  • Per-game-type 3D preview (open / flight / race / dungeon) with a radar, light/dark theming.
  • Large World mode: a region-graph scene that edits/previews the active region's streaming terrain.

Maven plugin

  • cn1:create-game-scene and cn1:gamebuilder goals (Java-17 gated).
  • gamebuilder-release reactor profile to build/deploy com.codenameone:codenameone-gamebuilder at the CN1 version.

Core fixes (desktop)

  • Hover tooltips delivered on desktop (JavaSEPort); TooltipManager detached-anchor guard; Form.pointerPressed dismisses tooltips on press.

Tests & docs

  • GameLevelTest + GameWorldTest (core-unittests); headless validation harnesses for the editor.
  • Three dated blog tutorials (2D platformer, board, 3D dungeon) with a screenshot-generating test that illustrates each step.
  • Developer-guide Game Builder and Game Assets chapters.

Notes

  • Excludes two unrelated pre-existing local edits (HTML5BrowserComponent, hellocodenameone settings).
  • Build artifacts (target/, isolated .m2-local) are gitignored and not committed.
  • Follow-up: routing the in-editor terrain brush into a region's streaming terrain (the engine already supports it).

🤖 Generated with Claude Code

Adds a visual game level/map editor for the com.codename1.gaming API and the
mode-aware authoring data model it builds on.

Core (com.codename1.gaming.level):
- GameLevel/GameElement/Layer/TileLayer/AssetCatalog/IsoProjection data model
  with JSON load/save (2D, 3D and board modes) and Sprite/Model realization.
- Streaming/region game-space engine: pluggable Material + MaterialRegistry,
  chunked StreamingTerrain (ChunkProvider, LRU paging, negative coords),
  variable-size TerrainFeature, loadable Region + RegionProvider + GameWorld
  (seamless neighbour streaming). GameLevel persists an optional GameWorld.

Editor (scripts/gamebuilder, Java-17 CN1 app, javase preview):
- Multi-panel editor (hierarchy, asset palette, inspector) with 2D/3D/board
  authoring, terrain sculpting (height/holes/walls/surface materials, smooth
  slopes), per-game-type 3D preview (open/flight/race/dungeon) with a radar,
  light/dark theming and a "Large World" region-graph mode.

Maven plugin: cn1:create-game-scene and cn1:gamebuilder goals (Java-17 gated),
gamebuilder-release reactor profile to publish codenameone-gamebuilder.

Core fixes: desktop hover tooltips (JavaSEPort), TooltipManager guard +
clear-on-press (Form).

Tests/docs: GameLevelTest + GameWorldTest; headless harnesses; three blog
tutorials with a screenshot-generating test; Game Builder + Game Assets guide
chapters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Blog prose gate

✅ No net-new prose findings introduced by this PR.

@shai-almog

shai-almog commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 11 screenshots: 11 matched.
✅ JavaSE simulator integration screenshots matched stored baselines.

@shai-almog

shai-almog commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 128 screenshots: 128 matched.

Native Android coverage

  • 📊 Line coverage: 14.23% (8667/60917 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 11.52% (42686/370530), branch 5.09% (1773/34863), complexity 6.07% (2028/33408), method 10.52% (1642/15613), class 17.11% (377/2203)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

✅ Native Android screenshot tests passed.

Native Android coverage

  • 📊 Line coverage: 14.23% (8667/60917 lines covered) [HTML preview] (artifact android-coverage-report, jacocoAndroidReport/html/index.html)
    • Other counters: instruction 11.52% (42686/370530), branch 5.09% (1773/34863), complexity 6.07% (2028/33408), method 10.52% (1642/15613), class 17.11% (377/2203)
    • Lowest covered classes
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysKt – 0.00% (0/6327 lines covered)
      • kotlin.collections.unsigned.kotlin.collections.unsigned.UArraysKt___UArraysKt – 0.00% (0/2384 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.ClassReader – 0.00% (0/1519 lines covered)
      • kotlin.collections.kotlin.collections.CollectionsKt___CollectionsKt – 0.00% (0/1148 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.MethodWriter – 0.00% (0/923 lines covered)
      • kotlin.sequences.kotlin.sequences.SequencesKt___SequencesKt – 0.00% (0/730 lines covered)
      • kotlin.text.kotlin.text.StringsKt___StringsKt – 0.00% (0/623 lines covered)
      • org.jacoco.agent.rt.internal_b6258fc.asm.org.jacoco.agent.rt.internal_b6258fc.asm.Frame – 0.00% (0/564 lines covered)
      • kotlin.collections.kotlin.collections.ArraysKt___ArraysJvmKt – 0.00% (0/495 lines covered)
      • kotlinx.coroutines.kotlinx.coroutines.JobSupport – 0.00% (0/423 lines covered)

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend scalar fallback (no native SIMD)
SIMD int-add (64K x300) java 292ms / native 140ms = 2.0x speedup
SIMD float-mul (64K x300) java 57ms / native 162ms = 0.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 306.000 ms
Base64 CN1 decode 236.000 ms
Base64 native encode 866.000 ms
Base64 encode ratio (CN1/native) 0.353x (64.7% faster)
Base64 native decode 798.000 ms
Base64 decode ratio (CN1/native) 0.296x (70.4% faster)
Image encode benchmark status skipped (SIMD unsupported)

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

@shai-almog

shai-almog commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 121 screenshots: 121 matched.
✅ JavaScript-port screenshot tests passed.

- Blog Vale (Microsoft.Hyphens): "freely placed", "aerial flyover".
- Developer-guide Vale (23): contractions, headings (punctuation/colon caps),
  adverbs, foreign abbreviations, "32×32", "autofits", de-hyphenation.
- Developer-guide LanguageTool (7): American spelling (gray/honoring/license),
  keyboard shortcuts moved to code spans, reworded subject-verb / "The This"
  false positives, and "transcoder" added to languagetool-accept.txt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Mac native screenshot updates

Compared 128 screenshots: 127 matched, 1 missing actual.

  • ListTheme_dark — missing actual screenshot. Actual screenshot missing (test did not produce output).

    No preview available for this screenshot.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 128 seconds

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 54ms / native 3ms = 18.0x speedup
SIMD float-mul (64K x300) java 55ms / native 3ms = 18.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 273.000 ms
Base64 CN1 decode 216.000 ms
Base64 native encode 616.000 ms
Base64 encode ratio (CN1/native) 0.443x (55.7% faster)
Base64 native decode 449.000 ms
Base64 decode ratio (CN1/native) 0.481x (51.9% faster)
Base64 SIMD encode 53.000 ms
Base64 encode ratio (SIMD/CN1) 0.194x (80.6% faster)
Base64 SIMD decode 46.000 ms
Base64 decode ratio (SIMD/CN1) 0.213x (78.7% faster)
Base64 encode ratio (SIMD/native) 0.086x (91.4% faster)
Base64 decode ratio (SIMD/native) 0.102x (89.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 18.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.167x (83.3% faster)
Image applyMask (SIMD off) 97.000 ms
Image applyMask (SIMD on) 63.000 ms
Image applyMask ratio (SIMD on/off) 0.649x (35.1% faster)
Image modifyAlpha (SIMD off) 72.000 ms
Image modifyAlpha (SIMD on) 54.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.750x (25.0% faster)
Image modifyAlpha removeColor (SIMD off) 76.000 ms
Image modifyAlpha removeColor (SIMD on) 49.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.645x (35.5% faster)

@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Developer Guide build artifacts are available for download from this workflow run:

Developer Guide quality checks:

  • AsciiDoc linter: No issues found (report)
  • Vale: No alerts found (report)
  • Paragraph capitalization: No paragraph capitalization issues (report)
  • LanguageTool: No grammar matches (report)
  • Image references: No unused images detected (report)

shai-almog and others added 4 commits June 17, 2026 16:04
- Replace em-dashes with ASCII '--' in StreamingTerrain/TerrainChunk doc
  comments (the Ant core build compiles with -encoding US-ASCII and rejected
  the UTF-8 0xE2 0x80 0x94 bytes; the Maven build tolerated them).
- Reword the asset-import sentence so LanguageTool's IS_AND_ARE rule no longer
  mis-parses "the project is bound ... writes" as a subject-verb disagreement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address shallow tutorials: each post now explains the why, sets up the
project and launches the editor, documents the .game file + the generated
companion class (loading, onUpdate), shows real win/lose code reading
GameElement properties, covers default behavior / physics / effects /
overriding, adds an enemy + goal to the platformer, and ends with future
directions. Adds animated gameplay GIFs (platformer, dungeon) generated by
BlogTutorialScreenshots, and drops the irrelevant "verified by a harness"
boilerplate.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LanguageTool's en-US dictionary lacks 'pushable'; 'movable' is in-dictionary
and reads the same.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The terrain walls()/materials() accessors always allocate, so the four
'!= null' guards in GameLevel.fromMap/toJson were redundant
(RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE, the build-test JDK-8 SpotBugs gate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 124 screenshots: 124 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 310 seconds

Build and Run Timing

Metric Duration
Simulator Boot 82000 ms
Simulator Boot (Run) 1000 ms
App Install 13000 ms
App Launch 6000 ms
Test Execution 321000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 128ms / native 3ms = 42.6x speedup
SIMD float-mul (64K x300) java 89ms / native 4ms = 22.2x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 660.000 ms
Base64 CN1 decode 537.000 ms
Base64 native encode 1536.000 ms
Base64 encode ratio (CN1/native) 0.430x (57.0% faster)
Base64 native decode 496.000 ms
Base64 decode ratio (CN1/native) 1.083x (8.3% slower)
Base64 SIMD encode 110.000 ms
Base64 encode ratio (SIMD/CN1) 0.167x (83.3% faster)
Base64 SIMD decode 61.000 ms
Base64 decode ratio (SIMD/CN1) 0.114x (88.6% faster)
Base64 encode ratio (SIMD/native) 0.072x (92.8% faster)
Base64 decode ratio (SIMD/native) 0.123x (87.7% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 26.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.115x (88.5% faster)
Image applyMask (SIMD off) 119.000 ms
Image applyMask (SIMD on) 106.000 ms
Image applyMask ratio (SIMD on/off) 0.891x (10.9% faster)
Image modifyAlpha (SIMD off) 556.000 ms
Image modifyAlpha (SIMD on) 412.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.741x (25.9% faster)
Image modifyAlpha removeColor (SIMD off) 578.000 ms
Image modifyAlpha removeColor (SIMD on) 222.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.384x (61.6% faster)

@shai-almog

shai-almog commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 128 screenshots: 128 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 317 seconds

Build and Run Timing

Metric Duration
Simulator Boot 84000 ms
Simulator Boot (Run) 1000 ms
App Install 17000 ms
App Launch 9000 ms
Test Execution 267000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 99ms / native 4ms = 24.7x speedup
SIMD float-mul (64K x300) java 85ms / native 3ms = 28.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 447.000 ms
Base64 CN1 decode 245.000 ms
Base64 native encode 982.000 ms
Base64 encode ratio (CN1/native) 0.455x (54.5% faster)
Base64 native decode 338.000 ms
Base64 decode ratio (CN1/native) 0.725x (27.5% faster)
Base64 SIMD encode 73.000 ms
Base64 encode ratio (SIMD/CN1) 0.163x (83.7% faster)
Base64 SIMD decode 84.000 ms
Base64 decode ratio (SIMD/CN1) 0.343x (65.7% faster)
Base64 encode ratio (SIMD/native) 0.074x (92.6% faster)
Base64 decode ratio (SIMD/native) 0.249x (75.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 22.000 ms
Image createMask (SIMD on) 4.000 ms
Image createMask ratio (SIMD on/off) 0.182x (81.8% faster)
Image applyMask (SIMD off) 87.000 ms
Image applyMask (SIMD on) 44.000 ms
Image applyMask ratio (SIMD on/off) 0.506x (49.4% faster)
Image modifyAlpha (SIMD off) 74.000 ms
Image modifyAlpha (SIMD on) 49.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.662x (33.8% faster)
Image modifyAlpha removeColor (SIMD off) 108.000 ms
Image modifyAlpha removeColor (SIMD on) 118.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 1.093x (9.3% slower)

shai-almog and others added 2 commits June 17, 2026 18:16
Satisfy the PMD gate (build-test JDK 8) for the new com.codename1.gaming.level
package + TooltipManager: @OverRide on StreamingTerrain's interface impls,
indexed for-loops -> foreach, document empty catch blocks, drop unused
imports / fully-qualified name / unused 'tile' param, preserve the exception
cause in Region.fromJson, size()>0 -> !isEmpty(), and a //NOPMD for the
intentional identity comparison in TooltipManager. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The PMD ruleset doesn't accept comment-only empty catch blocks, so suppress
the two close-the-reader catches with //NOPMD; convert the remaining
GameWorld.fromMap regions loop to foreach.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [HTML preview] [Download]
    • ByteCodeTranslator: 2 findings (Normal: 2)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

shai-almog and others added 12 commits June 17, 2026 20:47
…her tutorials

Editor / framework / codegen:
- Rename the editor's "Build" toolbar button to "Save" with a save icon
  (it saves; it doesn't build). Harness updated to btn.save.
- Fix the runtime resource path: CN1's resource namespace is flat, so the
  companion now loads "/<Name>.game", not "/games/<Name>.game"
  (CompanionCodeGen + CreateGameSceneMojo + ProjectIO + tutorials).
- GameSceneView gains protected scene-query helpers (elementOf, findByName,
  findByAsset, findAllByAsset, overlaps) and simple game state
  (score/lives: addScore/getScore/loseLife/getLives/isGameOver), so a
  generated scene and the user's onUpdate need no boilerplate.
- CompanionCodeGen now generates a `protected Sprite` field per named
  object (wired via findByName in initScene) and seeds lives from the
  player's property — variable names + initialization are generated.

Blog tutorials (2D platformer, board, 3D dungeon):
- 1024x512 JPEG hero per post; gameplay GIF for ALL three posts (added the
  board piece-slide and a real dungeon walk-and-look; the 3D capture now
  feeds device key codes so the camera actually moves).
- Editor screenshots re-rendered in a larger window then scaled down, so
  the toolbar no longer crowds/overlaps.
- onUpdate rewritten to use the generated `player` field + base helpers
  (no getUserData casts, scan loops or hand-rolled overlap math).
- "Physics, effects and overriding defaults" now has concrete drop-ins
  (Box2D PhysicsWorld + linked body, SoundPool, screen-shake, torch light).
- New section highlighting Codename One's UI power for game menus: HUD,
  pause Dialog, and a themed level-select Form/Toolbar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…et/behavior clarity

The editor's Live preview play-tested gravity/jump/patrol/pickups, but a shipped
GameSceneView ran none of it (only onUpdate) -- so the tutorial would have shipped a
stationary player. Close that gap and add a real parallax background, then rewrite the
docs to match what the code actually does.

Runtime arcade behavior (opt-in, overridable):
- GameSceneView.setArcadeBehavior(true) runs the preview's behavior at runtime: gravity
  (level "gravity" prop), run/jump (walkSpeed + player jumpHeight), tile collision,
  enemy patrol, coin/enemy pickups, and a follow camera. Every piece is a protected hook
  (updatePlayer/updateEnemies/onPickup/onPlayerHit/isCollectible/isEnemy/isSolidAt) so
  "overriding defaults" = override a hook, or setArcadeBehavior(false) to opt out.
- Codegen emits setArcadeBehavior(true) for 2D scenes, so a generated game is playable
  before any logic. Editor preview reads the same gravity/jumpHeight/speed tunables so
  preview == shipped.

Parallax backgrounds (real engine feature):
- Sprite + Layer gain a parallax factor; SpriteRenderer offsets each sprite by
  camera * parallax; realizeSprites wires layer->sprite; GameLevel JSON round-trips it.
- 2D starter ships a Background layer at parallax 0.4 with cloud/mountain/hill art;
  parallax (non-play-plane) tile layers are decoration, not collision.
- Editor 2D play preview now zooms + follows the player so the world scrolls and the
  parallax background drifts (and is visible in the tutorial GIF).

Docs/tutorial clarity (the reported confusion):
- New "Assets" section: an asset is a template; the two live art paths are procedural
  art-as-code (starter packs) and imported PNGs; packs are JSON. De-claimed the
  not-yet-wired SVG/Lottie transcode as a forward-looking `source` field (AssetCatalog
  javadoc + Game-Assets.asciidoc).
- New "Preview vs runtime" section + concrete HP/death/checkpoint/power-up examples via
  onPickup/onPlayerHit overrides; concrete opt-out. New parallax tutorial step.

Verified: core JDK8/ASCII; ArcadeBehaviorHarness (gravity/land/pickup/patrol/follow-cam +
parallax data + JSON round-trip) + Structure/Tutorial/ProjectIO green; blog + dev-guide
prose gates clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ity compare

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… freely-placed parallax

Assets were too thin -- "an asset is a PNG drawn in code." Make the asset model own the
formats Codename One already supports and ship real, editable art files.

Format-aware asset model (core):
- AssetDef gains a type (TYPE_IMAGE / TYPE_SHEET / TYPE_MESH) + sprite-sheet frame
  metadata (frameW/frameH/frames/fps); JSON parses "type"/"frameW"/... and infers the
  type from the source extension (.glb = mesh) when omitted.
- AssetCatalog resolves art by format: image(), sheet() (a SpriteSheet, with image()
  falling back to frame 0), meshData() (glTF bytes); resolveArt() loads each asset's
  source file (flat /<id>.png), best-effort.
- realizeSprites builds an AnimatedSprite for a sheet asset; GameSceneView.buildModels
  loads a real Model via GltfLoader for a mesh asset (cube fallback).

Real, editable starter art (no more faux drawing at runtime):
- AssetArt moved to test scope as a one-time generator; StarterArtGenerator bakes every
  starter asset to a committed PNG and writes the coin as a 6-frame sprite sheet (it
  spins at runtime). GameBuilder calls catalog.resolveArt() instead of AssetArt.install.
- Pack JSON carries source/type; StarterPacks defaults each source to <id>.png.

Feedback fixes:
- Mountains/clouds are now freely-placed actor assets at varied sizes on an ENTITY
  parallax Background layer (not evenly-spaced grid tiles, which clipped big art).
- Codegen generates a field per named element; the tutorial names flag/slime so onUpdate
  uses overlaps(player, flag), not findByAsset(...).
- CoinRun.game JSON in the tutorial updated to the current format (Background layer +
  parallax, named elements, scale). Removed an AI-cliche line.

Docs: blog + dev-guide assets sections rewritten around the three formats and real files;
import documented honestly (image via the button; sheet/mesh via file + pack entry today).

Verified: core JDK8/ASCII; resolveArt loads real images + the coin sheet; all 4 harnesses
green; blog + dev-guide prose gates clean; screenshots/GIFs regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Image in resolveArt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- suppress the CloseResource false-positive on the finally-closed stream (no
  try-with-resources at -source 1.5)
- openResource returns early instead of reassigning 'in' (clears the second finding)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…offee/exception theme

- Fix the editor's drawAsset: it gated drawing on hasImage() (only true for setImage'd
  statics), so sheet-backed assets + placeholders rendered as flat squares (the "green
  square coins"). Now it draws cat.image() (sheet frame 0) and animates the sheet frame by
  playClock in play mode.
- realizeSprites sets the sprite size from the AssetDef so a large sprite-sheet frame
  (Duke is 254x252) renders at the asset's intended on-screen size.
- Integrate a real sprite sheet: sliced the Gemini-generated Duke sheet (labeled
  RUNNING/IDLE sections, black bg) into a clean uniform, chroma-keyed duke_run.png (5
  frames) + duke_idle.png via the slicer; the platformer player is now the animated Duke.
- New themed art (baked PNGs via AssetArt): coffee cup (collectible) + exception monster
  (enemy). isCollectible/isEnemy recognize "coffee"/"exception" (runtime + editor preview).
- Pack JSON: player = Duke sheet, + coffee + exception assets. Harness places Duke
  collecting coffee and dodging an exception monster.

Engine + assets verified (4 harnesses green, core JDK8/ASCII). The tutorial prose rewrite
(Duke/coffee/exception narrative, honest asset-setup workflow, downloadable assets) and the
regenerated blog screenshots follow in the next commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rkflow

Rewrites the platformer tutorial around the new art and stops hiding the real work:
- Re-themed to "Duke's Coffee Run": Duke (animated sprite sheet) dashes, collects coffee
  cups, dodges exception monsters, reaches a flag. CoinRun -> CoffeeRun throughout.
- New "Step 1 — Get your art in" section does the part tutorials skip: download links for
  the assets, the RAW AI-generated Duke sheet embedded next to the SLICED result, the
  slice/chroma-key/uniform-grid explanation, and the sprite-sheet pack-JSON wiring
  (type/frameW/frameH/fps). Plain-image Import is documented honestly alongside it.
- Dropped the redundant -Dmode=2d (2D is the default) and the "magic scaffold" framing;
  project/toolchain setup links out (covered elsewhere).
- Bundled the downloadable assets under static/blog/gamebuilder/assets/ (duke_run,
  duke_idle, coffee, exception, and the raw sheet).
- Board + dungeon intros tie into Duke's story (his tabletop challenge; his crypt).
- Regenerated all screenshots + GIFs: Duke animates, coffee cups + exception monster
  render as real sprites (no more flat green squares), over the parallax background.

Blog + dev-guide prose gates pass; harness scene renamed CoinRun -> CoffeeRun.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…to Tutorial 3

- Duke's head was cropped: the background is pure black (<15) but Duke's dark head is
  ~47, so the >45 foreground threshold excluded it and the band started below the head.
  Re-sliced with a low threshold + hardcoded row bands; all 5 run frames now include the
  full head (269x312). Pack + blog frame size updated; downloadable copies refreshed.
- Mountains rendered in front of / floating above the grass. The editor drew all tiles
  then all elements, ignoring layer band; now both draw paths (edit + scrolling) iterate
  layers in band order, so a Background layer draws behind Terrain. Background is now
  horizontal-only parallax (parallaxY 1) so mountains stay anchored, and the harness sinks
  their bases so the grass overlaps them (clearly behind).
- Removed the "(the part tutorials skip)" and "where Codename One spoils you" asides;
  fixed the cross-links to the renamed menu heading.
- Tutorial 3: new "3D assets are meshes" section (the flagged omission) — TYPE_MESH ->
  glTF/.glb -> Model via GltfLoader, why the kit uses primitive meshes, how to drop in a
  real .glb. Board + dungeon intros tie into Duke's story.
- Regenerated screenshots/GIFs: Duke (full head) animates, mountains behind the grass.

Harnesses green; blog prose gate passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…or, fixed radar

The 3D dungeon rendered a gridded green floor and spaced pillar "walls" with gaps (also
visible as dots in the radar). Make it a real maze:
- Floor: stone-coloured for the dungeon, and floor faces no longer draw a per-cell edge
  outline; the explicit ground grid is skipped in dungeon — so the floor reads as
  continuous stone, not a tile grid. (Added a Face.outline flag; boxes still outline.)
- Walls: the dungeon is built from continuous TERRAIN WALLS (full-cell stone blocks that
  join seamlessly) instead of spaced pillar elements, so there are no gaps and the radar
  shows solid wall cells. The harness paints a maze (perimeter + inner walls + a corridor).
- Tutorial 3: Step 3 rewritten to build the maze with the Terrain Wall brush; the .game
  JSON shows terrain walls instead of pillar elements.

Harnesses green; dungeon screenshots regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add a visible Sun: the dungeon is now an open-top sunlit maze (blue sky + a sun disc
  drawn along the light direction) instead of a pitch-black crypt, so the light source is
  on screen.
- Directional lighting: box faces (walls, objects) are now shaded by how directly they
  face the sun (ambient + diffuse·max(0, n·sun)) instead of fixed per-side factors — so
  sun-facing faces are bright and shadowed faces dark, and the shading tracks the sun
  angle as you turn. This is what the new "Light and shade" tutorial section discusses
  (getLight().setDirection/Color/AmbientColor).
- Tutorial 3 gains the "Light and shade" section; walk prose updated for the maze walls.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two tutorial-completing gameplay features for the Game Builder series (PR #5253).

Tutorial 3 — dungeon combat (Duke vs tea cups):
- New Tea Cup enemy actor (angry porcelain cup art, 3D Kit pack, baked teacup.png).
- Duke fires coffee beans with Fire/Space: EditorCanvas grows a projectile loop
  (fire()/beanStep) — beans fly down the corridor, expire on walls/range, and pop a
  tea cup (scoring it). 3D preview now draws enemies/pickups as upright sprite
  billboards (with a line-of-sight cull so a cup behind a wall stays hidden) instead
  of flat boxes; beans render as small coffee-bean discs (size-capped, muzzle-culled).
- New "Coffee versus tea: fighting back" tutorial section + dungeon-6-combat shot;
  the gameplay GIF now shows Duke firing at the cups.

Tutorial 2 — rewritten as "Duke Jack", a playable blackjack game:
- Complete, dependency-free blackjack engine (Blackjack: 52-card shoe, Ace 11/1
  softening, hit/stand, dealer draws to 17, naturals, bust/push/win settlement),
  validated by BlackjackProbe (7191/7191 headless checks; surefire is skipped).
- EditorCanvas renders "card" elements as real playing cards (rank corners, red/black
  suit pips, a gold crown on the Duke face cards, a Duke-diamond face-down back) from
  each card's rank/suit/faceUp props.
- The board harness builds an 8x5 green-felt table and deals a real hand from the
  engine (Duke 16 -> hit -> 19, stands; dealer draws to 17; Duke wins), with a
  card-dealing GIF. Full tutorial-2 rewrite covering the felt, the deal, hit/stand,
  the engine, and wiring it into the companion. TutorialValidationHarness updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
shai-almog and others added 5 commits June 18, 2026 17:09
Review fixes across the three tutorial posts.

- Mountains and the goal flag no longer float: placement assumed each asset's raw pixel
  height, but the editor clamps an element's rendered size to <=1.8 cells, so big actors
  ended up hovering above the ground line. New onGround() helper anchors the rendered base
  to the ground using the same clamp; mountains, player, enemy and flag now sit on the grass.
- Hierarchy panel widened (42mm -> 62mm) and the layer-row controls tightened so labels read
  in full ("Actors"/"Terrain"/"Background", "Mountain 1", "Tea Cup 2") instead of truncating
  to "Act"/"Te"/"Spa" in the screenshots.
- Card art is now concrete and editable: a face-down card draws the actual "card" asset image
  (the bundled /card.png cover, redrawn as a proper Duke-blue card back), while faces stay
  composed from each card's rank/suit data. New board-post section "Where the card art comes
  from" explains the image-back / data-face split and how to swap in per-card images.
- Regenerated all tutorial screenshots and GIFs to reflect the above.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Duke Jack now actually features Duke, and the tutorial stops hand-waving about "generated"
faces.

- The card renderer blits the real Duke mascot (the same duke_run sprite from Tutorial 1)
  onto the card BACK and onto every picture card (Jack/Queen/King, on a suit-tinted panel);
  number cards still show a drawn suit pip. The baked card.png thumbnail composites Duke too.
- Rewrote the board post's "Where the card art comes from" to be concrete instead of vague:
  there is no card generator — your companion class draws each card with the Graphics API.
  It shows the actual drawCard() method, explains the Duke image rides the back and the face
  cards while suit shapes + rank text cover the rest (two art pieces for all 52 cards), and
  notes you can instead import 52 painted card images. No more "the faces are generated".
- Demo deal reseeded so a Duke face card is on the table: Duke holds 5H + JC (a Duke Jack),
  hits to 19, and beats the dealer's QH-led 18. Regenerated the board screenshots and GIF.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ling

Add a paragraph to the board post making the larger point explicit: drawCard uses the same
Graphics API Codename One has drawn UI with for fourteen years, and because GameView is an
ordinary Codename One Component, the whole mature stack — mutable-image buffers, anti-aliased
shapes, fonts, transforms, the chart/SVG libraries — works inside a game. The new gaming API
plugged into that tooling rather than replacing it; the card faces are the smallest example.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… square)

- The face cards drew an opaque tint rectangle behind a transparent sprite, leaving an ugly
  square around the figure. Removed the panel: Duke is drawn straight on the white card, with
  a small suit pip beside each corner rank so a face card still shows its suit.
- Swapped the running-Duke sprite frame for the iconic *waving* Duke — the BSD-licensed
  OpenJDK Java mascot (Wikimedia Commons), bundled as duke_wave.png (233x420 RGBA). It now
  rides the card back and the J/Q/K, with its real transparency respected. AssetArt bakes it
  onto the card.png thumbnail too.
- Board post updated to credit the classic waving Duke (BSD Java mascot) instead of the
  Tutorial 1 sprite. Regenerated the board screenshots and GIF.

NOTE: duke_wave.png is the New BSD-licensed Duke mascot; the project may want a formal
attribution/NOTICE entry for it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
duke_wave.png (the waving Duke used as Duke Jack card art) is the BSD-licensed Java mascot.
Add a "Duke (the Java mascot)" section to the top-level NOTICE with the source, provenance
and full BSD license text, plus a per-asset duke_wave.png.CREDITS.txt alongside the image
(matching the existing boombox.glb.CREDITS.txt convention).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shai-almog

shai-almog commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 209 screenshots: 209 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

shai-almog and others added 3 commits June 19, 2026 05:50
API review follow-ups for the com.codename1.gaming.level package.

- Replace the magic-int constants with type-safe enums: GameLevel.Mode (TWO_D/THREE_D/
  BOARD), AssetDef.Kind (TILE/ACTOR), AssetDef.Type (IMAGE/SHEET/MESH) and Layer.Kind
  (TILE/ENTITY/MODEL). Each enum carries its lowercase JSON wire token (wireName()) and,
  for Mode, a display label() — which removes the redundant GameLevel.modeName/modeLabel
  pair (one enum constant now owns both forms). getMode()/getKind()/getType(), the
  constructors and setters, and the JSON read/write all speak the enums; fromWire() keeps
  loading older/hand-edited files. All consumers (the editor, its harnesses, the unit
  tests) updated.
- GameElement now caches its resolved AssetDef: resolveDef(catalog) memoizes the catalog
  lookup (cleared by setAssetId), so the per-frame GameSceneView update/pickup paths stop
  re-hitting the catalog's HashMap every frame. The stored data stays the catalog-
  independent assetId (still the only thing serialized) — this is purely a transient cache.

Verified: core compiles; GameLevelTest (7) + GameWorldTest (6) pass (JSON round-trips across
all modes); the gamebuilder compiles and all six editor harnesses pass.

Note: the terrain MAT_* ints are intentionally left for a separate change — that one is a
registry, not a closed set, so it warrants unifying onto MaterialRegistry rather than an enum.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The fifth "magic int" set from the API review was the TerrainGrid MAT_* surface materials.
Unlike the others it is not a closed set: the package already ships an extensible, String-keyed
MaterialRegistry (used by the streaming terrain), so an enum would have been a third parallel
representation of the same concept. Instead, retire TerrainGrid's int MAT_* and have it speak the
same MaterialRegistry ids as the streaming terrain:

- TerrainGrid.getMaterial/setMaterial and the materials() array are now String ids (default
  MaterialRegistry.GRASS); the MAT_* constants are gone. Both terrain systems now share one
  material model.
- The .game JSON writes material names; the reader accepts a name or a legacy numeric ordinal
  (0=grass..5=dirt), so older files still load.
- Editor consumers updated: the terrain-surface picker maps its labels to MaterialRegistry ids,
  EditorModel.terrainMaterial is a String, and EditorCanvas colours a cell via
  MaterialRegistry.get(id).getColor() instead of a switch on the old ints.

Verified: core compiles; GameLevelTest (7) + GameWorldTest (6) pass; the gamebuilder compiles and
the terrain-painting / round-trip harnesses (Workflow, GameWorld, Tutorials) pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Document the public methods that were missing /// markdown doc comments on the two central,
most-read data types — GameElement (id/name/asset/layer, the transform setters, the typed
property accessors) and GameLevel's core accessors (grid, props, layers, elements). The
refactor commits already documented every member they touched (the new enums, resolveDef,
the material accessors); this closes the remaining gaps on the classes a user reads first.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant