Optimizations - earok/scorpion-editor-demos GitHub Wiki

In order to keep your Scorpion Engine game running smooth, consider the optimizations below.

All platforms

For Actors:

  • Turn on "Ghost Thru Walls (Fast)" for all objects that offer the setting that don't need to collide with walls, such as most bullets on shooting games that don't have walls.
  • For bullets that do need to collide with walls, consider setting the collision box as small as possible (1x1) as this reduces the number of possible tile overlap checks it needs to do when moving.
  • Turn off Projectile unless your object needs to damage an enemy. Bullets that hurt only the player don't need that tickbox since they can be handled with normal actor vs player collisions.
  • Turn off Block Collider unless you need to have special effects against Blocks (such as a bullet that can destroy a Block).
  • For objects that are destroyed as soon as they leave the screen, set the Recycle mode to Trash and remove all four of the now redundant "On Leave Screen" events to reduce the overhead of the actor.
  • Only set On Leave Screen to something other than none for the directions that the actor can actually leave. For example, a bullet that only travels right should only have On Leave Screen set for right (though as per the previous tip, you may not need On Leave Screen set even for directions that the actor can leave).
  • Only set the recycled mode to "Never" if you absolutely need an object to be active even when the camera is on the opposite side of the level. If you have it set on patrolling enemies for example, the patrolling enemy that was ignored at the start of the level will continue burning CPU cycles even when you're many screens away.
  • If an enemy actor type is never seen again when the player leaves the screen, such as a game that only scrolls in one direction, consider setting the recycle type to "Trash" in order to delete the actor from memory rather than "Always" to keep the actor in a dormant state.
  • Don't use melee attacks if it can be done with a normal collision event. Melee attacks are only really designed for fighting games where the position of a collision changes betwene frames, regular collision events are preferred for objects that don't change in size or shape.
  • If an actor is only 16x16 and doesn't move, consider using a Block instead. Blocks have a far smaller overhead than actors, and have limited capabilities for animation.
  • Set "Is Attacker" unticked if your actor has no attacks, or the attacks are handled with custom codeblocks logic.
  • Turn off most tick boxes, such as Moving Platform, Autoscroll and Spawn at Start, unless you absolutely need them. Most tick boxes in the actor tab add extra functionality at the cost of performance.
  • Rather than using a custom collision event for down for a platformer actor, it may be better to use a custom On Platform Landed event.

For Animations

  • For single frame animations that never automatically go to another frame, such as bullets, set the delay as "0" (infinite). This essentially removes the overhead of the animation engine on that actor.

For Code

  • Set FastSloppyCollisions to a higher mode if absolute pixel perfect accuracy isn't needed. Note that this does not affect melee attacks - these are always pixel perfect.
  • For code that is run frequently (such as in OnTimer events), try to reduce the logic that is executed as much as possible, especially remove commands that could run just once in the actor's OnSpawn or in the level load.

Inline instead of gosub

Instead of

Gosub to label

Use

Inline code label

For most cases where performance is the priority. If having code compiled to being as small as possible is the priority, stick to gosub.

Inline instead of goto

Instead of

Goto to label

Use

Inline code label
Return

For most cases where performance is the priority. If having code compiled to being as small as possible is the priority, stick to goto.

Goto at the end of an ontimer codeblock

Instead of

gosub myotherlabel
(end of ontimer codeblock)

Use

goto myotherlabel
(end of ontimer codeblock)

Or preferrably

inline myotherlabel
(end of ontimer codeblock)

Because gosub is now redundant, there's no reason to return to here if we're already the end of the codeblock where control returns to the engine.

Using animation tags in conditions rather than the animation name

Instead of

if player_animation == myanim_l or player_animation == myanim_r

Use

if player_animationtag == myanim

Because groups of animations can be tested with a single tag, which defaults to the name of the animation without _l or _r for autoflip animations.

This can likewise be used for actortags, blocktags etc.

Testing if the player is on a platform

Instead of

if player_isonplatform == true
end condition

Use

if player_isonplatform not zero
end condition

Because there are many different kinds of platforms, such as moving platforms and slopes, and this will guarantee detection of all of them.

In general, testing for not zero is faster than testing that the value exactly equals true (-1).

Using Else If instead of a series of If conditions

Instead of

if myvar == 1
    \\ commands
end condition
if myvar == 2
    \\ commands
end condition
if myvar == 3
    \\ comands
end condition

Use

if myvar == 1
    \\ commands
else if myvar == 2
    \\ commands
else if myvar == 3
    \\ commands
end condition

Because the redundant checks will immediately be skipped - it won't bother to test test for myvar == 2 if myvar == 1 is true. It also avoids a potential issue where multiple of those if statements could run if myvar is changed within them.

Bitwise AND & in conditions

Instead of

if myvar1 == true and myvar2 == true
    \\commands
endif

Use

if myvar1 & myvar2 == true
endif

Because the bitwise AND & means that the value will be true (-1) if every bit is set in both variables.

Bitwise OR | in conditions

Instead of

if myvar1 == false and myvar2 == false
    \\commands
endif

Use

if myvar1 | myvar2 == false
    \\commands
endif

Because the bitwise OR | means that the value will be false (0) if there are no bits set in either variable.

Bitwise left (multiply) or right (divide) shifts

Instead of

