2026-8-26

It totally hasn’t been 19 days since the last devlog…

I’m planning to add more powerups. One of them includes a roller powerup, which erases all tiles of the same color touching the powerup’s collision point.

I’ve already added a paintball powerup that turns nearby tiles into the same color as the one the powerup is touching:

output 1

And also, powerups are now stored globally; you get exactly 5 powerups per game. The code was already designed like this, I just made the UI reflect that too.

But for the roller, I must create a smooth animation of the tiles slowly being deleted. This involves recursively calling a neighbor search with a certain delay between each call, essentially doing BFS with a task scheduler.

Only thing is… I never wrote a task scheduler, and was trying to avoid it as it would add complexity to the project. And it makes sense, as I usually use the hardcoded 24fps frame rate as a scheduler by waiting for the next frame, and have my own basic delay checker for gravity. But for any complicated animations, a scheduler is very necessary and will make my life much easier in the future.

pub struct Task {
    time: Instant,
    run: Box<dyn FnOnce(&mut State)>,
}

impl PartialEq for Task {
    fn eq(&self, other: &Self) -> bool {
        self.time == other.time
    }
}

// more comparison trait implementations

pub fn update_tasks(state: &mut State) {
    let task_is_due = state
        .task_queue
        .peek()
        .is_some_and(|Reverse(task)| task.time <= Instant::now());

    if task_is_due {
        let Reverse(task) = state.task_queue.pop().unwrap();
        (task.run)(state);
		update_tasks(state);   // probably pretty easy to use a while loop, but recursion is even easier
    }
}

pub fn add_task(time: Duration, callback: impl FnOnce(&mut State) + 'static, state: &mut State) {
    state.task_queue.push(Reverse(Task {
        time: Instant::now() + time,
        run: Box::new(callback),
    }));
}

Nice, that wasn’t too bad. I finally had an opportunity to use Rust’s closure types, and learned a bit about FnOnce, FnMut, and some borrowing rules.

And it works great! I moved gravity over to the task scheduler (in the form of a recursive task that spawns another task in its callback), and everything is working normally.

Total time: 1 hr

turn your phone horizontally pls! this site is not very mobile friendly