Linked repository source
examples/ripple_pond/main.gd
This read-only view comes from the file in the Gododin repository. It retains its design, example or evidence context.
Download the original file · Return to the guide
extends Node2D
## Ripple Pond — the adopted game (the "after" state of the adoption tutorial).
##
## Click the water to splash it; press 1 or 2 to switch the pond's step
## between the preserved optimized GDScript arm and the native Odin arm.
## Both arms run the identical f64 arithmetic, so the water looks and behaves
## the same — only where the batch operation executes changes.
##
## Relative to game/main.gd (the "before" game) the adoption seam is exactly:
## - the MODE switch and the native arm in _process / _splash / _set_mode;
## - the splash-signal hook the native node emits on every impulse.
## Everything else — scene, rendering path, baseline algorithm — is unchanged.
const GRID := 96
const TEXTURE_SCALE := 5
const POND_POSITION := Vector2(120, 60)
const MODE_GDSCRIPT := 0
const MODE_NATIVE := 1
const MODE_NAMES := ["GDScript", "Odin (native)"]
var mode := MODE_GDSCRIPT
var gd_sim: GdRipples
var image: Image
var texture: ImageTexture
var step_ms := 0.0
var splash_count := 0
var last_signal_text := ""
@onready var pond_sprite: Sprite2D = $Pond
@onready var hud: Label = $Hud
@onready var ripple_pond: Node2D = $RipplePond
func _ready() -> void:
image = Image.create(GRID, GRID, false, Image.FORMAT_RGBA8)
texture = ImageTexture.create_from_image(image)
pond_sprite.texture = texture
pond_sprite.scale = Vector2(TEXTURE_SCALE, TEXTURE_SCALE)
pond_sprite.position = POND_POSITION
# The preserved GDScript baseline arm.
gd_sim = GdRipples.new()
gd_sim.configure(GRID, GRID)
gd_sim.damping = ripple_pond.damping
# The native arm: the scene's own RipplePond instance. Its damping was
# loaded from the scene's saved settings.
ripple_pond.call("configure", GRID, GRID)
# The typed native signal, decoded like any Godot signal.
var _connected: bool = ripple_pond.splashed.connect(_on_native_splash)
# Two fixed welcome splashes so the pond is alive before the first click.
splash(GRID / 3, GRID / 3, 3.0)
splash((GRID * 2) / 3, GRID / 2, -2.5)
func _process(_delta: float) -> void:
var start := Time.get_ticks_usec()
if mode == MODE_GDSCRIPT:
gd_sim.step()
else:
ripple_pond.call("step")
var elapsed := Time.get_ticks_usec() - start
step_ms = elapsed / 1000.0 if step_ms == 0.0 else step_ms * 0.9 + (elapsed / 1000.0) * 0.1
var bytes: PackedByteArray
if mode == MODE_GDSCRIPT:
bytes = gd_sim.frame_bytes()
else:
bytes = ripple_pond.call("frame_bytes")
image.set_data(GRID, GRID, false, Image.FORMAT_RGBA8, bytes)
texture.update(image)
hud.text = "%s grid=%dx%d step=%.2f ms splashes=%d damping=%.3f %s [1/2: arm, click: splash]" % [
MODE_NAMES[mode], GRID, GRID, step_ms, splash_count,
ripple_pond.damping, last_signal_text,
]
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.pressed \
and event.button_index == MOUSE_BUTTON_LEFT:
var cell := _mouse_cell()
if cell.x >= 0:
splash(cell.x, cell.y, 3.0)
elif event is InputEventKey and event.pressed and not event.echo:
if event.keycode == KEY_1:
_set_mode(MODE_GDSCRIPT)
elif event.keycode == KEY_2:
_set_mode(MODE_NATIVE)
func _set_mode(new_mode: int) -> void:
if new_mode == mode:
return
mode = new_mode
step_ms = 0.0
# Restart from the same flat water and welcome splashes so the two arms
# are visually comparable from the same state.
gd_sim.configure(GRID, GRID)
gd_sim.damping = ripple_pond.damping
ripple_pond.call("configure", GRID, GRID)
splash(GRID / 3, GRID / 3, 3.0)
splash((GRID * 2) / 3, GRID / 2, -2.5)
func splash(x: int, y: int, strength: float) -> void:
splash_count += 1
if mode == MODE_GDSCRIPT:
gd_sim.splash(x, y, strength)
else:
ripple_pond.call("splash", x, y, strength)
func _on_native_splash(at: Vector2, strength: float) -> void:
last_signal_text = "signal(%d,%d %.1f)" % [int(at.x), int(at.y), strength]
func _mouse_cell() -> Vector2i:
var local := (get_global_mouse_position() - pond_sprite.position) / TEXTURE_SCALE
var x := int(floor(local.x))
var y := int(floor(local.y))
if x < 1 or y < 1 or x > GRID - 2 or y > GRID - 2:
return Vector2i(-1, -1)
return Vector2i(x, y)