myvar = myvar / 2
myvar = myvar / 4
myvar = myvar / 8
myvar = myvar / 16
myvar = myvar * 2
myvar = myvar * 4
myvar = myvar * 8
myvar = myvar * 16

use

myvar = myvar << 1
myvar = myvar << 2
myvar = myvar << 3
myvar = myvar << 4
myvar = myvar >> 1
myvar = myvar >> 2
myvar = myvar >> 3
myvar = myvar >> 4

Because bitwise shifts are faster than divides or multiplies (but you still may want to use divide or multiply if it isn't a power of 2 value).

For Maps

  • Use Foregrounds tiles, Animated tiles and Animated Blocks as little as you can get away with.
  • In general, you want to have your maps as small as you can get away with. If you need to move outside of the level a little bit, set Empty Space Side Padding to as small as possible - a setting of 1 will add a whole 16 pixels around your map.

For Panels

  • If you have a variable connected to an element on a panel, update that variable as infrequently as possible.

For Project Settings

  • Set the buffer size as small as possible. Experiment - once you start noticing glitches at the edges, you've gone too far and so you need to make it a little bigger.

Amiga

For Animations:

  • If the colors of your animation can go into the sprite range, use auto sprite or only sprite. Only sprite forces sprite usage and won't display if no sprites left, auto sprite will degrade to normal blitter objects if no sprites left.
  • Consider turning off render clearing (in code) for objects that do not move.
  • Consider using maskless rendering for background objects that only overlap a sprite or simple background.
  • If you have a maskless rendered object, you can turn off render clearing on a moving object if you insert transparent pixels around the frame in order to "mop up" the trail of pixels.
  • A 17 pixel wide image is twice as slow as a 16 pixel wide image. More broadly speaking, any pixel width that's not a multiple of 16 is the same performance as a frame that's the size of the next multiple of 16.

For Project Settings

Any Amiga

  • Reduce color depth to 16 colors/4bp, this gives the best bang-for-buck for performance and visual fidelity. You can still use a 32 color palette with the l6-31 color range using sprites.
  • Index 0 should always be black (unless absolutely necessary to have it a different color) to reduce the complexity of the copper list.
  • Keep the width and height of the screen as small as you can get away with to save on DMA.
  • Turn off Smooth Scrolling on games that only scroll vertically, or don't scroll at all.
  • Leave Read CD32 buttons unticked unless your game actually uses CD32 buttons.

AGA Amiga

  • Take advantage of the wider sprite settings in order to have more and larger sprites.
  • Set the Fetch mode as high as possible. Higher fetch modes are much faster, though they come with severe limitations on screen size and position.

For Panels

  • Use the smallest possible color count. If your main game is a 32 color game, but there's only 4 colors on your panel, set it to 4 color mode with Override Game Palette turned off.

Mega Drive

For Animations

  • Turn off Streaming as much as you can get away with. Especially for small and frequently used objects like bullets. There is a limit to how much this can be used though as objects that are not streaming are permanently in the VDP.
  • For animations that are only in one level, such as boss characters, you can turn off streaming and assign the object to that level so they don't use up VDP space in other levels.
  • For animations that are only in a subset of levels (for example, say a hypothetical monkey enemy in a jungle world), you can parent levels 2, 3 etc of that world to level 1, and then assign the animation to level 1. This has the effect of adding those animations in a non-streamed way to a set of levels.
  • For frequently used enemies, use synchronize cycle in order to reduce the amount of transfers to the VDP (only having one frame of a long walk cycle in memory at a time, for example).

For Maps

  • By default, all tiles take up two bytes of RAM, one byte for the block and one byte for the tile information. Since this data is loaded into RAM, it's crunched on the cartridge in ZX0 format by default.
  • However, you can save RAM space by flagging that either the blocks are write only, or the tiles are write only, or both. Reducing from two bytes of RAM per tile to one or even none. This comes at the cost of more cartridge space as such levels cannot be ZX0 compressed as they're stored in ROM.
  • If a map has block set as write only, it only means you can't change them - but you can turn them on or off by destroying or setting a tile (assuming tiles are not write only).

For Panels

  • On Parallax Panels, unsliced provides the best speed at the cost of not being able to do parallax slices. Tile is slower but provides the ability to do slices at no thinner than 8 pixels. Line allows full scrolling on every line, but is slower still and requires manually scrolling the game in codeblocks.

For Project Settings

  • Turn off Read Six Buttons unless absolutely necessary for your game.

NeoGeo

For Maps

  • Consider using the auto animation flag on tiles (which is only compatible with 4 and 8 frame tile animations) in order to reduce the overhead of tile animations as far as possible.
  • By default, all tiles take up two bytes of RAM, one byte for the block and one byte for the tile information. Since this data is loaded into RAM, it's crunched on the cartridge in ZX0 format by default.
  • However, you can save RAM space by flagging that either the blocks are write only, or the tiles are write only, or both. Reducing from two bytes of RAM per tile to one or even none. This comes at the cost of more cartridge space as such levels cannot be ZX0 compressed as they're stored in ROM.
  • If a map has block set as write only, it only means you can't change them - but you can turn them on or off by destroying or setting a tile (assuming tiles are not write only).

For Project Settings

  • To reduce frame skips on fade in, reduce the number of palettes to the actual number of palettes you're using.