I just finished implementing a confetti animation. It renders a thousand little images on the screen that are moving and spinning around in random looking directions (see attached image/video).
The challenge is, what’s the minimal amount of state you need to keep from the previous frame(s) to render the next frame?
Note: there is no acceleration, each confetti’s movement direction spin rate, though different from each other, are constant.
const std = @import("std");
fn renderConfettiPiece(
x: u32,
y: u32,
angle_degrees: u9,
) void {
// assume this function is implemented for you
std.log.info("confetti {},{} {}", .{ x, y, angle_degrees });
}
fn renderConfettiFrame(frame: usize) void {
// CHALLENGE: insert code to render 1000 confettis
// for the given frame
_ = frame;
}
pub fn main() void {
// animate 10 frames
for (0..10) |frame| {
renderConfettiFrame(frame);
}
}
5 Likes
How about none at all? Pseudo random number generators are deterministic. My strategy: Pick a seed, generate from there.
fn renderConfettiFrame(frame: usize) void {
const width = 933;
const height = 644;
const maxTranslationSpeed = 100;
const maxRotationSpeed = 10;
const seed: u64 = 0;
var randomEngine = std.Random.DefaultPrng.init(seed);
const random = randomEngine.random();
const iframe: i32 = @truncate(@as(isize, @bitCast(frame)));
for (0..1000) |_| {
const x = random.intRangeAtMostBiased(i32, 0, width);
const y = random.intRangeAtMostBiased(i32, 0, height);
const angle = random.intRangeAtMostBiased(i32, 0, 359);
const dx = random.intRangeAtMostBiased(i32, -maxTranslationSpeed, maxTranslationSpeed);
const dy = random.intRangeAtMostBiased(i32, -maxTranslationSpeed, maxTranslationSpeed);
const dAngle = random.intRangeAtMostBiased(i32, -maxRotationSpeed, maxRotationSpeed);
renderConfettiPiece(
@as(u32, @bitCast(x +% (dx *% iframe))),
@as(u32, @bitCast(y +% (dy *% iframe))),
@intCast(@mod(angle +% (dAngle *% iframe), 360)),
);
}
}
4 Likes