Gododin Gododin Documentation

Linked repository source

examples/bunny_mark/project/gd_bunnies_bulk.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

class_name GdBunniesBulk
extends Node2D

## The maintained optimized GDScript bulk baseline (DX-7): the same fixed-step
## rules, identity stream, and 5-float state layout as the per-sprite arm, but
## all state lives in one PackedFloat32Array and the frame submits through one
## MultiMesh buffer assignment — the closest GDScript shape to the native
## staged arm, counting the engine's own buffer copy rather than hiding it.
## What differs from the native arm is exactly the language and the submission
## call; population, seed, step, precision contract, texture, scale, and
## drawable are shared.

const PerSprite := preload("res://gd_bunnies.gd")

const FIXED_DT := 0.01666666753590107 # exact f32(1/60), shared with Odin
const POPULATION_SEED := 20260911
const WALL_W := 1280.0
const WALL_H := 720.0

@onready var _mmi: MultiMeshInstance2D = $MMI

var speed_scale := 1.0
var gravity := 900.0
var count := 0
var draw_scale := 14.0 / 64.0
var state := PackedFloat32Array()
var frames_submitted := 0


func _ready() -> void:
	set_process(false)


func set_count(new_count: int, seed: int = POPULATION_SEED, texture: Texture2D = null, scale_factor: float = 14.0 / 64.0) -> void:
	count = new_count
	draw_scale = scale_factor
	state.resize(count * 5)
	if texture != null:
		_mmi.multimesh.mesh = _quad_mesh()
	_mmi.multimesh.instance_count = count
	for index in count:
		var p := PerSprite.parameter(index, seed)
		state[index*5] = p[0]
		state[index*5+1] = p[1]
		state[index*5+2] = p[2]
		state[index*5+3] = p[3]
		state[index*5+4] = p[4]
	frames_submitted = 0
	_submit_buffer()


# Identical arithmetic order to the per-sprite arm: velocity gains gravity
# first, then position integrates with the scaled step, then the walls reflect.
func step_fixed() -> void:
	var gravity_step := gravity * FIXED_DT
	var travel_dt := speed_scale * FIXED_DT
	for index in count:
		var base := index * 5
		var vx := state[base+2]
		var vy := state[base+3] + gravity_step
		var x := state[base] + vx * travel_dt
		var y := state[base+1] + vy * travel_dt
		if x > WALL_W:
			x = WALL_W
			vx = -vx
		elif x < 0.0:
			x = 0.0
			vx = -vx
		if y > WALL_H:
			y = WALL_H
			vy = -vy
		elif y < 0.0:
			y = 0.0
			vy = -vy
		state[base] = x
		state[base+1] = y
		state[base+2] = vx
		state[base+3] = vy
	frames_submitted += 1
	_submit_buffer()


# One MultiMesh buffer assignment per frame: the GDScript bulk submission.
# The 2D transform buffer carries 6 floats per instance (basis columns and
# origin); the scale rides the basis exactly like the per-sprite arm's
# sprite.scale.
func _submit_buffer() -> void:
	var buffer := PackedFloat32Array()
	buffer.resize(count * 6)
	for index in count:
		var base := index * 5
		var s := state[base+4] * draw_scale
		var offset := index * 6
		buffer[offset] = s
		buffer[offset+1] = 0.0
		buffer[offset+2] = 0.0
		buffer[offset+3] = s
		buffer[offset+4] = state[base]
		buffer[offset+5] = state[base+1]
	_mmi.multimesh.buffer = buffer


# A readback of the live packed state in the shared 5-float layout: the same
# shape verify_buffer consumes for the native arm.
func state_buffer() -> PackedFloat32Array:
	return state.duplicate()


# A scene-owned ArrayMesh with the shared 14px quad and top-left UV origin,
# matching the staged arm's canvas quad.
func _quad_mesh() -> Mesh:
	var arrays := []
	arrays.resize(Mesh.ARRAY_MAX)
	arrays[Mesh.ARRAY_VERTEX] = PackedVector2Array([Vector2(-7,-7),Vector2(7,-7),Vector2(7,7),Vector2(-7,7)])
	arrays[Mesh.ARRAY_TEX_UV] = PackedVector2Array([Vector2(0,0),Vector2(1,0),Vector2(1,1),Vector2(0,1)])
	arrays[Mesh.ARRAY_INDEX] = PackedInt32Array([0,1,2,0,2,3])
	var quad := ArrayMesh.new()
	quad.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
	return quad