beast/overview/index.html @ 4fd427fcd0a2

beast: Update site.
author Steve Losh <steve@stevelosh.com>
date Mon, 23 Dec 2019 16:22:20 -0500
parents f3fc28996523
children (none)
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <title>Overview / beast</title>
        <link rel="stylesheet" href="../_dmedia/tango.css"/>
        <link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/>
        <script src="../_dmedia/less.js" type="text/javascript">
        </script>
    </head>
    <body class="content">
        <div class="wrap">
            <header><h1><a href="..">beast</a></h1></header>
                <div class="markdown">
<h1 id="overview"><a href="">Overview</a></h1><p>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:</p>
<ul>
<li><a href="http://gameprogrammingpatterns.com/component.html">http://gameprogrammingpatterns.com/component.html</a></li>
<li><a href="http://en.wikipedia.org/wiki/Entity_component_system">http://en.wikipedia.org/wiki/Entity_component_system</a></li>
<li><a href="http://www.gamedev.net/page/resources/_/technical/game-programming/understanding-component-entity-systems-r3013">http://www.gamedev.net/page/resources/_/technical/game-programming/understanding-component-entity-systems-r3013</a></li>
</ul>
<p>There were a couple of ECS libraries for Common Lisp already:</p>
<ul>
<li>cl-ecs</li>
<li>ecstasy</li>
</ul>
<p>Which were both superseded by <a href="https://github.com/mfiano/net.axity.common-lisp.gamedev/tree/master/ecs">net.axity.common-lisp.gamedev.ecs</a>.</p>
<p>All of these favor composition over inheritance -- game objects (entities)
<em>contain</em> various components, but they don't <em>inherit</em> from components.</p>
<p>Beast takes the opposite approach, favoring (restricted) inheritance over
composition.</p>
<p>Components in Beast are called "aspects" to try to overload the word "component"
a little bit less in this crazy world.  Aspects are essentially
<a href="https://en.wikipedia.org/wiki/Mixin">mixins</a>, with some sugar for defining them
and running systems over them:</p>
<div class="codehilite"><pre><span/><span class="p">(</span><span class="nv">define-aspect</span> <span class="nv">throwable</span> <span class="nv">accuracy</span> <span class="nv">damage</span><span class="p">)</span>
<span class="p">(</span><span class="nv">define-aspect</span> <span class="nv">edible</span> <span class="nv">nutrition-value</span><span class="p">)</span>

<span class="p">(</span><span class="nv">define-entity</span> <span class="nv">dart</span> <span class="p">(</span><span class="nv">throwable</span><span class="p">))</span>
<span class="p">(</span><span class="nv">define-entity</span> <span class="nv">cheese</span> <span class="p">(</span><span class="nv">edible</span><span class="p">))</span>
<span class="p">(</span><span class="nv">define-entity</span> <span class="nv">pie</span> <span class="p">(</span><span class="nv">throwable</span> <span class="nv">edible</span><span class="p">))</span>

<span class="p">(</span><span class="nv">define-system</span> <span class="nv">rot-food</span> <span class="p">((</span><span class="nv">e</span> <span class="nv">edible</span><span class="p">))</span>
  <span class="p">(</span><span class="nb">decf</span> <span class="p">(</span><span class="nv">edible/nutrition-value</span> <span class="nv">e</span><span class="p">))</span>
  <span class="p">(</span><span class="nb">when</span> <span class="p">(</span><span class="nb">zerop</span> <span class="p">(</span><span class="nv">edible/nutrition-value</span> <span class="nv">e</span><span class="p">))</span>
    <span class="p">(</span><span class="nv">destroy-entity</span> <span class="nv">e</span><span class="p">)))</span>

<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*steel-dart*</span> 
  <span class="p">(</span><span class="nv">create-entity</span> <span class="ss">'dart</span>
    <span class="ss">:throwable/accuracy</span> <span class="mf">0.9</span>
    <span class="ss">:throwable/damage</span> <span class="mi">10</span><span class="p">))</span>

<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*hunk-of-swiss*</span>
  <span class="p">(</span><span class="nv">create-entity</span> <span class="ss">'cheese</span>
    <span class="ss">:edible/nutrition-value</span> <span class="mi">50</span><span class="p">))</span>

<span class="p">(</span><span class="nb">defparameter</span> <span class="vg">*banana-cream-pie*</span>
  <span class="p">(</span><span class="nv">create-entity</span> <span class="ss">'pie</span>
    <span class="ss">:throwable/accuracy</span> <span class="mf">0.3</span>
    <span class="ss">:throwable/damage</span> <span class="mi">5</span>
    <span class="ss">:edible/nutrition-value</span> <span class="mi">30</span><span class="p">))</span>
</pre></div>


<p>Beast tries to be just a very thin layer over CLOS, because CLOS is quite
powerful.  You can use <code>typep</code>, generic methods, before/after/around methods,
and everything else CLOS gives you.</p>
<p>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.</p>
<p>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 <a href="../usage/">Usage</a> document.</p>
                </div>
            <footer><p><i>Made with Lisp and love by <a href="http://stevelosh.com/">Steve Losh</a> in Reykjavík, Iceland.</i></p>
<script>
  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');

  ga('create', 'UA-15328874-3', 'auto');
  ga('send', 'pageview');

</script></footer>
        </div>
    </body>
</html>