Actualmente estoy creando un pequeño proyecto de pasatiempo para volver al desarrollo del juego, y he decidido estructurar mis entidades utilizando un ECS (Sistema de componentes de la entidad). Esta implementación de un ECS está estructurada así:
- Entidad : en mi caso, es un
int
identificador único que se utiliza como clave para una lista de componentes. - Componente : Contiene solo datos, por ejemplo, el
Position
componente contiene unax
yy
coordenada, y elMovement
componente contiene unaspeed
ydirection
variable. - Sistema : componente maneja, por ejemplo, las que se necesita
Position
yMovement
componentes y añade laspeed
ydirection
en la posición dex
yy
coordenadas.
Esto funciona bien, pero ahora deseo implementar secuencias de comandos en mis juegos, en forma de lenguaje de secuencias de comandos. En proyectos anteriores, he usado una implementación OOP de objetos de juego, lo que significaba que las secuencias de comandos eran bastante sencillas. Por ejemplo, un script simple podría verse así:
function start()
local future = entity:moveTo(pos1)
wait(future)
local response = entity:showDialog(dialog1)
if wait(response) == 1 then
local itemStack = entity:getInventory():removeItemByName("apple", 1)
world:getPlayer():getInventory():addItemStack(itemStack)
else
entity:setBehavior(world:getPlayer(), BEHAVIOR_HOSTILE)
end
end
Sin embargo, cuando se usa un ECS, la entidad en sí no tiene funciones como moveTo
o getInventory
, en cambio, el script anterior escrito en estilo ECS se vería así:
function start()
local movement = world:getComponent(MOVEMENT, entity)
movement:moveTo(pos1)
local position = world:getComponent(POSITION, entity)
local future = Future:untilEquals(position.pos, pos1)
wait(future)
local dialogComp = world:getComponent(DIALOG, entity)
local response = dialogComp:showDialog(dialog1)
if wait(response) == 1 then
local entityInventory = world:getComponent(INVENTORY, entity)
local playerInventory = world:getComponent(INVENTORY, world:getPlayer())
local itemStack = entityInventory:removeItemByName("apple", 1)
playerInventory:addItemStack(itemStack)
else
local entityBehavior = world:getComponent(BEHAVIOR, entity)
local playerBehavior = world:getComponent(BEHAVIOR, world:getPlayer())
entityBehavior:set(playerBehavior, BEHAVIOR_HOSTILE)
end
end
Esto es mucho más detallado en comparación con la versión OOP, lo que no es deseable cuando la secuencia de comandos está dirigida principalmente a no programadores (jugadores del juego).
Una solución sería tener algún tipo de objeto contenedor que encapsule Entity
y suministre funciones como moveTo
directamente, y maneje el resto internamente, aunque tal solución parece subóptima ya que toma mucho trabajo cubrir todos los componentes, y cada cada vez que se agrega un nuevo componente, deberá cambiar el objeto contenedor con nuevas funciones.
Para todos los desarrolladores de juegos que han implementado scripts en un ECS anteriormente, ¿cómo lo hicieron? El enfoque principal aquí es la usabilidad para el usuario final, con el menor costo de "mantenimiento" posible (preferiblemente no necesita cambiarlo cada vez que agrega un componente).
moveTo
método como parte del sistema subyacente en su caso de uso, por ejemplo, MoveSystem? De esta manera, no solo puede usarlo en los scripts que escribe, sino que también puede usarlo como parte del código de C ++ donde lo necesite. Entonces sí, tendrá que exponer nuevos métodos a medida que se agreguen nuevos sistemas, pero eso es de esperar ya que su comportamiento completamente nuevo estos sistemas introducen de todos modos.
System
/ s clase / s para permitir que los componentes sigan siendo estructuras de datos.