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).
fuente
System
/ s clase / s para permitir que los componentes sigan siendo estructuras de datos.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.Respuestas:
Puede crear un sistema ScriptExecutionSystem que funcione en todas las entidades con un componente Script. Obtiene todos los componentes de la entidad que podrían ser útiles para exponer al sistema de secuencias de comandos y los pasa a la función de secuencias de comandos.
Otro enfoque sería lograr que sus usuarios también adopten ECS y les permitan definir sus propios componentes e implementar sus propios sistemas utilizando el lenguaje de secuencias de comandos.
fuente
ECS tiene sus pros y sus contras. Los scripts fáciles de usar no son una de sus ventajas.
El problema que resuelve ECS es la capacidad de tener una gran cantidad de cosas similares en tu juego al mismo tiempo mientras se mantiene el rendimiento. Pero esta solución tiene un costo: el costo de una arquitectura fácil de usar. No es la mejor arquitectura para cada juego.
Por ejemplo, ECS habría sido una buena opción para Space Invaders , pero no tanto para PacMan .
Entonces, no es exactamente la respuesta que estaba buscando, pero es posible que ECS no sea la herramienta adecuada para su trabajo.
Si agrega una envoltura, observe el costo general. Si terminas eliminando el aumento de rendimiento de ECS en tu contenedor, entonces tienes lo peor de ambos mundos.
Pero para responder directamente a su pregunta: "Para todos los desarrolladores de juegos que han implementado secuencias de comandos en un ECS anteriormente, ¿cómo lo hicieron?"
Casi exactamente como lo estás haciendo, sin envoltorio. Las entidades no tienen más que un identificador. Los componentes no tienen más que datos. Los sistemas no tienen nada más que lógica. Los sistemas que aceptan entidades con los componentes necesarios se ejecutan. Agregue sistemas, entidades y componentes libremente.
Una vez utilicé un marco con un cuarto aspecto, llamado pizarra. Básicamente era una forma en que los sistemas se comunicaban entre sí. Creó más problemas de los que resolvió.
Relacionado: ¿Debería implementar Entity Component System en todos mis proyectos?
fuente
Con ECS puede dividirse en una única responsabilidad, por lo que cualquier entidad que se mueva querría dos componentes de datos: un MoveComponent y un MoveSpeedComponent.
así que ahora en su conversión agrega esos componentes a sus entidades
Ahora que tenemos la conversión y los datos que podemos trasladar al sistema, eliminé el sistema de entrada para facilitar la lectura, pero si desea obtener más información sobre el sistema de entrada, lo tendré todo en mi artículo la próxima semana sobre la unidad de conexión.
Tenga en cuenta que la clase anterior está utilizando Unity.Mathmatics. Esto es excelente para poder utilizar diferentes funciones matemáticas con las que está acostumbrado a trabajar en los sistemas normales. Con todo esto en línea, ahora puede trabajar en el comportamiento de las entidades, nuevamente eliminé la entrada aquí, pero todo esto se explica mucho mejor en el artículo.
Ahora puede introducir entidades que avanzarán a gran velocidad.
Pero también esto moverá a todas las entidades con este comportamiento para que pueda introducir etiquetas, por ejemplo, si agregó un PlayerTag, entonces solo la entidad con el playerTag IComponentData podrá realizar MoveForward si solo quiero mover el reproductor como el ejemplo abajo.
Voy a profundizar en eso también en el artículo, pero se ve así en un ComponentSystem típico
Gran parte de esto se explica bastante bien en la presentación de Angry Dots con Mike Geig, si aún no lo ha visto, le recomiendo echarle un vistazo. Señalaré mi artículo también después de que esté listo. Realmente debería ser útil obtener varias de esas cosas con las que está acostumbrado a trabajar de la manera que le gustaría en ECS.
fuente