docs/01-overview.markdown @ b85831aa30bb
Unfuck the test system loading
author |
Steve Losh <steve@stevelosh.com> |
date |
Fri, 13 Jan 2017 21:54:26 +0000 |
parents |
42aaba702d24 |
children |
(none) |
Overview
========
When you're making a video game you need a way to model things in the game
world. In the past couple of decades Entity/Component systems have become
popular:
* <http://gameprogrammingpatterns.com/component.html>
* <http://en.wikipedia.org/wiki/Entity_component_system>
* <http://www.gamedev.net/page/resources/_/technical/game-programming/understanding-component-entity-systems-r3013>
There were a couple of ECS libraries for Common Lisp already:
* cl-ecs
* ecstasy
Which were both superseded by [net.axity.common-lisp.gamedev.ecs](https://github.com/mfiano/net.axity.common-lisp.gamedev/tree/master/ecs).
All of these favor composition over inheritance -- game objects (entities)
*contain* various components, but they don't *inherit* from components.
Beast takes the opposite approach, favoring (restricted) inheritance over
composition.
Components in Beast are called "aspects" to try to overload the word "component"
a little bit less in this crazy world. Aspects are essentially
[mixins](https://en.wikipedia.org/wiki/Mixin), with some sugar for defining them
and running systems over them:
:::lisp
(define-aspect throwable accuracy damage)
(define-aspect edible nutrition-value)
(define-entity dart (throwable))
(define-entity cheese (edible))
(define-entity pie (throwable edible))
(define-system rot-food ((e edible))
(decf (edible/nutrition-value e))
(when (zerop (edible/nutrition-value e))
(destroy-entity e)))
(defparameter *steel-dart*
(create-entity 'dart
:throwable/accuracy 0.9
:throwable/damage 10))
(defparameter *hunk-of-swiss*
(create-entity 'cheese
:edible/nutrition-value 50))
(defparameter *banana-cream-pie*
(create-entity 'pie
:throwable/accuracy 0.3
:throwable/damage 5
:edible/nutrition-value 30))
Beast tries to be just a very thin layer over CLOS, because CLOS is quite
powerful. You can use `typep`, generic methods, before/after/around methods,
and everything else CLOS gives you.
Like every engineering decision this comes with tradeoffs. You can't (easily)
add or remove aspects to/from a particular entity at runtime like you can with
cl-ecs. And there's no way to give an entity multiple "copies" of a single
aspect.
The author has found this approach to work well for his needs. You should take
a look at both approaches and decide which is best for you. If you want to read
more, check out the [Usage](../usage/) document.