Volver

ECS

ECS

Un enemigo que vuela, un barril que explota, una nube que se mueve sola: en un juego hay miles de cosas distintas. Esta es la historia de cómo los programadores dejaron de construirlas como bloques gigantes y empezaron a armarlas con piezas, como un juego de encastre.

Las tres palabras de esta guía son entidad (quién es), componente (qué tiene) y sistema (qué hace con eso). Juntas forman la sigla ECS, de Entity Component System. No hace falta saber programar.

Empezá por el taller: tocá las piezas amarillas y mirá cómo cambia la Entidad #1. Después bajá. La línea de años de arriba te muestra en qué época estás.

Entidad #1

Todavía no tiene piezas: es solo un número, la Entidad #1.

Así queda la Entidad #1 con todas sus piezas: Posición, Dibujo, Vuela, Dispara y Vida.

Tocá una pieza para encastrarla y otra vez para sacarla. Cada pieza le da algo nuevo a la entidad.

Antes de 2002

El personaje que lo hacía todo

Durante años, la forma más común de programar un juego fue la herencia: un árbol genealógico de tipos de cosas. Arriba de todo está el «Personaje», que sabe dibujarse y moverse. De él heredan el «Caminante» y el «Volador», y del Caminante hereda el «Tirador», que además dispara.

Funciona bien hasta que alguien pide algo que el árbol no previó. ¿Un enemigo que vuela y dispara, pero no camina? No entra en ninguna rama: o hereda cosas que no necesita, o hay que copiar el código de disparar en otro lado.

Con el tiempo, la clase de arriba junta un poco de todo: física, sonido, dibujo, inteligencia. Robert Nystrom lo cuenta con un juego imaginario protagonizado por Bjørn, un panadero: su código termina amontonado en una sola clase de 5.000 líneas, que solo los programadores más valientes se animan a tocar, porque cualquier cambio chico puede romper algo lejano. A ese monstruo se le dice blob: una masa sin forma.

¿La herencia es mala? No. Sirve muy bien para muchas cosas. El problema aparece cuando hay que combinar habilidades libremente, que es justo lo que pasa en un juego.

El pedido: un enemigo que vuela y dispara, pero no camina.

Con herencia

  • Personaje
    • Caminante
      • Tirador
    • Volador

Con piezas

Entidad #8

PosiciónVuelaDispara

Fuentes: Robert Nystrom, Game Programming Patterns: Component (2014), y Mick West, Evolve Your Hierarchy (2007).

2002

Las primeras piezas

En la conferencia de desarrolladores de juegos de 2002 (la GDC), Scott Bilas, de Gas Powered Games, contó cómo habían hecho Dungeon Siege. El juego tenía más de 7.300 tipos de objetos y más de 100.000 objetos repartidos por el mapa: barriles, cofres, monstruos, puertas.

No los programaron uno por uno. Cada objeto se describía con datos: una lista de piezas independientes, como «se dibuja», «se rompe» o «camina». Para crear un objeto nuevo alcanzaba con elegir piezas. El lema de la charla lo resume: «no hace falta un ingeniero».

Barril

PosiciónDibujoSe rompe

Cofre

PosiciónDibujoSe abreGuarda cosas

Esqueleto

PosiciónDibujoCaminaAtacaVida

7.300 tipos de objeto, armados combinando las mismas piezas.

Fuentes: Scott Bilas, A Data-Driven Game Object System (GDC 2002), citado por Adam Martin en Entity Systems are the future of MMOG development (2007).

2007

Tony Hawk se desarma

En enero de 2007, Mick West, uno de los programadores de la serie Tony Hawk's Pro Skater en Neversoft, contó cómo habían pasado su motor de la herencia a los componentes. El motor venía de tres juegos y había juntado tanta complejidad que el cambio les llevó dos años.

No lo hicieron de golpe. En el taller podés recorrer las etapas que describe: primero ordenaron el blob por dentro, después lo convirtieron en un contenedor de piezas y, al final, el objeto desapareció. La cosa del juego pasó a ser solamente la suma de sus piezas.

Al principio hubo resistencia. West mostró que funcionaba con un primer componente chico, y el cambio se fue contagiando. El resultado: menos código repetido, menos errores y diseñadores que podían inventar objetos nuevos combinando piezas.

Ese mismo año, en septiembre, Adam Martin escribió una serie de artículos que hizo famosa la idea con el nombre de entity systems. Su argumento: con piezas, los juegos en línea pueden seguir cambiando después de salir, sin reescribir todo.

Una predicción exagerada. Adam Martin llegó a decir que los grandes juegos en línea serían «prácticamente imposibles» de hacer sin este enfoque. Era una opinión de 2007: muchos juegos exitosos se hicieron de otras formas.

Patinador: la clase que hace todo

DibujoFísicaSonidoTrucosCámara

