Gododin Gododin Documentation

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)