Log In  
0

Pico8 use 2 functions to update stuff at a regular basis. One _update() function called every 1/30s (or 1/60s since the 0.1.8 version) and one _draw() function that do the same thing.

That allow, for a project use that use the second as a time unit, to not slow down that unit.

From manual: _draw() is normally called at 30fps, but if it can not complete in time, PICO-8 will attempt to run at 15ps and call _update() twice per visible frame to compensate.

e.g., If I decide to make a sprite move from A to B in exactly 1 second but have a render process slower than 1/30s. I can put the move process in the _update function and the rendering in the _draw() function. The animation will not be smooth but the sprite will move from A to B in exactly 1 second.
This is true if the _update() function do not take more than 1/30s to execute.
This simple separation is only used for that purpose.

For my shmup project (bullet hell/danmaku) project, it's different.

The collisions are not physically computed because it will be too slow. It's a very simple collision system that use pixel color. At each frame, if a bullet is draw on the player ship, a collision will be produced.

Using a pixel-based collision system as a caveat: If a bullet move down at 3 pixel speed and the ship move up at 2 pixel speed. They can cross each other without generate collision. Why it is like it? Because the main purpose of a my shump is not about dodging a single bullets but a wall of hundreds. It will happen for a player to cross over a bullet without touching it but he will probably hit the following one. So it's ok.

That explained, It will be unfair to compute the collision and the rendering at two different speed using the two available functions _update() and _draw(), because player ship would collide with bullets that as not be rendered. To avoid that, everything related to the gameplay is in the _draw() function, collision and rendering.

What it change? It did not produce a time-based game but a frame-based game.

technique
P#46003 2017-11-08 05:33

1

For my shmup project (bullet hell/danmaku), my first step was to work on the bullet/pattern system because it's related to the core of the gameplay (dodging bullets); and also because it's accountable to the beauty of the game.

First, I need limits. I decide to handle 128 bullets at a time. New bullets will replace the old one. From my perspective, is not a big deal. Players probably won't notice it. But if it happens, maybe it's because that bullet stays visible too long on the screen, turning arround following a circular path or simply moving too slowly. So, fuck that bullet.

I decide to create a table of bullet objects during the _init() proccess and reuse them instead of creating/deleting every time. It is called object pool

The process: every time I need to shoot a bullet, I select the next bullet object in the pool, If I reach the last one, I loop to the first. Simple.

To do that, I need:

  • a list of bullet objects,
  • an index for the next bullet index to use
  • and a number of max bullet:
bullets={}
bullets.next=1
bullets.len=128

Notice how the bullets table is used as an indexed list (to store bullets objects) and an object (to store properties).

During the _init() process, I warm up the table by creating all the bullet objects:

for i=1,bullets.len do
  local b={}
  bullets[i]=b
  ...later... -- warmup bullet properties
end

Next, every time I want to shoot a new bullet:

function shoot(...later...)
  -- get the bullet object
  local b=bullets[bullets.next]
  -- increment the next property
  bullets.next=(bullets.next%bullets.len)+1
  ...later... -- setup bullet properties
end

This little code make the .next property goes from 1 to 128 and loop to 1. Remember, tables in lua do not start at 0, but 1.

To draw the bullets:

for i=1,bullets.len do
 local b=bullets[i]
 spr(...later...)
end

I'm using a simple for loop here and not a for all() because I need the index for other purpose.

I'm using this technique for every data objects in my shmup game: bullet pattern functions, enemy ship, particles
With a little subtlety for the particles, because the objects are render sequentially, particles would appear stacked on each other. To fix that, when I need to create a new one, I do not increment by 1 but 7:

particles.next=(particles.next+6)%32+1

That's all for today, I hope you will use object pooling like crazy for your futur pico8 projects.

technique
P#32758 2016-11-25 16:29


:: More
X
About | Contact | Updates | Terms of Use
Follow Lexaloffle:        
Generated 2017-11-22 03:52 | 0.211s | 1572k | Q:11