Crash-tan Crash-tan

Sprites too slow? 1.61.2 helps with that.

Crash BASIC 1.61.2 Release Notes

written by Crash-tan

This one is about crowds.

Put a few thousand sprites on screen, give each one a behavior, and the
behavior engine used to fall over. Not crash: fall behind. A field of about
6,000 sprites, every one of them running a BOSCILLATE, got its behaviors
advanced roughly three times a second instead of sixty. Everything still moved,
just in slow, lurching steps, like the whole scene was wading through syrup.

That same scene now keeps up. On our test Mac the behavior engine holds a full
60 updates a second with at least 7,500 behavior-driven sprites. Nothing in
your program has to change to get it.

And two things about time, further down: behaviors now tick at exactly the rate
you asked for, and the web player no longer hitches while things move.

THE BEHAVIOR ENGINE STOPPED DOING HOMEWORK NOBODY ASSIGNED

Every tick, the engine walks your sprites and moves each one along. On the way
it was doing a remarkable amount of busywork, and almost all of it scaled with
how many sprites you had:

  • It kept asking every sprite who its children were, by checking every other
    sprite.
    That is how SET SPRITE n PARENT(p) rigid bodies learn which pieces
    move together, and it was done the slow way: for 6,000 sprites, 36 million
    checks, sixty times a second, even in a game with no parents at all. The
    engine now keeps track of parents and children as you set them, so the answer
    is one lookup.
  • It photocopied each behavior's working state before running it, then
    copied it back afterwards. For every sprite, every behavior, every tick.
    Behaviors now run on their own state directly.
  • It prepared for collisions nobody was listening for. Before checking for
    ONCOLLISION and AFTERCOLLISION handlers, it gathered up every sprite's
    shape, tag and room, and only then noticed there were no handlers to fire. It
    checks first now.
  • It kept re-reading sprites a handler might have changed, on ticks where no
    handler ran. It still does that on every tick where one does run, so a
    HIDE SPRITE or DISABLE SPRITE inside ONCOLLISION lands exactly as it
    did before.
  • It measured positions for BFOLLOW and BORBIT in games that use neither.

None of this changes what your game does. Sprites land in the same places,
collisions fire on the same ticks, parents drag their children the same way,
and a child you make SOLID from inside a collision handler still joins its
parent's rigid body on the very next tick. It just gets there without the
detours.

SIXTY MEANS SIXTY

SET BEHAVIOR RATE 60 is the default, and it was quietly coming up short.

The behavior engine waits between ticks by asking the operating system to wake
it up in time for the next one. Operating systems are not punctual about that.
They wake you a little late, and the engine was measuring each wait from the
moment it actually woke up, so every late wake-up was lost for good. On our test
Mac a quiet scene ticked about 52 times a second instead of 60, and a busy one
about 57, and it changed with the load.

Now the engine keeps a fixed schedule. If one tick starts late, the next wait is
shorter, and the count comes out right: 60 means 60 real ticks every second,
whether the scene has ten sprites or thousands. If the engine falls briefly
behind, say for a slow handler, it catches up on a few ticks. If it falls far
behind, it picks up from where it is instead of racing through a backlog.

What you might notice: anything counted in ticks, like BMOVE velocity,
BOSCILLATE swings, DURATION steps and particle lifetimes, now runs at the
speed you wrote it for. If you tuned a game's speeds while it was running
slow, it may feel a touch quicker. That is the rate it was always meant to have.
CALL TIMER, DELAY, YIELD, COOLDOWN and animation frame rates keep
their own clocks and are unchanged.

A SMOOTHER WEB PLAYER

In the browser, a moving game could hitch: a frame lingering on screen a moment
too long, then the next one hurrying to catch up. Scrolling starfields showed
it every few seconds; a busy game full of moving sprites showed it more often.
Brief, but in a fast game you feel it.

There were two causes, and both are gone.

Pictures that were half-sent. If the browser went to put a new picture on
screen while your game was still sending one, the web player showed nothing new
at all, and the previous picture stayed up for an extra refresh. It now shows
the last finished frame, the same way the desktop and mobile players always
have. You never see a half-drawn frame either way.

Frames stuck in a queue. Your game runs in its own background thread, and
used to hand every drawing command to the page as a separate message. When the
page was busy with anything else, those messages waited in line, so a frame
your game finished right on time could reach the screen too late for its
refresh and land on top of the next one. Frames now travel through memory the
two sides share, and the web player picks up everything that is ready at the
start of each refresh. Nothing waits in line, so nothing arrives late.

The web player also does less work per refresh. It used to draw the whole
screen at least twice before showing it, and once more for every frame your
game had finished since the last refresh. Now it draws once.

WHO NOTICES

If your game has a handful of sprites, the crowd work changes nothing you can
see. The one thing you might notice is the rate fix above: behaviors moving at
their full, intended speed.

If you have been keeping sprite counts down because big scenes turned sluggish,
especially bullet-hell patterns, particle-heavy effects, flocks, crowds, or
large animated tile fields built from sprites, try the bigger version again.

Want to see how much headroom you have? Behaviors normally tick 60 times a
second, so that is the fastest they can look. Raise the rate while you test,
and anything below your target tells you where the limit is:

SET BEHAVIOR RATE 240

240 is the highest rate it accepts. Put it back to 60 before you ship: anything
counted in ticks, like BMOVE velocity, BOSCILLATE speed and particle
lifetimes, runs four times as fast at 240. Animations keep their authored frame
rate.