Log In  
Follow
jesstelford
Follow

Introducing PECS (PICO-8 Entity Component System)

✅ Small API
🏃‍♀️ Efficient even with lots of Entities
😀 Fun to say
🎈 Has a "lite" version for the token-conscious

Based on the fantastic Tiny ECS Framework by @KatrinaKitten 🙏

Full code & docs on GitHub: github.com/jesstelford/pecs

Here's a demo cart showing off some Particle Emitters using PECS v1.1.1:

Cart #pecs-0 | 2021-03-14 | Code ▽ | Embed ▽ | No License
5

Update 20210316: I have added a Camera Follow/Window example also:

Cart #pecs_camera-0 | 2021-03-16 | Code ▽ | Embed ▽ | No License
5

P#88957 2021-03-14 11:38 ( Edited 2021-03-16 00:23)

Cart #fgetty-1 | 2020-08-20 | Code ▽ | Embed ▽ | License: CC4-BY-NC-SA
5

Pseudo motion blur using the extended palette + dithering with fillp.

The crux of making this happen is:

local defaultFills = {
  0b0000000000000000,   -- solid
  0b0000000000000001.1, -- single pixel missing
  0b0000010100000101.1, -- 4 pixels missing
  0b0101101001011010.1, -- half pixels missing
  0b1111101011111010.1, -- 4 pixels rendered
  0b1111111111111110.1, -- 1 pixel rendered
}

-- Derived from https://stackoverflow.com/a/10086034/473961
function resizeAndFill(input, outputLength)
  assert(outputLength >= 2, "behaviour not defined for n<2")
  local step = (#input-1)/(outputLength-1)
  local result = {}
  for x=1,outputLength do
    result[x] = input[ceil(0.5 + (x-1)*step)]
  end
  return result
end

function createTrailSystem(shapes)
  local longestTrail = 0
  foreach(shapes, function(shape)
    shape.trailPositions = shape.trailPositions or {}
    shape.trails = shape.trails or 1
    shape.framesPerTrail = shape.framesPerTrail or 1
    -- Normalise the length of .fills to match number of trails
    shape.fills = resizeAndFill(shape.fills or defaultFills, shape.trails + 1)
    -- Normalise the length of .colors to match number of trails
    shape.colors = resizeAndFill(shape.colors or { shape.color }, shape.trails + 1)
    longestTrail = max(longestTrail, shape.trails)
  end)

  return {
    update=function()
      foreach(shapes, function(shape)
        if (#shape.trailPositions > (shape.trails * shape.framesPerTrail)) then
          -- Remove the oldest / now stale trail
          deli(shape.trailPositions, 1)
        end
        -- Add a new trail at the last position
        add(shape.trailPositions, { x=shape.x, y=shape.y })
      end)
    end,

    draw=function()
      -- Draw all the trail layers at the same time to avoid weird overlay
      -- artefacts
      for trail=longestTrail,1,-1 do
        foreach(shapes, function(shape)
          if (shape.trails < trail) then return end
          local frame = ((shape.trails - trail) * shape.framesPerTrail) + 1
          if (not shape.trailPositions[frame]) then return end
          -- +1 for 1-index in lua
          -- +1 for the actual shape itself
          local color = shape.colors[shape.trails - trail + 2]
          local fill = shape.fills[trail + 1]
          pal(shape.colors[1], color)
          shape.draw(
            shape.trailPositions[frame].x,
            shape.trailPositions[frame].y,
            shape.colors[1],
            fill
          )
        end)
      end
      foreach(shapes, function(shape)
        pal(shape.colors[1], shape.colors[1])
        shape.draw(shape.x, shape.y, shape.colors[1], shape.fills[1])
      end)
    end
  }
end

And it's used like so:

local trails

local shape = {
  colors={1,2,3,4},
  -- Force no dithering
  fills={0b0000000000000000},
  x=64,
  y=64,
  trails=4,
  framesPerTrail=7,
  draw=function(x, y, color, fill)
    fillp(fill)
    circfill(x, y, 8, color)
  end
}

function _init()
  -- Setup the palettes in use
  -- Colors figured out thanks to http://kometbomb.net/pico8/fadegen.html
  pal(1, 12, 1)
  pal(2, 129, 1)
  pal(3, 131, 1)
  pal(4, 140, 1)

  -- Initialise the shapes ready for motion trail
  trails = createTrailSystem({ shape })
end

function _update60()
  if (btn(0)) then shape.x -= 2 end -- Left
  if (btn(1)) then shape.x += 2 end -- Right
  if (btn(2)) then shape.y -= 2 end --Up
  if (btn(3)) then shape.y += 2 end --Down

  trails.update()
end

function _draw()
  cls(0)
  trails.draw()
end
P#80976 2020-08-20 16:35 ( Edited 2020-08-20 16:45)

Follow Lexaloffle:        
Generated 2021-06-15 19:08:23 | 0.099s | Q:16