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
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
# shape verify_buffer consumes for the native arm.
func state_buffer() -> PackedFloat32Array:
return state.duplicate()
# 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