El blob: una sola clase hace todo. Dibujo, física, sonido, trucos y cámara están mezclados en el mismo lugar.

Fuentes: Mick West, Evolve Your Hierarchy (2007), y Adam Martin, Entity Systems are the future of MMOG development (2007).

2009

Los datos mandan

Las piezas ordenaban el código. Pero en 2009 apareció otra pregunta: ¿cómo están ordenados los datos en la memoria? Noel Llopis lo planteó así: programar es, en el fondo, transformar datos. Si los datos están bien acomodados, el resto se vuelve más simple y más rápido.

Robert Nystrom lo explica con una analogía. Imaginá a un contador que necesita cajas con documentos. Si cada caja está en un almacén lejano, cada viaje le lleva un día. Pero si el depósito le manda un pallet entero con las cajas que están al lado de la que pidió, y están en el orden en que las va a usar, trabaja sin parar.

Con la computadora pasa lo mismo. El procesador tiene una memoria chiquita y rapidísima, la caché. Cuando busca un dato no trae solo ese: trae también los vecinos, un bloque de 64 a 128 bytes. Si lo que viene después está justo al lado, ya lo tiene a mano. Probalo en el taller.

¿Siempre es más rápido? No. Nystrom cuenta que reordenar los datos llegó a hacer un código cincuenta veces más rápido, pero en ese caso puntual. Si un programa no recorre muchos datos parecidos, la mejora puede ser mínima.

Almacén desordenado

Viajes: 0. Cajas: 0 de 8.

Cajas en fila

Viajes: 0. Cajas: 0 de 8.

Los dos necesitan las mismas 8 cajas, y cada viaje al almacén tarda lo mismo. Las marcadas son las que hacen falta.

Fuentes: Noel Llopis, Data-Oriented Design (2009), y Robert Nystrom, Game Programming Patterns: Data Locality (2014).

2014

Todo en fila

Para 2014 las dos ideas se juntaron: armar las cosas con piezas y guardar cada tipo de pieza en fila. En vez de que cada objeto tenga sus datos desparramados por la memoria, todas las posiciones van juntas, todas las velocidades juntas, y así.

Adam Martin hizo la cuenta para un juego grande: unas 100.000 entidades y unos 150 tipos de piezas distintas. Con esos números, que los datos estén pegados ya no es un detalle: decide si el juego llega o no a dibujar cada fotograma a tiempo.

Ese mismo año, Mike Acton dio una charla famosa, Data-Oriented Design and C++, que convenció a muchos programadores de pensar primero en los datos.

  • PPosición
  • VVelocidad
  • SSalud
  • Hueco

Desparramados: cada objeto guarda sus datos donde puede, con huecos en el medio. Para mover a todos, el procesador salta de un lado a otro.

100.000 entidades y 150 tipos de piezas en un juego grande, según Adam Martin.

Fuentes: Adam Martin, Data Structures for Entity Systems: Contiguous memory (2014), y la lista de Daniele Bartolini, data-oriented-design (charla de Mike Acton, 2014).

2018

Nace el ECS como lo conocemos

Con todo eso junto, la idea tomó su forma actual. Tiene tres partes, y cada una tiene su color en esta guía:

  • Una entidad es solo un número que identifica a una cosa del juego: la #1, la #2. No tiene código ni datos propios.
  • Un componente es una pieza de datos: una posición, una velocidad, una cantidad de vida. No hace nada por sí mismo.
  • Un sistema es una regla que recorre todas las entidades que tienen ciertas piezas y hace algo con ellas. El sistema «Mover» busca las que tienen Posición y Velocidad, y suma una a la otra.

La biblioteca Artemis, para Java, lo explica con un juego de plataformas: el jugador tiene las piezas Movimiento, Ubicación, Física y Control. Agregar un enemigo nuevo no obliga a tocar nada: alcanza con darle sus piezas, y los sistemas de dibujar y de mover lo procesan solos.

En 2018, Unity presentó su propio ECS en la GDC y lo incluyó como versión de prueba en Unity 2018.1. Ese mismo año, Aras Pranckevičius dio una charla que lo explicaba en detalle. Desde entonces, la sigla ECS dejó de ser cosa de unos pocos y llegó a los motores más usados.

Datos en las piezas, comportamiento en los sistemas. Es la regla de oro de la guía de buenas prácticas de Artemis-ODB. Si una pieza empieza a «hacer cosas», probablemente sea un sistema disfrazado.

Seis entidades y las piezas que tiene cada una
EntidadPosiciónVelocidadDibujoArma
#1JugadorSíSíSíSí
#2ÁrbolSíNoSíNo
#3BalaSíSíSíNo
#4NubeSíSíSíNo
#5TorretaSíNoSíSí
#6Punto de guardadoSíNoNoNo

Elegí un sistema para ver qué entidades recorre.

