A resposta de Josh é incrível, mas eu gostaria de acrescentar:
Um dos recursos mais legais do Entity / Component é a maneira orientada por dados na qual todas as "coisas" do seu jogo são criadas e gerenciadas. Pelo que vi, depois de criar uma boa biblioteca de tipos e sistemas de componentes, você pode criar praticamente qualquer coisa com modificações mínimas de código. (Nota: mínimo! = 0 )
Ao definir o seu jogo em termos de comportamento e permitir-se modificar esses comportamentos em tempo real - durante a inicialização, durante a inicialização, carregando-os de um script ou banco de dados etc. -, você abre um mundo inteiro de novas possibilidades. Quer ver por que suas sombras não pousam onde você esperaria? Adicione um componente de câmera / POV à sua luz.
Entidade / componente permite criar o que quiser, desde que você tenha criado os blocos.
Além disso, a herança múltipla causa o mesmo problema que a herança única. Quando você adiciona um atributo ou comportamento na hierarquia, ele se propaga. Contanto que você esteja criando hierarquias profundas, você encontrará situações em que carrega peso desnecessário, duplica código ou resolve conflitos. A maior parte disso pode ser evitada quando você imagina seu jogo como dados.
Comecei a brincar com isso nas últimas semanas, mas estou impressionado com a simplicidade das coisas. Não é uma bala de prata - já deparei com alguns casos em que anexar um lambda a um componente era a maneira mais limpa e conveniente de resolver um problema - mas é um ótimo padrão, se você pode chamar assim.
Em uma observação um pouco relacionada: um dos grandes e chatos geradores de manutenção em aplicativos centrados em dados (sites etc.) está mapeando objetos hierárquicos em bancos de dados relacionais. Temos muitas soluções bacanas por aí, mas elas são basicamente todos os hacks projetados para nivelar hierarquias. Em vez de criar seu modelo para que ele atenda ao objetivo do aplicativo, você acaba comprometendo entre uma hierarquia eficaz e uma representação relacional lógica. Estou brincando com a idéia de reconstruir uma hierarquia bastante grande em um dos meus aplicativos como um sistema de entidade / componente - abandone a hierarquia e torne o banco de dados o Gold Standard para o restante da implementação - e é promissor.
Quando você integra recursos como geração dinâmica de código / cache de código que pode resolver problemas de desempenho, acaba com uma arquitetura lógica rápida, flexível e que pode apenas atingir o objetivo de código reutilizável do OOP de uma maneira muito, muito melhor.