beast/overview/index.html @ ddd8d6bd4f50
cl-ggp: Update site.
author |
Steve Losh <steve@stevelosh.com> |
date |
Mon, 15 Jan 2018 15:26:35 -0500 |
parents |
f3fc28996523 |
children |
4fd427fcd0a2 |
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>Overview / beast</title>
<link rel="stylesheet" href="../_dmedia/pygments-clean.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>