bug-catcher-game: Bug Catcher Gnome: The Tiny Godot Game That Cheats Depth and Multiplies Players
A compact arcade project that uses authoritative multiplayer, browser-aware RPC paths, and a convincing 2.5D illusion to make a small world feel much bigger.
- The game’s biggest surprise is visual, because it turns flat sprites into layered space without leaving Godot 2D.
- Multiplayer is treated as a transport problem with different rules on desktop and web, not as a one-size-fits-all API choice.
- The bee-to-spear reward loop changes the player’s verb from collecting to attacking, which sharpens the pace of each round.
- The repo reads like a reusable scaffold because managers, scenes, and procedural setup stay separated instead of collapsing into one script.
A 2D World That Pretends to Be Deep
The first trick in Bug Catcher Gnome is visual, not networked. The meadow is built from flat assets, but DepthManager, Y-position scaling, and pinned shadows make the arena feel stacked in space.
Flying bugs get a small altitude illusion on top of their 2D coordinates, so the eye reads them as hovering instead of sliding on a plane. That buys the game a lot of staging depth for very little engine cost.
The Multiplayer Trick Behind the Curtain
The networking code makes the same refusal: do not pretend one transport fits every target. The host creates the ENet server, clients join through the same manager, and spawned players are assigned authority so input stays with the right peer.
The browser path is the subtle part. if OS.has_feature("web"): tells the code to branch before desktop assumptions leak into the export, which is why the project reads like a small multiplayer lesson instead of a fragile demo.
var peer := ENetMultiplayerPeer.new()
func host_game() -> void:
peer.create_server(PORT)
multiplayer.multiplayer_peer = peer
add_player(multiplayer.get_unique_id())
func join_game(address: String) -> void:
peer.create_client(address, PORT)
multiplayer.multiplayer_peer = peer
@rpc("any_peer", "call_local")
func add_player(id: int) -> void:
var player := PLAYER_SCENE.instantiate()
player.name = str(id)
player.set_multiplayer_authority(id)
add_child(player)
func _ready() -> void:
if OS.has_feature("web"):
# Browser-safe path. Avoid desktop-only assumptions.
pass
Bugs, Behaviors, and the Spear Economy
Once the netting loop starts, the bug AI stops being background decoration. BugBehavior switches between wandering, fleeing, and chase-like responses, and the flee logic uses interpolation so movement looks organic instead of robotic.
The smartest design move is the bee-to-spear reward loop. Catch the right bug, and the game changes your verb from collecting to attacking, which lets one pickup reshape the entire tempo of a round.
enum BugBehavior { WANDER, FLEE, CHASE }
func _behavior_flee(delta: float) -> void:
var away := (global_position - nearest_player.global_position).normalized()
direction = direction.lerp(away, 6.0 * delta)
var bugs_hit: Array = []
func _on_spear_body_entered(body: Node) -> void:
if body in bugs_hit:
return
bugs_hit.append(body)
if bugs_hit.size() > 1:
_trigger_multikill_effect()
Why the Code Reads Like a Template
This is the part that makes the repository useful even if you never ship the game. Main.gd stays out of the way because managers own music, sound, and multiplayer, while scene scripts handle local behavior and procedural spawning fills the arena at runtime.
That separation matters in Godot. The project keeps the art crisp with SVGs and a compatibility-minded render path, so the same structure can serve browser targets and low-end desktop hardware without forcing the whole codebase through one monolithic script.
What It Sits Beside
| Axis | Bug Catcher Gnome | Typical Godot arcade jam | Typical Godot multiplayer starter |
|---|---|---|---|
| Multiplayer model | Host-as-server ENet with authority assignment. | Often local only or loosely synced. | Usually network-first, with little game feel. |
| Web export | Branches around browser constraints instead of ignoring them. | Frequently left untested until it breaks. | Often missing or brittle. |
| Depth illusion | 2D Y-scaling, pinned shadows, and sprite discipline. | Flat top-down staging or a jump to real 3D. | Usually unrelated to the core loop. |
| Code organization | Manager pattern plus scene-script separation. | One oversized main scene script. | Engine demo code without much cohesion. |
| Reuse value | Playable template and architecture reference. | A one-off slice of gameplay. | Transport example without a game loop. |
That middle ground is the point. It is charming enough to read as a game, but disciplined enough to reuse as a pattern for Godot multiplayer and 2.5D staging.