Hello there. I don't normally chime in on this list, but thought I could 
explain my usage of CHANGE-CLASS in a large Common Lisp project (~20k lines of 
code).

Said application is a game engine, as anyone familiar with my work would 
probably have guessed as much (I have several, this being the latest one I have 
developed over the past 2 years). CLOS, and most notably CHANGE-CLASS is an 
integral part of the entire system.

During a running game, there can exist multiple "entities" in the game world. 
They do nothing on their own however, until a "component" is attached to one. 
Entities and components are both represented as standard-object's. At any point 
in a game developer's code, they can create an entity, and either immediately 
or some other time later, attach a new component, or detach a previously 
attached component to/from an entity.

In actuality, there is no "has-a" relationship between entities and components. 
Upon attaching or detaching of a component from an entity, what effectively 
happens is the entity's class is changed by means of CHANGE-CLASS. It is done 
so in a way that it's class precedence list is topologically sorted according 
to an ordering the game developer defines. For example, they could specify that 
COMPONENT-FOO should be before or after COMPONENT-BAR.

At various points during the execution of each game frame, various hooks are 
called. For example, there  are "on attach" and "on detach" hooks that are 
called when a component is attached or detached from an entity. There are also 
an "on render" hook that is fired if a particular component should be rendered 
to the screen, and many others.

However, since these hooks are defined by the programmer specializing on a 
component class, an invocation of any one of these hooks would only affect the 
most applicable object. And this is why we topologically sort the class 
precedence list as mentioned above; instead of the user-definable hooks using 
standard method combination, they actually all use PROGN method combination. 
This means any time a hook is fired for an entity, it will affect all of the 
components it has attached (with a no-op method for those the programmer does 
not define).

This whole system works beautifully for us, even though I probably didn't 
explain it very well. I personally use it for games with thousands of entities 
with various components each, that all have to be processed each frame, where 1 
frame is 1/60 of a second. I am bottle-necked by CLOS somewhere around 2000 
entities with a few components each for my personal game in progress, but I 
don't find this to be a problem for what I need currently.

Reply via email to