This simple game is built on the ECS described here: Minimal entity component system, take 2
local ecs = require 'ecs'
local ui = require 'ui'
local entities = {}
local velocitySystem = ecs.createSystem(
{ 'position', 'velocity' },
function (entity, pos, vel)
pos.x = pos.x + vel.vx
pos.y = pos.y + vel.vy
end)
local rectangleRenderingSystem = ecs.createSystem(
{ 'position', 'rectangle' },
function (entity, pos, rect)
ui.drawRect(pos, rect)
end)
local aiSystem = ecs.createSystem(
{ 'position', 'speed', 'ai' },
function (entity, pos, speed)
local closestBallPos
for target, posB in ecs.each(entities, { 'position', 'ball' }) do
if (not closestBallPos) or (posB.x > closestBallPos.x) then
closestBallPos = posB
end
end
pos.y = pos.y + (closestBallPos.y - pos.y) * speed
end)
local inputSystem = ecs.createSystem(
{ 'position', 'player' },
function (entity, pos)
pos.y = ui.mouseY or 0
end)
local outOfBoundsSystem = ecs.createSystem(
{ 'position', 'rectangle', 'velocity', 'ball' },
function (entity, pos, rect, vel)
if pos.x <= 0 then
print('Right player scored')
pos.x = 50
elseif pos.x >= 100 then
print('Left player scored')
pos.x = 50
end
end)
local bounceSystem = ecs.createSystem(
{ 'position', 'rectangle', 'velocity', 'ball' },
function (entity, pos, rect, vel)
if pos.y <= 0 or pos.y >= 100 then
vel.vy = vel.vy * -1
end
end)
local function checkCollision(posA, rectA, posB, rectB)
local halfHeightA, halfHeightB = rectA.h / 2, rectB.h / 2
local halfWidthA, halfWidthB = rectA.w / 2, rectB.w / 2
if (posA.y + halfHeightA <= posB.y - halfHeightB) or
(posA.y - halfHeightA >= posB.y + halfHeightB) or
(posA.x + halfWidthA <= posB.x - halfWidthB) or
(posA.x - halfWidthA >= posB.x + halfWidthB) then
return false
end
return true
end
local collisionSystem = ecs.createSystem(
{ 'collidable', 'position', 'rectangle', 'velocity' },
function (entity, col, pos, rect, vel)
for target, posB, rectB in ecs.each(entities,
{ 'position', 'rectangle', 'solid' }) do
if entity == target then next() end
if checkCollision(pos, rect, posB, rectB) then
vel.vx = vel.vx * -1
vel.vy = vel.vy * -1
end
end
end)
local ball = {
ball = true,
collidable = true,
position = { x = 50, y = 33 },
velocity = { vx = 1, vy = 1 },
rectangle = { w = 2, h = 2 },
}
local leftPaddle = {
player = true,
solid = true,
position = { x = 0, y = 50 },
rectangle = { w = 4, h = 10 },
}
local rightPaddle = {
ai = true,
solid = true,
speed = 0.5,
position = { x = 100, y = 50 },
rectangle = { w = 4, h = 10 },
}
table.insert(entities, ball)
table.insert(entities, leftPaddle)
table.insert(entities, rightPaddle)
ui.loop(function ()
velocitySystem(entities)
rectangleRenderingSystem(entities)
aiSystem(entities)
inputSystem(entities)
outOfBoundsSystem(entities)
bounceSystem(entities)
collisionSystem(entities)
end)
I'm wondering if I have the right idea about how I should be using an ECS. If there are problems with the ECS itself, please address it in linked question rather than here.
The "ui" script that's required at the top is some uninteresting OpenGL/GLUT stuff using luaglut. I can post it if necessary, but it should be pretty clear what it's doing.
I realize there are lots of magic numbers. I don't care, it's just a throwaway project to make sure the ECS is doing its job. All of the coordinates and measurements are in percents, so just pretend I made constants named FIFTY_PERCENT and so on.