Fuentes: Introduction to Entity Systems y Best Practices (wiki de Artemis-ODB), la charla Job System & Entity Component System (Unity, GDC 2018) y la lista de Daniele Bartolini (charla de Aras Pranckevičius, 2018).

Hoy

ECS en los motores de hoy

Hoy ECS está en todos lados. Unity lo ofrece como su paquete Entities, que llegó a la versión 1.0 en junio de 2023. Unreal Engine 5 tiene Mass, nacido para las multitudes de la demo The Matrix Awakens. Bevy, un motor gratuito escrito en Rust que salió en agosto de 2020, está construido entero sobre ECS. Y hay bibliotecas independientes muy usadas, como flecs y EnTT.

La idea de separar los datos del comportamiento incluso salió de los videojuegos. En 2022, Brian Goetz, arquitecto de Java, escribió sobre la programación orientada a datos en ese lenguaje, y llegó a una conclusión equilibrada: los objetos y los datos son herramientas distintas, y conviven.

¿Entonces conviene usar ECS siempre? No necesariamente. En el taller tenés un resumen de cuándo brilla y cuándo sobra.

Unity

Paquete Entities 1.0 (2023)

Unreal Engine 5

Mass, para multitudes

Bevy

Hecho entero con ECS (2020)

flecs

Biblioteca para C y C++

EnTT

Biblioteca para C++

Artemis-ODB

Biblioteca para Java

Brilla cuando…

  • hay miles de cosas parecidas
  • las habilidades se combinan de muchas formas
  • el rendimiento importa mucho

Puede sobrar cuando…

  • el juego tiene pocos objetos
  • es un prototipo de un fin de semana
  • se trata de menús o interfaces

Fuentes: Sander Mertens, ECS FAQ; Brian Goetz, Data Oriented Programming in Java (2022); Unity, junio de 2023; Epic Games, Mass; Introducing Bevy 0.1 (2020).

Para consultar

Cuestionario

El cuestionario necesita JavaScript para funcionar.

En ECS, ¿qué es una entidad?

¿Dónde van los datos, como la posición o la vida?

¿Qué hace un sistema?

¿Qué problema tenía la herencia en los juegos?

¿Por qué guardar los datos en fila acelera el juego?

¿Conviene usar ECS siempre?

Glosario
Entidad
Un número que identifica a una cosa del juego. No tiene datos ni código propios.
Componente
Una pieza de datos que se le da a una entidad: posición, velocidad, vida.
Sistema
Una regla que recorre las entidades con ciertas piezas y hace algo con ellas.
Herencia
Armar tipos de cosas como un árbol genealógico, donde cada hijo hereda lo del padre.
Composición
Armar cosas combinando piezas, en vez de heredarlas.
Blob
Una clase gigante que hace de todo y se volvió difícil de cambiar.
Caché
Una memoria chica y muy rápida dentro del procesador.
Línea de caché
El bloque de datos vecinos, de 64 a 128 bytes, que el procesador trae de una vez.
Memoria contigua
Datos guardados uno al lado del otro, sin huecos.
Diseño orientado a datos
Pensar primero en qué datos hay y cómo se recorren, y después en el código.
Arquetipo
Un grupo de entidades con exactamente las mismas piezas. Muchos ECS las guardan juntas en una tabla.
Aclaraciones

Algunas fuentes de esta guía tienen errores o datos que quedaron viejos. Estas son aclaraciones que corrigen esos errores.

  • Las fechas de la línea de tiempo. Marcan cuándo se publicó cada idea en las fuentes, no cuándo se inventó. Por ejemplo, la wiki de Artemis-ODB que se cita es de 2016, pero la biblioteca original es anterior.
  • «Cincuenta veces más rápido». Es lo que logró Robert Nystrom en un caso concreto. No es una regla: la mejora depende de cuántos datos parecidos recorra el programa.
  • La predicción de Adam Martin (2007). Decía que los grandes juegos en línea serían prácticamente imposibles de hacer sin entity systems, o que costarían 100 millones de dólares. Era una opinión de época, no un hecho.
  • El nombre «ECS». Mick West habla de componentes y Adam Martin de entity systems. La sigla ECS se impuso después, cuando los motores grandes adoptaron la idea.
  • La propia Artemis-ODB cambió de idea. Su guía de buenas prácticas recomienda identificar las entidades con números y dejar de usar su antigua clase Entity: justamente la idea de que una entidad es solo un número.
  • Objetos contra datos. Noel Llopis (2009) culpa a la programación orientada a objetos de los problemas de rendimiento. Brian Goetz (2022) es más equilibrado: las dos formas conviven en el mismo programa.
  • Dos links no analizados. El artículo de Medium y la discusión de Reddit bloquean la lectura automática, así que no se usaron para esta guía.
Fuentes

Fuentes utilizadas para crear esta guía.