https://tylorallison.github.io/ tylog 2022-05-11T14:46:58+00:00 »tylog« is a log of my gamedev experiences, trials, tribulations, and obstacles overcome. Tylor Allison https://tylorallison.github.io/ Jekyll https://tylorallison.github.io/games/spark/ Spark 2021-12-06T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ Can fairy magic overcome gnomish grumpiness? <h3 id="the-game">The Game</h3> <p>Set in the world Innis Fhaolain, guide the fairy Alette as she helps a village of grumpy gnomes. Discover why a gloom has been cast over the town, use Alette’s spark to solve puzzles unlocking more of the island, bring peace and joy to the citizens, and resolve Alette’s own troubled heart.</p> <p>Can you solve all the NPC puzzles? Can you find all the hidden items in the world that can be transformed by a little spark?</p> <p>Be the hope of gnomies everywhere! Shine your light and be the Spark of change!</p> <p>A HomeTeam GameDev project!</p> <p>A Javascript puzzle game.</p> <h3 id="my-contribution">My Contribution</h3> <p>Core gameplay, project lead, AI behaviors, main artist, primary writer, tutorial, UI framework and primitives, performance optimizations, environment design, building/furniture art, NPC and object interactions, pathfinding, fairy/gnome sprites, custom editor, particles, input handling, asset management, hints system</p> <h3 id="play">Play</h3> <p>This game was developed by members of the HomeTeam Gamedev club, myself included. Find out more about the club, or play this game: <a href="https://tylorallison.itch.io/spark">Play on itch.io</a></p> <h3 id="media">Media</h3> <div> <ul class="clearing-thumbs small-block-grid-4" data-clearing=""> <li><a href="https://tylorallison.github.io/images/game-spark_title.png"><img data-caption="Spark Title Page" src="https://tylorallison.github.io/images/game-spark_title-thumb.png" alt="Spark Title Page" /></a></li> </ul> </div> 2021-12-06T00:00:00+00:00 https://tylorallison.github.io/general/jspart/ An Intro to JS Particles, Pt. 1 2021-01-20T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ Full vanilla javascript particle walkthrough and implementation <script src="/assets/js/jspart.js"></script> <script> var mgrs = []; window.onload = async function() { let lastUpdate = performance.now(); const maxDeltaTime = 1000/20; window.requestAnimationFrame(loop); var ctx = { deltaTime: 0, } function loop(hts) { ctx.deltaTime = Math.min(maxDeltaTime, hts - lastUpdate); lastUpdate = hts; for (const mgr of mgrs) { mgr.update(ctx); mgr.render(); } loopID = window.requestAnimationFrame(loop); } } </script> <h3 id="about">About</h3> <p>This article will describe how you can implement a particle system in pure/vanilla javascript for use in web/html games or other web-based/browser projects.</p> <h3 id="meet-pete">Meet Pete</h3> <canvas id="cvs1" style="background-color: #373F51;"></canvas> <p><button id="button1" style="padding: 2px 8px;">Poke</button></p> <p>So <strong>Pete the Particle</strong> is a happy little particle running in the canvas shown above. Feel free to poke him if he isn’t showing up very well. But you may be wondering… What exactly is a particle and why would I want to implement my own? First, in my perspective at least, a particle is simply a procedurally-generated visual effect that can be added to a game (or any other visual project). Think of a shower of sparks when you shoot an enemy, a trail of dust that kicks up when your character runs, even footsteps that your character may leave in sand or snow. These can all be implemented through particles. And while there are other ways to implement visual effects, I’ll present you some advantages to using a particle system, which allows you to fire and forget visual effects so that your game logic doesn’t need to keep track of animation or effect state.</p> <script> class Mgr1 { constructor() { this.canvas = document.getElementById("cvs1"); this.width = window.innerWidth * .2; this.height = window.innerHeight*.2; this.canvas.width = this.width; this.canvas.height = this.height; this.ctx = this.canvas.getContext("2d"); this.makePete(); // wire poke button let b1 = document.getElementById("button1"); b1.onclick = this.makePete.bind(this); } makePete() { let v = 200; let angle = Math.random() * Math.PI*2; this.pete = new PeteParticle({ x: Math.random() * this.width, y: Math.random() * this.height, dx: v*Math.cos(angle), dy: v*Math.sin(angle), color: new Color(169,188,208,1), maxx: this.width, maxy: this.height, }); } update(ctx) { this.pete.update(ctx); } render() { this.ctx.clearRect(0,0,this.width,this.height); this.pete.render(this.ctx); } } mgrs.push(new Mgr1()); </script> <h3 id="game-hooks">Game Hooks</h3> <p>Let’s take a closer look at Pete and how Pete is integrated into the game loop. To understand how the particle logic works, we need to understand how it will be hooked into your game. The game model I’m using here assumes that all logic is tied to <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame">Animation Frames</a>. In each frame, you are making updates to your game model and then rendering out the results to display in your browser. The particle logic will follow this same model.</p> <p>I’ll assume that your game loop looks something like this:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">lastUpdate</span> <span class="o">=</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">maxDeltaTime</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">updateCtx</span> <span class="o">=</span> <span class="p">{</span> <span class="na">deltaTime</span><span class="p">:</span> <span class="mi">0</span> <span class="p">};</span> <span class="kd">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">renderCtx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span> <span class="kd">function</span> <span class="nx">loop</span><span class="p">(</span><span class="nx">hts</span><span class="p">)</span> <span class="p">{</span> <span class="nx">updateCtx</span><span class="p">.</span><span class="nx">deltaTime</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">maxDeltaTime</span><span class="p">,</span> <span class="nx">hts</span> <span class="o">-</span> <span class="nx">lastUpdate</span><span class="p">);</span> <span class="nx">lastUpdate</span> <span class="o">=</span> <span class="nx">hts</span><span class="p">;</span> <span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">);</span> <span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">);</span> <span class="nx">loopID</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">loop</span><span class="p">);</span> <span class="p">}</span></code></pre></figure> <p>Where <code class="language-plaintext highlighter-rouge">update(updateCtx)</code> and <code class="language-plaintext highlighter-rouge">render(renderCtx)</code> are your entry points to your game logic. We’ll use these same entry points for operations on particles. While you could get away with a single function call, there’s reasons breaking them out makes sense. Specifically there may be cases where you want to update the state of particles (e.g.: position and such), but don’t want to render it (e.g.: offscreen or not visible). Also note that I’m passing in a few variables which will be important to particle operations. <code class="language-plaintext highlighter-rouge">updateCtx</code> is an object that contains a <code class="language-plaintext highlighter-rouge">deltaTime</code> attribute, and <code class="language-plaintext highlighter-rouge">renderCtx</code> is the canvas context onto which stuff will be rendered. We’ll see how these are used when we dive deeper into what makes <strong>Pete</strong> tick. Let’s assume that your main <code class="language-plaintext highlighter-rouge">update</code> and <code class="language-plaintext highlighter-rouge">render</code> methods have been updated to call the particle’s entry points, as shown here:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">const</span> <span class="nx">pete</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">PeteParticle</span><span class="p">();</span> <span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">pete</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">);</span> <span class="p">...</span> <span class="p">}</span> <span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">)</span> <span class="p">{</span> <span class="p">...</span> <span class="nx">pete</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">);</span> <span class="p">...</span> <span class="p">}</span></code></pre></figure> <h3 id="particle-logic">Particle Logic</h3> <p>To implement <strong>Pete</strong>, we are going to introduce a class to store some data and define the game hooks to allow updates and rendering of the particle. What data is needed to model the behaviour we are seeing? Well, there’s a position on the screen to start with (let’s call that <code class="language-plaintext highlighter-rouge">x</code>,<code class="language-plaintext highlighter-rouge">y</code> coordinates), and the shape of the particle, which is a filled circle which can simply be represented by a radius (<code class="language-plaintext highlighter-rouge">size</code>) as well as a color associated with the particle (<code class="language-plaintext highlighter-rouge">color</code>). Those are all static properties, but what about the behaviour? Well, the particle is moving. There’s lots of ways we could represent that, but the easiest is to just consider the change of position based on x and y axis over time. Let’s call the change in x, delta X and the change in y, delta Y (<code class="language-plaintext highlighter-rouge">dx</code> and <code class="language-plaintext highlighter-rouge">dy</code> for short). Finally, when the particle hits the edge of the canvas it changes direction. We need some logic to keep track of the bounds of the canvas. This can be represented by a minimum and maximum x value (<code class="language-plaintext highlighter-rouge">minx</code>, <code class="language-plaintext highlighter-rouge">maxx</code>) and same for a y value (<code class="language-plaintext highlighter-rouge">miny</code>, <code class="language-plaintext highlighter-rouge">maxy</code>). Putting that into a class structure and initializing looks like:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">class</span> <span class="nx">PeteParticle</span> <span class="p">{</span> <span class="kd">constructor</span><span class="p">(</span><span class="nx">spec</span><span class="o">=</span><span class="p">{})</span> <span class="p">{</span> <span class="c1">// current position</span> <span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">x</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">y</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// deltas for x,y - represents velocity in pixels per second</span> <span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">=</span> <span class="p">(</span><span class="nx">spec</span><span class="p">.</span><span class="nx">dx</span> <span class="o">||</span> <span class="mi">50</span><span class="p">)</span> <span class="o">*</span> <span class="p">.</span><span class="mi">001</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">=</span> <span class="p">(</span><span class="nx">spec</span><span class="p">.</span><span class="nx">dy</span> <span class="o">||</span> <span class="mi">50</span><span class="p">)</span> <span class="o">*</span> <span class="p">.</span><span class="mi">001</span><span class="p">;</span> <span class="c1">// size of particle</span> <span class="k">this</span><span class="p">.</span><span class="nx">size</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">size</span> <span class="o">||</span> <span class="mi">5</span><span class="p">;</span> <span class="c1">// color of particle</span> <span class="k">this</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">color</span> <span class="o">||</span> <span class="dl">"</span><span class="s2">red</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// bounds</span> <span class="k">this</span><span class="p">.</span><span class="nx">minx</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">minx</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">miny</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">miny</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxx</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">maxx</span> <span class="o">||</span> <span class="mi">400</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxy</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">maxy</span> <span class="o">||</span> <span class="mi">400</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <p>Two notes here:</p> <ol> <li>I like to use <code class="language-plaintext highlighter-rouge">spec</code> objects in constructors. It allows you to easily change the calling parameters for object creation (which we will do later) as well as allows you a way to easily specify defaults for object creation, which I’ve done here (<em><code class="language-plaintext highlighter-rouge">spec.x || 0</code> becomes <code class="language-plaintext highlighter-rouge">undefined || 0</code> which becomes <code class="language-plaintext highlighter-rouge">0</code> when <code class="language-plaintext highlighter-rouge">spec.x</code> is <code class="language-plaintext highlighter-rouge">undefined</code></em>). So you can make a <strong>PeteParticle</strong> just by calling <code class="language-plaintext highlighter-rouge">new PeteParticle()</code> and you will get something that works.</li> <li>You’ll note a <code class="language-plaintext highlighter-rouge">.001</code> multipled to the <code class="language-plaintext highlighter-rouge">dx</code> and <code class="language-plaintext highlighter-rouge">dy</code> values. If you look back at the game loop, the <code class="language-plaintext highlighter-rouge">deltaTime</code> attribute being passed to the update function is computed in milliseconds. I like to think of velocity in terms of pixels per second, so to get to pixels per millisecond, multiply by <code class="language-plaintext highlighter-rouge">.001</code>.</li> </ol> <p>Now that we have data, let’s take a closer look at the <code class="language-plaintext highlighter-rouge">update</code> and <code class="language-plaintext highlighter-rouge">render</code> methods that are needed to get <strong>Pete</strong> moving and rendering. If you remember, <code class="language-plaintext highlighter-rouge">update</code> is being passed a context that includes a <code class="language-plaintext highlighter-rouge">deltaTime</code> attribute which represents the amount of time in milliseconds since the last <code class="language-plaintext highlighter-rouge">update</code> was called. This is the elapsed time since we’ve last made any changes to <strong>Pete</strong>. In the data for <strong>Pete</strong>, we expressed movement by keeping track of a delta x and y which represented the number of pixels of movement per unit time (millisecond in our case, remembering we converted <code class="language-plaintext highlighter-rouge">dx</code> and <code class="language-plaintext highlighter-rouge">dy</code> in seconds by multiplying by <code class="language-plaintext highlighter-rouge">.001</code> converting to milliseconds). So to get how far <strong>Pete</strong> should have traveled since the last update, all we need to do is multiply the <code class="language-plaintext highlighter-rouge">dx</code> and <code class="language-plaintext highlighter-rouge">dy</code> values by <code class="language-plaintext highlighter-rouge">deltaTime</code> and then store the results. Take a look:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">)</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">dt</span> <span class="o">=</span> <span class="nx">updateCtx</span><span class="p">.</span><span class="nx">deltaTime</span><span class="p">;</span> <span class="c1">// update position</span> <span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">+=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">*</span> <span class="nx">dt</span><span class="p">));</span> <span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">+=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">*</span> <span class="nx">dt</span><span class="p">));</span> <span class="p">}</span></code></pre></figure> <p>Pretty simple right? <em>Note: I’m rounding the change in position I’m adding to <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> as a best practice to keep <code class="language-plaintext highlighter-rouge">x</code> and <code class="language-plaintext highlighter-rouge">y</code> as integers. Floats work too, but are a bit more computationally heavy when rendering.</em> This is enough to get <strong>Pete</strong> moving, but <strong>Pete</strong> would quickly become lost as he wanders off the screen we’ve provided. To keep <strong>Pete</strong> safe (and always visible) we’re going to add a fence that tells <strong>Pete</strong> how far he can go in any direction. If he goes to far in any direction, we’ll tell him to turn around and go in the opposite direction at the same speed. From our data model for <strong>Pete</strong> the fence was represented by the <code class="language-plaintext highlighter-rouge">minx, miny, maxx, maxy</code> variables. Let’s take a look at how to implement the fence:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">)</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">dt</span> <span class="o">=</span> <span class="nx">updateCtx</span><span class="p">.</span><span class="nx">deltaTime</span><span class="p">;</span> <span class="c1">// update position</span> <span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">+=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">*</span> <span class="nx">dt</span><span class="p">));</span> <span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">+=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">((</span><span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">*</span> <span class="nx">dt</span><span class="p">));</span> <span class="c1">// update direction</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">&lt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">minx</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dx</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">&lt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">miny</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dy</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">&gt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxx</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">=</span> <span class="o">-</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dx</span><span class="p">));</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">&gt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxy</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">=</span> <span class="o">-</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dy</span><span class="p">));</span> <span class="p">}</span></code></pre></figure> <p>The logic for the fence is pretty straightforward. If <strong>Pete’s</strong> position ever drops below a minimum or above a maximum, we change the corresponding position delta to be negative or positive to turn <strong>Pete</strong> around. <em>Implementation Note: using <code class="language-plaintext highlighter-rouge">Math.abs</code> is required here vs. just flipping the sign of the delta (e.g.: <code class="language-plaintext highlighter-rouge">this.dx = -this.dx</code>). Remember that the delta time passed in is variable as it is the actual milliseconds since last call. For example, say your <code class="language-plaintext highlighter-rouge">dx</code> is -5 and current <code class="language-plaintext highlighter-rouge">x</code> position is 5 and you are passed a <code class="language-plaintext highlighter-rouge">deltaTime</code> of 10. Your new position would be calculated as <code class="language-plaintext highlighter-rouge">this.x += -5*10</code> or <code class="language-plaintext highlighter-rouge">this.x = 5 - 50</code> or <code class="language-plaintext highlighter-rouge">this.x = -45</code>. Assuming our <code class="language-plaintext highlighter-rouge">minx</code> is 0, -45 is well under this, so we would swap the sign of <code class="language-plaintext highlighter-rouge">dx</code> which would now be 5. Now say the next frame’s <code class="language-plaintext highlighter-rouge">deltaTime</code> is 5. Using the same computation, <code class="language-plaintext highlighter-rouge">this.x += 5*5</code> or <code class="language-plaintext highlighter-rouge">this.x = -45 + 25</code> or <code class="language-plaintext highlighter-rouge">this.x = -20</code>. Uh oh… this is still below our minumum <code class="language-plaintext highlighter-rouge">minx</code>, so if we were just swapping the sign, we would send the particle back in the wrong direction. I actually had this bug when I first implemented ;p.</em></p> <p>Final step is to figure how to actually draw <strong>Pete</strong>. Using Javascript primitives, this is actually pretty easy. <strong>Pete</strong> is just a filled circle with a specific color, so we’ll use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc">Arc</a> primitive:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">)</span> <span class="p">{</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">size</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span><span class="o">*</span><span class="mi">2</span><span class="p">);</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">color</span><span class="p">;</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span> <span class="p">}</span></code></pre></figure> <p>Let’s break this down. First <code class="language-plaintext highlighter-rouge">renderCtx.beginPath()</code> (reference: <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath">BeginPath</a>) is used to start a new rendering path in Javascript. Rendering paths in Javascript allow you to build out a shape using multiple primitives (like lines, arcs, rectangles, etc) before doing a single render/draw call for the entire path you’ve laid out. Here, we’re only using a single primitive, so it doesn’t buy us much, but is still needed to setup our render state. <code class="language-plaintext highlighter-rouge">renderCtx.arc(this.x, this.y, this.size, 0, Math.PI*2)</code> is where we are telling Javascript to draw our filled circle. We pass in <strong>Pete’s</strong> position using <code class="language-plaintext highlighter-rouge">this.x, this.y</code>, <code class="language-plaintext highlighter-rouge">this.size</code> is the radius of the circle (in pixels) which we set in our constructor, and the <code class="language-plaintext highlighter-rouge">0, MathPI*2</code> is used to identify the start and end angles (in radians) of the arc to draw (0 to 2*PI is a full circle). <code class="language-plaintext highlighter-rouge">renderCtx.fillstyle = this.color</code> is used to set the color of the circle (reference: <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillStyle">FillStyle</a>), which can be named colors like <code class="language-plaintext highlighter-rouge">red</code> or <code class="language-plaintext highlighter-rouge">black</code>, RGB hex values like <code class="language-plaintext highlighter-rouge">#808080</code> for a gray, or a RGB color string like <code class="language-plaintext highlighter-rouge">rgb(127,127,127)</code> (reference: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value">CSS Color Value</a>). And finally, the <code class="language-plaintext highlighter-rouge">renderCtx.fill()</code> call renders the path, our circle, to the canvas (reference: <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fill">Fill</a>).</p> <h3 id="putting-it-together">Putting It Together</h3> <p>The last piece of the puzzle is the initialization code for <strong>Pete</strong>. You’ll note that in the first introduction to <strong>Pete</strong> and the canvas below, <strong>Pete</strong> starts at a random location and heads off in a random direction and a fixed speed. We also need to define what the <em>fence</em> parameters should be. Let’s start with the fence, as it’s easiest. We will set <strong>Pete’s</strong> boundaries to simply be the boundaries of the canvas we are using. So <code class="language-plaintext highlighter-rouge">spec = { minx: 0, miny: 0, maxx: canvas.width, maxy: canvas.height }</code> will do fine. Starting position also is easy. We’ll pick a random number between 0 and 1 (using <code class="language-plaintext highlighter-rouge">Math.random()</code>) and multiply by the canvas height and width, as that’s the bounds we are using for <strong>Pete</strong>. For handling movement in a random direction but at constant speed. I pick a speed, say 200 (measured in pixels per second), then pick a random angle. To get the x, y deltas, I simply use <code class="language-plaintext highlighter-rouge">Math.sin(angle)</code> and <code class="language-plaintext highlighter-rouge">Math.cos(angle)</code>. Let’s put this all in a function:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">makePete</span><span class="p">()</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">speed</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">angle</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// angle is in radians, so full circle is PI * 2</span> <span class="nx">petes</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span> <span class="k">new</span> <span class="nx">PeteParticle</span><span class="p">({</span> <span class="na">x</span><span class="p">:</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">width</span><span class="p">,</span> <span class="c1">// random starting location</span> <span class="na">y</span><span class="p">:</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">height</span><span class="p">,</span> <span class="na">dx</span><span class="p">:</span> <span class="nx">speed</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">cos</span><span class="p">(</span><span class="nx">angle</span><span class="p">),</span> <span class="c1">// starting deltas/movement based on angle</span> <span class="na">dy</span><span class="p">:</span> <span class="nx">speed</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sin</span><span class="p">(</span><span class="nx">angle</span><span class="p">),</span> <span class="na">color</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#A9BCD0</span><span class="dl">"</span><span class="p">,</span> <span class="c1">// picking a color</span> <span class="na">maxx</span><span class="p">:</span> <span class="nx">width</span><span class="p">,</span> <span class="c1">// setting fence max values (min default to 0)</span> <span class="na">maxy</span><span class="p">:</span> <span class="nx">height</span> <span class="p">})</span> <span class="p">);</span> <span class="p">}</span></code></pre></figure> <p>By putting this in a function, we can now call it multiple times to get multiple particles, all following the same rules. The only thing we haven’t accounted for is keeping track of our <strong>Pete</strong> particles. In the above function there is a <code class="language-plaintext highlighter-rouge">petes.push()</code> call. I’m using an array named petes, allocated by using <code class="language-plaintext highlighter-rouge">var petes = [];</code>. So that will need to be declared prior to calling our <code class="language-plaintext highlighter-rouge">makePete()</code> function:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">petes</span> <span class="o">=</span> <span class="p">[];</span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="nx">i</span><span class="o">&lt;</span><span class="mi">5</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="nx">makePete</span><span class="p">();</span> <span class="p">}</span></code></pre></figure> <p>This handles creating five <strong>Pete</strong> particles (yay, he now has friends!). But we need to update our <code class="language-plaintext highlighter-rouge">update()</code> and <code class="language-plaintext highlighter-rouge">render()</code> functions accordingly:</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">pete</span> <span class="k">of</span> <span class="nx">petes</span><span class="p">)</span> <span class="p">{</span> <span class="nx">pete</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">pete</span> <span class="k">of</span> <span class="nx">petes</span><span class="p">)</span> <span class="p">{</span> <span class="nx">pete</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure> <canvas id="cvs2" style="background-color: #373F51;"></canvas> <p><button id="button2" style="padding: 2px 8px;">+friend</button></p> <script> class Mgr2 { constructor() { this.canvas = document.getElementById("cvs2"); this.width = window.innerWidth * .5; this.height = window.innerHeight*.2; this.canvas.width = this.width; this.canvas.height = this.height; this.ctx = this.canvas.getContext("2d"); this.petes = []; for (let i=0; i<5; i++) { this.makePete(); } // wire poke button let btn = document.getElementById("button2"); btn.onclick = this.makePete.bind(this); } makePete() { if (this.petes.length >= 50) return; let speed = 200; let angle = Math.random() * Math.PI*2; this.petes.push(new PeteParticle({ x: Math.random() * this.width, y: Math.random() * this.height, dx: speed*Math.cos(angle), dy: speed*Math.sin(angle), color: new Color(169,188,208,1), maxx: this.width, maxy: this.height, })); } update(ctx) { for (const pete of this.petes) { pete.update(ctx); } } render() { this.ctx.clearRect(0,0,this.width,this.height); for (const pete of this.petes) { pete.render(this.ctx); } } } mgrs.push(new Mgr2()); </script> <p>So now we have <strong>Pete</strong> and a couple of friends. Use the <strong>+friend</strong> button to add more.</p> <p>I’ll stop here for now, as I ended up being long winded in explaining everything. And there’s still quite a bit of ground to cover. Hopefully I’ve outlined some basic concepts: a particle really is some data and rules for updating and rendering. Logic for initialization lies outside the particle and that we need to be able to hook to the main game loop. We still need to get to the “fire and forget” point I raised at the beginning of the article. And I still want to cover more on the creation and expiration of particles and ways to handle that. And while I’m sure <strong>Pete</strong> is a fine particle, there’s a lot more we can do and I’ll provide some more exciting examples. Look for these topics to be covered in more detail in coming posts.</p> <p>The full code for this example is below, or you can play around with it for yourself using this <a href="https://codepen.io/tyallison/pen/zYKQdVV">CodePen</a>.</p> <figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">lastUpdate</span> <span class="o">=</span> <span class="nx">performance</span><span class="p">.</span><span class="nx">now</span><span class="p">();</span> <span class="kd">const</span> <span class="nx">maxDeltaTime</span> <span class="o">=</span> <span class="mi">100</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">updateCtx</span> <span class="o">=</span> <span class="p">{</span> <span class="na">deltaTime</span><span class="p">:</span> <span class="mi">0</span> <span class="p">};</span> <span class="kd">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">"</span><span class="s2">canvas</span><span class="dl">"</span><span class="p">);</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">innerWidth</span><span class="p">;</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">innerHeight</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">width</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">height</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span><span class="p">;</span> <span class="kd">const</span> <span class="nx">renderCtx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="dl">"</span><span class="s2">2d</span><span class="dl">"</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">petes</span> <span class="o">=</span> <span class="p">[];</span> <span class="kd">function</span> <span class="nx">loop</span><span class="p">(</span><span class="nx">hts</span><span class="p">)</span> <span class="p">{</span> <span class="nx">updateCtx</span><span class="p">.</span><span class="nx">deltaTime</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">min</span><span class="p">(</span><span class="nx">maxDeltaTime</span><span class="p">,</span> <span class="nx">hts</span> <span class="o">-</span> <span class="nx">lastUpdate</span><span class="p">);</span> <span class="nx">lastUpdate</span> <span class="o">=</span> <span class="nx">hts</span><span class="p">;</span> <span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">);</span> <span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">);</span> <span class="nb">window</span><span class="p">.</span><span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">loop</span><span class="p">);</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">)</span> <span class="p">{</span> <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">pete</span> <span class="k">of</span> <span class="nx">petes</span><span class="p">)</span> <span class="p">{</span> <span class="nx">pete</span><span class="p">.</span><span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">)</span> <span class="p">{</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">clearRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">width</span><span class="p">,</span> <span class="nx">height</span><span class="p">);</span> <span class="k">for</span> <span class="p">(</span><span class="kd">const</span> <span class="nx">pete</span> <span class="k">of</span> <span class="nx">petes</span><span class="p">)</span> <span class="p">{</span> <span class="nx">pete</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="nb">window</span><span class="p">.</span><span class="nx">requestAnimationFrame</span><span class="p">(</span><span class="nx">loop</span><span class="p">);</span> <span class="kd">class</span> <span class="nx">PeteParticle</span> <span class="p">{</span> <span class="kd">constructor</span><span class="p">(</span><span class="nx">spec</span> <span class="o">=</span> <span class="p">{})</span> <span class="p">{</span> <span class="c1">// current position</span> <span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">x</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">y</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span> <span class="c1">// deltas for x,y - represents velocity in pixels per second</span> <span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">=</span> <span class="p">(</span><span class="nx">spec</span><span class="p">.</span><span class="nx">dx</span> <span class="o">||</span> <span class="mi">50</span><span class="p">)</span> <span class="o">*</span> <span class="mf">0.001</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">=</span> <span class="p">(</span><span class="nx">spec</span><span class="p">.</span><span class="nx">dy</span> <span class="o">||</span> <span class="mi">50</span><span class="p">)</span> <span class="o">*</span> <span class="mf">0.001</span><span class="p">;</span> <span class="c1">// size of particle</span> <span class="k">this</span><span class="p">.</span><span class="nx">size</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">size</span> <span class="o">||</span> <span class="mi">5</span><span class="p">;</span> <span class="c1">// color of particle</span> <span class="k">this</span><span class="p">.</span><span class="nx">color</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">color</span> <span class="o">||</span> <span class="dl">"</span><span class="s2">red</span><span class="dl">"</span><span class="p">;</span> <span class="c1">// bounds</span> <span class="k">this</span><span class="p">.</span><span class="nx">minx</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">minx</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">miny</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">miny</span> <span class="o">||</span> <span class="mi">0</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxx</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">maxx</span> <span class="o">||</span> <span class="mi">400</span><span class="p">;</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxy</span> <span class="o">=</span> <span class="nx">spec</span><span class="p">.</span><span class="nx">maxy</span> <span class="o">||</span> <span class="mi">400</span><span class="p">;</span> <span class="p">}</span> <span class="nx">update</span><span class="p">(</span><span class="nx">updateCtx</span><span class="p">)</span> <span class="p">{</span> <span class="kd">let</span> <span class="nx">dt</span> <span class="o">=</span> <span class="nx">updateCtx</span><span class="p">.</span><span class="nx">deltaTime</span><span class="p">;</span> <span class="c1">// update position</span> <span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">+=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">*</span> <span class="nx">dt</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">+=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">round</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">*</span> <span class="nx">dt</span><span class="p">);</span> <span class="c1">// update direction</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">&lt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">minx</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dx</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">&lt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">miny</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dy</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">x</span> <span class="o">&gt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxx</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">dx</span> <span class="o">=</span> <span class="o">-</span><span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dx</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">y</span> <span class="o">&gt;=</span> <span class="k">this</span><span class="p">.</span><span class="nx">maxy</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">dy</span> <span class="o">=</span> <span class="o">-</span><span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">dy</span><span class="p">);</span> <span class="p">}</span> <span class="nx">render</span><span class="p">(</span><span class="nx">renderCtx</span><span class="p">)</span> <span class="p">{</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">arc</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">size</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">2</span><span class="p">);</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">color</span><span class="p">;</span> <span class="nx">renderCtx</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span> <span class="p">}</span> <span class="p">}</span> <span class="kd">function</span> <span class="nx">makePete</span><span class="p">()</span> <span class="p">{</span> <span class="k">if</span> <span class="p">(</span><span class="nx">petes</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;=</span> <span class="mi">50</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">speed</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span> <span class="kd">let</span> <span class="nx">angle</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="mi">2</span><span class="p">;</span> <span class="nx">petes</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span> <span class="k">new</span> <span class="nx">PeteParticle</span><span class="p">({</span> <span class="na">x</span><span class="p">:</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">width</span><span class="p">,</span> <span class="na">y</span><span class="p">:</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">height</span><span class="p">,</span> <span class="na">dx</span><span class="p">:</span> <span class="nx">speed</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">cos</span><span class="p">(</span><span class="nx">angle</span><span class="p">),</span> <span class="na">dy</span><span class="p">:</span> <span class="nx">speed</span> <span class="o">*</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sin</span><span class="p">(</span><span class="nx">angle</span><span class="p">),</span> <span class="na">color</span><span class="p">:</span> <span class="dl">"</span><span class="s2">#A9BCD0</span><span class="dl">"</span><span class="p">,</span> <span class="na">maxx</span><span class="p">:</span> <span class="nx">width</span><span class="p">,</span> <span class="na">maxy</span><span class="p">:</span> <span class="nx">height</span> <span class="p">})</span> <span class="p">);</span> <span class="p">}</span> <span class="k">for</span> <span class="p">(</span><span class="kd">let</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="mi">25</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span> <span class="nx">makePete</span><span class="p">();</span> <span class="p">}</span></code></pre></figure> 2021-01-20T00:00:00+00:00 https://tylorallison.github.io/games/witches-and-guns/ Witches and Guns 2020-12-06T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ Who needs spells? <h3 id="the-game">The Game</h3> <p>A Javascript shooter with waves of enemies, bosses and multiple levels.</p> <h3 id="my-contribution">My Contribution</h3> <p>More Javascript and pixel art work and related game integration.</p> <p>Room design and tile art (yard, kitchen, living room, laundry room), level grid implementation, tile layering, subtile support, animated tile support, fridge boss animations and taunts, ice cube animation integration, teapot animation, title screen and related transitions.</p> <h3 id="play">Play</h3> <p>This game was developed by members of the HomeTeam Gamedev club, myself included. Find out more about the club, or play this game: <a href="https://hometeamgamedev.itch.io/witches-n-guns">Play on itch.io</a></p> <h3 id="media">Media</h3> <div> <ul class="clearing-thumbs small-block-grid-4" data-clearing=""> <li><a href="https://tylorallison.github.io/images/game-witches_title.png"><img data-caption="Witches and Guns Title Page" src="https://tylorallison.github.io/images/game-witches_title-thumb.png" alt="Witches and Guns Title Page" /></a></li> <li><a href="https://tylorallison.github.io/images/game-witches_fridge.gif"><img data-caption="Fridge Boss Gif" src="https://tylorallison.github.io/images/game-witches_fridge-thumb.gif" alt="Fridge Boss Gif" /></a></li> <li><a href="https://tylorallison.github.io/images/game-witches_lvl1.png"><img data-caption="Witches level 1" src="https://tylorallison.github.io/images/game-witches_lvl1-thumb.png" alt="Witches level 1" /></a></li> <li><a href="https://tylorallison.github.io/images/game-witches_lvl2.png"><img data-caption="Witches level 2" src="https://tylorallison.github.io/images/game-witches_lvl2-thumb.png" alt="Witches level 2" /></a></li> <li><a href="https://tylorallison.github.io/images/game-witches_lvl3.png"><img data-caption="Witches level 3" src="https://tylorallison.github.io/images/game-witches_lvl3-thumb.png" alt="Witches level 3" /></a></li> <li><a href="https://tylorallison.github.io/images/game-witches_lvl4.png"><img data-caption="Witches level 4" src="https://tylorallison.github.io/images/game-witches_lvl4-thumb.png" alt="Witches level 4" /></a></li> </ul> </div> 2020-12-06T00:00:00+00:00 https://tylorallison.github.io/games/hard-glitch/ Hard Glitch 2020-10-25T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ Help Glitch escape in this rogue-like cyber adventure <h3 id="the-game">The Game</h3> <p>A rogue-like adventure cyber adventure set inside your computer. Help “Glitch” survive and escape the confines of the computer world. Solve puzzles and come up with a strategy to make it out alive.</p> <h3 id="my-contribution">My Contribution</h3> <p>Continued to build out my experience with Javascript games. First time building/working with a particle system in JS. First time building out a procedural wall and tile system.</p> <p>Particle systems (glitch, trace, scan, portal, spawn, missile, color, spark, repair, wait, hex spin, lightning jump, fade, explosion swirl, blip edge pathing), procedural tile selection and wall generation, FX randomization, wall tiles art, decrypt/triangle animations, mock ups (tile bg, walls, void, experimentation with negative space/holes, perspective, color tests), color adjustments, moving wall v2, level design concepts art, floor art, NPC wait animation, warm and cool level themes, lots of asset and code cleanup, additional tile type rules, seam fix, take/drop animations, move animations, highlights art v2, laser walls, tile overlay, title screen background</p> <h3 id="play">Play</h3> <p>This game was developed by members of the HomeTeam Gamedev club, myself included. Find out more about the club, or play this game: <a href="https://klaim.itch.io/hardglitch">Play on itch.io</a></p> <h3 id="media">Media</h3> <div> <ul class="clearing-thumbs small-block-grid-4" data-clearing=""> <li><a href="https://tylorallison.github.io/images/game-hard_glitch_title.png"><img data-caption="Hard Glitch Title Page" src="https://tylorallison.github.io/images/game-hard_glitch_title-thumb.png" alt="Hard Glitch Title Page" /></a></li> <li><a href="https://tylorallison.github.io/images/game-hard_glitch_gameplay.png"><img data-caption="Hard Glitch Gameplay" src="https://tylorallison.github.io/images/game-hard_glitch_gameplay-thumb.png" alt="Hard Glitch Gameplay" /></a></li> <li><a href="https://tylorallison.github.io/images/game-hard_glitch_portal.gif"><img data-caption="Hard Glitch Portal Animation" src="https://tylorallison.github.io/images/game-hard_glitch_portal-thumb.gif" alt="Hard Glitch Portal Animation" /></a></li> <li><a href="https://tylorallison.github.io/images/game-hard_glitch_walls.png"><img data-caption="Hard Glitch Walls" src="https://tylorallison.github.io/images/game-hard_glitch_walls-thumb.png" alt="Hard Glitch Walls" /></a></li> <li><a href="https://tylorallison.github.io/images/game-hard_glitch_blipdemo.gif"><img data-caption="Hard Glitch Blip Effect" src="https://tylorallison.github.io/images/game-hard_glitch_blipdemo-thumb.gif" alt="Hard Glitch Blip Effect" /></a></li> </ul> </div> 2020-10-25T00:00:00+00:00 https://tylorallison.github.io/games/falldale-ee/ Falldale Extended Edition 2020-05-03T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ Revisiting Previous HomeTeam Javascript RPG Adventure <h3 id="the-game">The Game</h3> <p>A role-playing action adventure game taking place in and around the medieval village of Falldale. Rid the village of the goblin threat. Uncover the truth behind the missing caravan. Help the Princess bring peace to the countryside once and for all.</p> <h3 id="my-contribution">My Contribution</h3> <p>My first real taste for both working on a Javascript game and working on pixel art.</p> <p>Art (tables, bar stools, wooden walls, flagstones, bar joints, top wall updates, window, pine tree, cliffs, caves, bonfire animation, castle walls), level design (castle, east woods), Tiled integration, map data conversion, assorted bug fixes, collision system updates, new quest code features, AI zone support, orc boss AI improvements, hammer weapon, wizard area colliders, transitions, enemy placement &amp; tuning</p> <h3 id="play">Play</h3> <p>This game was developed by members of the HomeTeam Gamedev club, myself included. Find out more about the club, or play this game: <a href="https://hometeamgamedev.itch.io/falldale-ee">Play on itch.io</a></p> <h3 id="media">Media</h3> <div> <ul class="clearing-thumbs small-block-grid-4" data-clearing=""> <li><a href="https://tylorallison.github.io/images/game-falldale_title.png"><img data-caption="Falldale Title Page" src="https://tylorallison.github.io/images/game-falldale_title-thumb.png" alt="Falldale Title Page" /></a></li> <li><a href="https://tylorallison.github.io/images/game-falldale_castle.png"><img data-caption="Falldale Game Play: Castle" src="https://tylorallison.github.io/images/game-falldale_castle-thumb.png" alt="Falldale Game Play: Castle" /></a></li> <li><a href="https://tylorallison.github.io/images/game-falldale_forest.png"><img data-caption="Falldale Game Play: Forest" src="https://tylorallison.github.io/images/game-falldale_forest-thumb.png" alt="Falldale Game Play: Forest" /></a></li> <li><a href="https://tylorallison.github.io/images/sss-inside_castle.png"><img data-caption="screenshotsaturday Castle Interior" src="https://tylorallison.github.io/images/sss-inside_castle-thumb.png" alt="screenshotsaturday Castle Interior" /></a></li> <li><a href="https://tylorallison.github.io/images/sss-wip_castle.png"><img data-caption="screenshotsaturday WIP Castle" src="https://tylorallison.github.io/images/sss-wip_castle-thumb.png" alt="screenshotsaturday WIP Castle" /></a></li> <li><a href="https://tylorallison.github.io/images/sss-bonfire.gif"><img data-caption="screenshotsaturday Bonfire" src="https://tylorallison.github.io/images/sss-bonfire-thumb.gif" alt="screenshotsaturday Bonfire" /></a></li> </ul> </div> 2020-05-03T00:00:00+00:00 https://tylorallison.github.io/general/tile-level-editor/ TyTe Tile Level Editor 2020-04-02T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ My roughly weeklong project to create a tile editor used for JS game building... <h3 id="intro">Intro</h3> <p>OK. The world is a bit of a crazy place right now (thanks CoronaVirus). And like many folks, my family has been seeing a lot of each other over the past few weeks, and likely months to come. While we are truly going through some strange times, our outlook is good. Let’s use this time together to make something positive. So that’s our plan.</p> <p>One of the ideas that we have had for a bit now, but really haven’t had the time for, is to come up with a video game that the entire family can work on together. Guess what, we have the time now… so, what we’ve basically came up with is a top-down adventure-style game, where the goal is to basically help others and make your world a better place. So, my family all has different talents… my oldest daughter likes work on art, my youngest is more of a planner/layout specialist, and my wife likes to develop stories (I guess that leaves me with coding).</p> <p>As I’m trying to pull together how we can all contribute, I figured that level editing a top-down world would be one of those areas. I also figured that we would work with javascript as the development language, as it’s pretty easy to use (you have a browser, right?) and it’s something that I’ve been actively trying to learn as part of my <a href="https://hometeamgamedev.com/">HomeTeam GameDev</a> club work. Given those constraints, I started looking at free software that’s already out there that we could use. And there are quite a few options. Top of the list is <a href="https://www.mapeditor.org/">Tiled</a>. This is a full-featured, actively-maintained and looks to be very efficient level editor. It supports all kinds of features, different kinds of maps, and integrations into GameDev engines. And it’s basically free. It seems like if you take the time to learn how to use the tool, it would be a great editor. However, one of my goals for our family project is for folks to be able to jump right in and help, without too much of a learning curve, or having to figure out how to use a tool.</p> <p>So, with the idea of keeping things simple, I thought it would be a good idea to write a level editor from scratch using Unity. Ya, that sounds like a good idea…</p> <h3 id="design">Design</h3> <p>One of the direct benefits of writing your own tool is that you can build it to meet your exact needs. So lots of fancy features like supporting different map types or engine integrations aren’t needed. I’m making my game in javascript and I’m using a top-down set of tiles. This limits the required feature set:</p> <ul> <li>Easy to use</li> <li>Needs to directly edit the game files used in my javascript project (more on this below).</li> <li>Support for multiple levels/zones</li> <li>Support for multiple layers of tiles within a zone (e.g.: background, foreground, etc).</li> <li>Basic editor functions, add tiles, flood fill, erase</li> <li>Portable (can run on Mac/Windows)</li> </ul> <p>My gamefiles are laid like so:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>projectdir ├── index.html ├── ... └── src ├── img │   ├── 32grass.png │   ├── 32road.png │   ├── sprites.json ├── js │   ├── ... └── zones ├── start.json └── test.json </code></pre></div></div> <p>The important folders here are the <code class="language-plaintext highlighter-rouge">img</code> and <code class="language-plaintext highlighter-rouge">zones</code> folders. First off, the <code class="language-plaintext highlighter-rouge">img</code> folder contains all of the individual sprite files that I intend to use in my game. I know lots of games use sprite sheets instead of individual images for a number of reasons. Maybe I’ll get there at some point, but for right now, it is easier for me to manage them in separate files. Within this folder is also a <code class="language-plaintext highlighter-rouge">sprites.json</code> file which I maintain. This contains the mapping between a unique identifier for the sprite (just an integer), and the sprite file itself. The unique identifier will be used and referenced in the saved level data. For now, I just manually edit this json file when I need to add more sprites. The biggest reason for this is that besides data needed for the level editor, I intend to add data specific to the game (like collision info, terrain info, etc.). Next is the <code class="language-plaintext highlighter-rouge">zones</code> folder. This is where I intend to keep all of my level/zone definitions. A zone definition consists of information about the zone, like it’s name, it’s dimensions, and the list of layers for that zone. Each layer has a name and the actual grid of tiles that have been assigned to that layer, referenced by the unique sprite identifiers (it’s an array of integers :)).</p> <p>My design approach for the editor was driven by getting something running as soon as possible. Probably the biggest issue with this approach is that I ended up using individual game objects to represent the tiles within the grid to edit. You can do the math… let’s say you have a new zone you want to create that is 50x50 in dimensions. Let’s also say you want to have a background, a foreground, and a separate object layer. You now have 50x50x3=<code class="language-plaintext highlighter-rouge">ouch</code> game objects running in unity while you edit this zone. Now, I’ve been editing zones with multiple layers as big as 100x100 on my laptop. There is a little lag, but it works (my laptop just gets warm). So, it fits within the constraint of getting something working up and running ASAP. I do want to go back and change out the grid managemnt using a single canvas. It’s on my to-do list along with a handful (err… maybe more than that) of other things.</p> <p>The rest of the code is pretty straight-forward and I’m overall happy with how it’s working. I won’t bore you with further details. The code is there for you to view or drop me a line if you have questions/comments.</p> <h3 id="usage">Usage</h3> <p>See my README file in the github project for the level editor for how to use the tool.</p> <h3 id="references">References</h3> <ul> <li><a href="https://github.com/tylorallison/tyte">TyTe on Github</a> - The entire Unity project and all source code, with example levels/tile art.</li> <li><a href="https://github.com/tylorallison/tyjsjam">TyJsJam on Github</a> - An example JS game framework utilizing the levels/tiles managed by the editor.</li> </ul> <h3 id="thanks">Thanks</h3> <ul> <li><a href="https://hometeamgamedev.com/">HomeTeam GameDev Folks</a> - For support, inspiration, and bits and pieces of code from other JS games.</li> <li><a href="https://twitter.com/AABlackwood">Austin Blackwood @AABlackwood</a> - For the dragon tile used in the referenced projects.</li> <li><a href="https://opengameart.org/content/top-down-grass-beach-and-water-tileset"><em>Matiaan</em> from OpenGameArt.Org</a> - For the top-down tileset used in the example projects.</li> </ul> 2020-04-02T00:00:00+00:00 https://tylorallison.github.io/tutorial/unity-packages/ Utilizing Unity Packages for Reusable Code Chunks 2020-02-29T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ Learn how to use Unity packages to share code across projects. <h3 id="intro">Intro</h3> <p>So, you find a good way to solve a particular problem while working on a project, and you write some code to handle it. It gets integrated into your project and everything works. Yay! Now if there’s one thing I’ve learned about Unity, it’s that there are tons of repeated patterns of problems. So, it’s not too hard of stretch to imagine that the problem that you faced and solved in project <em>foo</em> you will also find your new project <em>bar</em>. Common practice would say to just copy the code and be done with it. And for small snippets of code, that totally makes sense. For larger problems, well, maybe not so much.</p> <p>As an example, a lot of my projects I’ve been working on lately make use of procedural meshes. So I’ve created some code to provide an easy-to-use interface for creating dynamic meshes and interface w/ Unity to translate to in-game objects. Over time, I’ve slowly added features to this code base to handle new use cases. The result is a self-contained set of functions and assets that I can use in a wide range of projects. And while I could technically copy this code from project to project, that ends up being quite a bit of overhead. I have things like unit tests, shaders and unity test scenes that I used to develop the set of mesh routines. I want to be able to treat this code as a single unit, and use it in whatever project I want, without that project worrying about the maintenance of the mesh code.</p> <p>This tutorial will cover how I go about doing just that by utilizing <a href="https://docs.unity3d.com/Manual/CustomPackages.html">Unity’s Custom Packages</a>. While Unity packages can be used for a number of things, including models, textures, and other custom assets, this demo will show you how to setup custom packages for C# code assemblies. Allowing you to easily share common code across your Unity projects.</p> <h3 id="basics">Basics</h3> <p>There’s a few assumptions that I’m going to make here:</p> <ul> <li>You’re using Unity and have C# projects</li> <li>You’re using git for version control</li> <li>You have a single release branch (this demo just uses master)</li> <li>You have code that is modular/self-contained and want to use it across multiple projects</li> </ul> <p>For the purposes of this demo, I’ll be introducing three Unity projects that I’m using as packages:</p> <ul> <li><a href="https://bitbucket.org/ptjal/tytest">tytest</a> - A simple test framework for tests in Unity.</li> <li><a href="https://bitbucket.org/ptjal/tymesh">tymesh</a> - A utility class for working with dynamic meshes</li> <li><a href="https://bitbucket.org/ptjal/tysimpleshape">tysimpleshape</a> - A utilily class that uses the tymesh package and introduces simple shape primitives.</li> </ul> <h3 id="building-a-unity-package">Building a Unity Package</h3> <p>There’s a little setup and reorganization that’s needed to setup a Unity package. While there’s likely multiple ways of managing a Unity package, my requirement for my workflow is that I need to be able to manage each package as a complete Unity project. That way, if I need to make a change to a package script, I can test make that change locally in a full blown Unity project, make sure it compiles and run any tests that I may have for the package before creating a new package release. More on this below…</p> <h4 id="project-layout">Project Layout</h4> <p>Unity has a <a href="https://docs.unity3d.com/Manual/cus-layout.html">suggested package layout convention</a> documented and I’m going to take this format and embed it within a pretty standard Unity project layout.</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;root&gt; ├── Assets │ ├── Scenes │ ├── Scripts │ └── &lt;package&gt; │ ├── package.json │ ├── README.md │ ├── CHANGELOG.md │ ├── LICENSE.md │ ├── Editor │ │ ├── Unity.[YourPackageName].Editor.asmdef │ │ └── EditorExample.cs │ └── Runtime │ ├── Unity.[YourPackageName].asmdef │ └── RuntimeExample.cs ├── Packages └── ProjectSettings </code></pre></div></div> <p>You may find that you have other folders in the package for other assets, such as textures, shaders, sound files, or whatever. Modify as you see fit. The important part is that all files that should be part of your package should all live under the <em>package</em> directory.</p> <p>I’m going to use a git trick to manage the package directory as a separate subtree branch from the main branch. I’m going to name this branch <em>upm</em> and will manage release tags off of this <em>upm</em> branch. <a href="https://www.atlassian.com/git/tutorials/git-subtree">git subtree</a> is an alternative to submodules and in this case, allows me to break off of a chunk of the repo and manage it as a separate branch. This separate branch will then also be what Unity will end up pulling from, so when it loads a package from git, all it sees is the package subdirectory, not the entire project directory. Pretty cool, but takes some getting used to.</p> <p>Once you get your project to match the layout above, proceed to the next step below.</p> <h4 id="namespaced-code">Namespaced code</h4> <p>Perhaps this isn’t a mandatory step, but it’s usually a good idea to namespace your package code. The whole point of this demo is that you have a chunk of code you want to reuse, so to me it just makes sense to have all that code belong under a namespace as a way of organizing the classes. It’s real easy to do… In all of your <code class="language-plaintext highlighter-rouge">&lt;file&gt;.cs</code> files, wrap your classes with a namespace definition, like so:</p> <div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">namespace</span> <span class="nn">TyMesh</span> <span class="p">{</span> <span class="c1">// Class definitions go here</span> <span class="p">}</span> </code></pre></div></div> <p>All code that belongs to that namespace can reference any other public classes within that namespace directly. Any code outside of the namespace definition needs to reference using the full <code class="language-plaintext highlighter-rouge">namespace.class</code> path or by utilizing the <code class="language-plaintext highlighter-rouge">using</code> keyword. Basic C# stuff here.</p> <h4 id="assembly-definitions">Assembly Definitions</h4> <p>According to Unity: “You can use Assembly Definitions to organize the scripts in your Project into assemblies. When you create an Assembly Definition Asset in a folder, Unity compiles a separate managed assembly from all the scripts in that folder. Scripts in subfolders are included unless the subfolder has its own Assembly Definition. These managed assemblies act as a single library within your Unity Project.” You can read up on the properties and benefits of assembly definitions <a href="https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html">here</a>.</p> <p>You must create assembly definitions when creating packages, as referenced in the <a href="https://docs.unity3d.com/Manual/cus-asmdef.html">Unity docs</a>. Note, that you should create separate assemblies for editor and normal code (see the assemblies referenced in the subfolders above).</p> <p>Assembly definitions can be created within Unity. In your project panel, navigate to the <em>Runtime</em> folder as identified above. Right click in panel (or choose <em>Assets</em> in top menu bar), select <em>Create</em>, select <em>Assembly Definition</em>.<br /> <img src="https://tylorallison.github.io/images/post-create_assembly_def.png" alt="Create Assembly Definition" /></p> <p>Give Unity a minute or so to bring up the inspector window for the newly created assembly definition. Under name, select the name of your project. Rename the asset file to also be your project name. If you have editor code, create a separate assembly for that, using a <em>.Editor</em> suffice to your package name.</p> <h4 id="packagejson">package.json</h4> <p>Within the directory structure above, you’ll note that the top-level package directory has a <code class="language-plaintext highlighter-rouge">package.json</code> file. This serves as <a href="https://docs.unity3d.com/Manual/upm-manifestPkg.html">Unity’s Package Manifest</a> and is a required file for Unity packages.</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"com.ptjal.tymesh"</span><span class="p">,</span><span class="w"> </span><span class="nl">"displayName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"TyMesh Dynamic Mesh Generation Package"</span><span class="p">,</span><span class="w"> </span><span class="nl">"version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"0.2.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"unity"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2019.2"</span><span class="p">,</span><span class="w"> </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tymesh - A Unity package providing methods for dynamic mesh generation"</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>Unity states that the <code class="language-plaintext highlighter-rouge">name</code> and <code class="language-plaintext highlighter-rouge">version</code> are the only required fields, but strongly suggest all of the above fields for better management and visibility of packaging. <code class="language-plaintext highlighter-rouge">displayName</code> gives user-friendly name to the package, as seen in the editor, <code class="language-plaintext highlighter-rouge">description</code> provides a brief package description that will display in the details view for the package, and <code class="language-plaintext highlighter-rouge">unity</code> identifies the lowest unity version the package is expected to be compatible with. See the manifest page referenced above for all the available fields.</p> <h4 id="readmelicensechangelog">Readme/License/ChangeLog</h4> <p>These are not mandatory files either, but provide some basic documentation and licensing for your package. Standard disclaimer, I’m not a lawyer, so I’m not going to tell you what you should or should not put for your license. Having a Readme is nice to give some context on what your package provides and/or how it should be used. A changelog is also nice to include, especially if you plan to make your package publicly available.</p> <p>I’ll be showing how to use tags to reference package versions, so it is nice to have the change log, the package.json and the git tags in sync.</p> <h4 id="packaging-the-package">Packaging the Package</h4> <p>So, assuming you’re still following along, you’ve organized your project as described, created all of the required extra package files and you’re ready for the next steps. Here’s the basics of what we’re going to do next:</p> <ul> <li>Commit all changes to main and push to master</li> <li>Use <code class="language-plaintext highlighter-rouge">git subtree</code> to create a <code class="language-plaintext highlighter-rouge">upm</code> branch, pushing master content to <code class="language-plaintext highlighter-rouge">upm</code> and uploading to origin</li> <li>Update the changelog and <code class="language-plaintext highlighter-rouge">package.json</code> with a new revision. Push those changes to master and the <code class="language-plaintext highlighter-rouge">upm</code> branch.</li> <li>Create a tag on the <code class="language-plaintext highlighter-rouge">upm</code> branch for the new release.</li> </ul> <p>Let’s start!</p> <p><strong><em>Commit</em></strong></p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># commit your local changes and push to master</span> <span class="nv">$ </span>git add ... <span class="nv">$ </span>git commit ... <span class="nv">$ </span>git push </code></pre></div></div> <p><strong><em>Git Subtree</em></strong></p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># run from base directory of repo</span> <span class="c"># use gitsubtree to split your repo and push subtree to separate branch</span> <span class="c"># Assets/&lt;package&gt; should be the name of the folder containing your package files</span> <span class="nv">$ </span>git subtree push <span class="nt">--prefix</span> Assets/&lt;package&gt; origin upm </code></pre></div></div> <p><strong><em>Create Revision</em></strong></p> <p>I’ve been using <a href="https://semver.org/spec/v2.0.0.html">Semantic Versioning</a> to label my releases. This uses a version string formatted as: <code class="language-plaintext highlighter-rouge">major</code>.<code class="language-plaintext highlighter-rouge">minor</code>.<code class="language-plaintext highlighter-rouge">patch</code> and gives guidance on when to use each. You are obviously welcome to use whatever versioning scheme works best for you. When creating a new revision, you’ll need to update the version in two spots: the <code class="language-plaintext highlighter-rouge">CHANGELOG.md</code> if you’re using it and the <code class="language-plaintext highlighter-rouge">package.json</code>.</p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># update package revision, e.g.: 0.1.0 might be my initial version for a package</span> <span class="nv">$ </span>vim CHANGELOG.md ... <span class="nv">$ </span>vim package.json ... <span class="c"># push revision changes to master and upm</span> <span class="nv">$ </span>git add ... <span class="nv">$ </span>git commit ... <span class="nv">$ </span>git push <span class="nv">$ </span>git subtree push <span class="nt">--prefix</span> Assets/&lt;package&gt; origin upm </code></pre></div></div> <p><strong><em>Tag the Release</em></strong></p> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># download and check out upm branch from origin</span> <span class="nv">$ </span>git checkout upm <span class="c"># create tag for your release</span> <span class="nv">$ </span>git tag 0.1.0 <span class="c"># push tags to origin</span> <span class="nv">$ </span>git push origin <span class="nt">--tags</span> <span class="c"># return to master branch, delete local upm branch</span> <span class="nv">$ </span>git checkout master <span class="nv">$ </span>git branch <span class="nt">-D</span> upm </code></pre></div></div> <p>I remove the local <code class="language-plaintext highlighter-rouge">upm</code> branch after tagging to try to prevent any mistakes that could be made by commiting locally to the <code class="language-plaintext highlighter-rouge">upm</code> branch. The merging strategy for <code class="language-plaintext highlighter-rouge">upm</code> has all changes from master being directly pushed and merged to <code class="language-plaintext highlighter-rouge">upm</code>. No changes should be made directly to the <code class="language-plaintext highlighter-rouge">upm</code> branch.</p> <p>Battle is half done and we should have a proper Unity package ready to be referenced.</p> <h3 id="referencing-a-unity-package">Referencing a Unity Package</h3> <p>Luckily referencing a Unity package is much easier then creating them! I’ll show you two ways. For this part of the demo, I’m going to reference my modules from my old blog page, which I’ve updated now to be Unity packages:</p> <ul> <li><a href="https://bitbucket.org/ptjal/tytest">tytest</a> - A simple test framework</li> <li><a href="https://bitbucket.org/ptjal/tymesh">tymesh</a> - A utility class for working with dynamic meshes</li> <li><a href="https://bitbucket.org/ptjal/tysimpleshape">tysimpleshape</a> - Another utility class that provide simple methods for drawing lines/dots in Unity</li> </ul> <p>In this example <code class="language-plaintext highlighter-rouge">tysimpleshape</code> utilizes methods from both <code class="language-plaintext highlighter-rouge">tytest</code> and <code class="language-plaintext highlighter-rouge">tymesh</code>, so let’s take a look at how those dependencies can be met by using Unity packages.</p> <h4 id="registry-hacking">Registry Hacking</h4> <p>Unity keeps track of all of your project’s package dependencies in the <code class="language-plaintext highlighter-rouge">manifest.json</code> located in your project’s <code class="language-plaintext highlighter-rouge">Packages</code> top-level folder. To reference a Unity package from a github repo, all you need to do is modify the <code class="language-plaintext highlighter-rouge">manifest.json</code> file, as outlined below.</p> <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w"> </span><span class="nl">"dependencies"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="nl">"com.ptjal.tymesh"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://bitbucket.org/ptjal/tymesh.git#0.1.0"</span><span class="p">,</span><span class="w"> </span><span class="nl">"com.ptjal.tytest"</span><span class="p">:</span><span class="w"> </span><span class="s2">"https://bitbucket.org/ptjal/tytest.git#0.1.1"</span><span class="p">,</span><span class="w"> </span><span class="err">...</span><span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="p">}</span><span class="w"> </span></code></pre></div></div> <p>You add a new line for each package dependency within the <code class="language-plaintext highlighter-rouge">dependencies</code> section of the file. Each line uses a key (e.g.:<code class="language-plaintext highlighter-rouge">"com.ptjal.tymesh"</code>) and a value (e.g.: <code class="language-plaintext highlighter-rouge">"https://bitbucket.org/ptjal/tymesh.git#0.1.0"</code>). The key must match the name you assigned in the <code class="language-plaintext highlighter-rouge">package.json</code> file of the package. The value is URL for the git repository. Note that in this example, I’m referencing a specific version by appending the <code class="language-plaintext highlighter-rouge">#0.1.0</code> syntax to the repo link.</p> <blockquote> <p>Note: mind proper JSON formatting. I tend to make my additions to the top of the file, but if you add to the end, make sure don’t leave a dangling comma.</p> </blockquote> <h4 id="unitys-package-manager">Unity’s Package Manager</h4> <p>I’m not 100% sure when this feature was added, but in Unity 2019.3, you can add references to git repos directly from Unity’s Package Manager interface. From the top menu bar, select <em>Windows</em> and <em>Package Manager</em>. This will bring up the <em>Package Manager</em> window.</p> <p><img src="https://tylorallison.github.io/images/post-package_manager.png" alt="PackageManager" /></p> <p>In the upper left hand corner, click on the <code class="language-plaintext highlighter-rouge">+</code> button to add a new package. Select <code class="language-plaintext highlighter-rouge">Add package from git URL ...</code>, then enter the full URL and optional version info.</p> <p><img src="https://tylorallison.github.io/images/post-package_manager_add_url.png" alt="PackageManagerAddURL" /></p> <p>Both of these methods should cause Unity to download the package from the specified URL and integrate it into your project.</p> <h3 id="private-repos">Private Repos</h3> <p>Well, the official word from Unity is that private repos that require authentication are not yet supported by Unity’s package manager. Reading on <a href="https://forum.unity.com/threads/setup-for-scoped-registries-private-registries.573934/">Unity’s forum threads</a> it appears that this feature is high on their priority list and may come out in Unity 2020.1. I’ll keep my fingers crossed.</p> <p>You may be able to work around this limitation by setting your local git client global config w/ credentials for you private repo. I was able to pull from a private repo doing this, but your mileage may vary.</p> <h3 id="nested-dependencies">Nested Dependencies</h3> <p>In general, it’s a good idea to have your packages all be nice, neat, self-contained packages without any dependencies on any other packages. And while this may be a goal to strive for, the reality is that you may end up with some dependencies that need to be accounted for in a package.</p> <h4 id="fixing-assembly-definitions">Fixing Assembly Definitions</h4> <p>If your package you’re building has code dependencies on other packages, as soon as you add an assembly definition for your new package, you will get compiler errors within Unity. This is because the assembly definition expects everything within the definition to be self-contained. However, you can add external dependencies to your newly created assembly definition.</p> <p>Within Unity’s Project Window, find your assembly definition file and click on it to bring up the inspector. Find the <em>Assembly Definition References</em> panel:</p> <p><img src="https://tylorallison.github.io/images/post-assembly_def_ref.png" alt="Assembly Definition References" /></p> <p>Click the <code class="language-plaintext highlighter-rouge">+</code> at the bottom to add a new reference, then click the little bullseye to select your assembly definition from the available assemblies in your project.</p> <p><img src="https://tylorallison.github.io/images/post-select_assembly_def_ref.png" alt="Select Assembly Definition References" /></p> <p>Do this for all referenced packages.</p> <h4 id="manually-managing-dependencies">Manually Managing Dependencies</h4> <p>Unity’s <code class="language-plaintext highlighter-rouge">package.json</code> definition file does have a dependency key that can be used for official unity packages. Unfortunately, it doesn’t seem to work for custom git references. So, this means that if your new custom package is dependent on other custom packages, you will need to manage these dependencies manually. All this really means is that when you go to pull the new package into another project, you will need to manually add any other dependent packages in as well. Not that big of a deal, but something to be aware of.</p> <h3 id="maintaining-unity-packages">Maintaining Unity Packages</h3> <p>Updating a package follows the same procedure as setting up the initial <code class="language-plaintext highlighter-rouge">upm</code> branch and tagging.</p> <ul> <li>Make your local changes to the project. Including changes to changelog and <code class="language-plaintext highlighter-rouge">package.json</code> Commit.</li> <li>Use <code class="language-plaintext highlighter-rouge">git subtree</code> to push changes to your <code class="language-plaintext highlighter-rouge">upm</code> branch.</li> <li>Create a tag on the <code class="language-plaintext highlighter-rouge">upm</code> branch for the new release.</li> </ul> <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># commit changes to master (including any changes to package.json)</span> <span class="nv">$ </span>git commit ... <span class="c"># push changes to upm</span> <span class="nv">$ </span>git subtree push <span class="nt">--prefix</span> Assets/&lt;package&gt; origin upm <span class="c"># checkout local upm</span> <span class="nv">$ </span>git checkout upm <span class="c"># create tag for your release</span> <span class="nv">$ </span>git tag 0.1.1 <span class="c"># push tags to origin</span> <span class="nv">$ </span>git push origin <span class="nt">--tags</span> <span class="c"># return to master branch, delete local upm branch</span> <span class="nv">$ </span>git checkout master <span class="nv">$ </span>git branch <span class="nt">-D</span> upm </code></pre></div></div> <h3 id="try-it-out">Try It Out</h3> <p>The referenced repositories are public. Feel free to try it out and/or poke around. Not much to look at, but here’s my little simple shape test:</p> <p><img src="https://tylorallison.github.io/images/post-simple_shapes.png" alt="Test Shapes" /></p> <ul> <li><a href="https://bitbucket.org/ptjal/tytest">tytest</a> - A simple test framework</li> <li><a href="https://bitbucket.org/ptjal/tymesh">tymesh</a> - A utility class for working with dynamic meshes</li> <li><a href="https://bitbucket.org/ptjal/tysimpleshape">tysimpleshape</a> - Another utility class that provide simple methods for drawing lines/dots in Unity</li> </ul> <h3 id="credits">Credits</h3> <p>Credits go to <a href="https://twitter.com/@neogeek">Scott Doxey</a> for documenting how to use subtrees and branches combined with Unity packages. You can reference his <a href="https://neogeek.dev/creating-custom-packages-for-unity-2018.3/">writeup here</a>.</p> 2020-02-29T00:00:00+00:00 https://tylorallison.github.io/general/hello-world/ Hello World 2020-01-01T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ My introduction <h3 id="my-story">My Story</h3> <p>As with any good introduction into new territory, this story starts here at <strong>hello world</strong>. And I’ll build from there. Just want to provide a quick introduction for myself and to provide a little context to what I’m trying to do with this blog.</p> <p>First, I am an aspiring game designer and developer. This has been a lifelong dream, but I have really only started working towards it in the past year or so. I am a software developer by trade and my real job is currently working in network and application security, focusing on secure software development and architecture along with cryptography and VPN technologies.</p> <p>I am also a husband and father and part of a very active household. This put together with a <em>real job</em> means that I get limited time to work on gamedev. So my progress on projects and with this blog will not be at a rapid pace. But I continue to do what I can.</p> <h3 id="my-purpose">My Purpose</h3> <p>So, yes, this will be yet another game developer blog. There’s tons of them out there, so what am I planning on doing that will make this any different? Honestly, nothing. My intent isn’t to be flashy or new, but to simply document some of my experiences as a newcomer to game development. I plan to share some of the struggles I’ve had, including how I’ve overcome them. Tips, tricks, ideas and even failures are all fair game.</p> <p>Throughout my learning experience, I’ve googled this and borrowed that. I’ve leaned heavily on those that have come before me and my sincere hope is that someone with similar interests and experience may stumble upon one of my articles and it may help them.</p> <p>Some of the topics I plan on covering include:</p> <ul> <li>Voxel engine and isometric surface extraction</li> <li>Procedural generation</li> <li>Constructed solid geometry</li> <li>AI</li> <li>Unity Tips and Tricks</li> </ul> <h3 id="sneak-peek">Sneak Peek</h3> <p>The wonky little globe at the top of the article was generated from some procedural generation code I’ve been working on. Hopefully will have more details to come…</p> 2020-01-01T00:00:00+00:00 https://tylorallison.github.io/games/delv/ Delv 2019-04-28T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ Pixel-art based action adventure with co-op AI <h3 id="the-game">The Game</h3> <p>An action adventure game.</p> <h3 id="my-contribution">My Contribution</h3> <p>Main menu (scroll art, opening animation, buttons, font selection, stone background, and related implementation), volume sliders, pause menu improvements, and particle options.</p> <h3 id="play">Play</h3> <p>This game was developed by members of the HomeTeam Gamedev club, myself included. Find out more about the club, or play this game: <a href="https://itch.io/queue/c/188585/hometeam-gamedev-formerly-gamkedo-club?game_id=413150">Play on itch.io</a></p> <h3 id="media">Media</h3> <div> <ul class="clearing-thumbs small-block-grid-4" data-clearing=""> <li><a href="https://tylorallison.github.io/images/game-delv_title.png"><img data-caption="Delv Title Page" src="https://tylorallison.github.io/images/game-delv_title-thumb.png" alt="Delv Title Page" /></a></li> <li><a href="https://tylorallison.github.io/images/game-delv_scroll_animation.gif"><img data-caption="Delv Scroll Animation" src="https://tylorallison.github.io/images/game-delv_scroll_animation-thumb.gif" alt="Delv Scroll Animation" /></a></li> <li><a href="https://tylorallison.github.io/images/game-delv_gameplay_1.png"><img data-caption="Delv Game Play: Town" src="https://tylorallison.github.io/images/game-delv_gameplay_1-thumb.png" alt="Delv Game Play: Town" /></a></li> <li><a href="https://tylorallison.github.io/images/game-delv_gameplay_2.png"><img data-caption="Delv Game Play: Dungeon" src="https://tylorallison.github.io/images/game-delv_gameplay_2-thumb.png" alt="Delv Game Play: Dungeon" /></a></li> <li><a href="https://tylorallison.github.io/images/game-delv_gameplay_3.png"><img data-caption="Delv Game Play: Paused" src="https://tylorallison.github.io/images/game-delv_gameplay_3-thumb.png" alt="Delv Game Play: Paused" /></a></li> </ul> </div> 2019-04-28T00:00:00+00:00 https://tylorallison.github.io/games/team-scrap-metal/ Scrap Metal Elites 2018-05-28T00:00:00+00:00 Tylor Allison https://tylorallison.github.io/ RC robot arena destruction! <h3 id="the-game">The Game</h3> <p>A browser game made in HTML5. RC robot arena destruction!</p> <p>Battle of the bots with physics-based destruction, breakable bots and lots of action. Become a contender in the challenging title matches to become the new Scrap Metal Elites champion or jump right into the action with the Death Match mode.</p> <p>Game made in Unity.</p> <h3 id="my-contribution">My Contribution</h3> <p>Co-lead and project manager. Physics design/implementation for ram/spinner/hammer weapons, breakable parts, track and wheel steering, damage, and health. Blender modeling for modular bot cubes and bot panels. Arena camera controller and sound system/manager implementation.<br /> Main UI design for main menu, bot selection, title round selection, in-game HUD including health-bars. Enemy AI implementation.</p> <h3 id="play">Play</h3> <p>This game was developed by members of the HomeTeam Gamedev club, myself included. Find out more about the club, or play this game: <a href="https://gamkedo.itch.io/scrap-metal-elites">Play on itch.io</a></p> <p>Find out more about the club here: <a href="https://gamkedo.com">Gamkedo Club</a>.</p> <h3 id="media">Media</h3> <div> <ul class="clearing-thumbs small-block-grid-4" data-clearing=""> <li><a href="https://tylorallison.github.io/images/game-scrap_metal_title.png"><img data-caption="Scrap Metal Title Page" src="https://tylorallison.github.io/images/game-scrap_metal_title-thumb.png" alt="Scrap Metal Title Page" /></a></li> <li><a href="https://tylorallison.github.io/images/game-scrap_metal_play.gif"><img data-caption="Scrap Metal Game Play" src="https://tylorallison.github.io/images/game-scrap_metal_play-thumb.gif" alt="Scrap Metal Game Play" /></a></li> <li><a href="https://tylorallison.github.io/images/game-scrapmetal_gameplay_1.png"><img data-caption="Scrap Metal Game Play" src="https://tylorallison.github.io/images/game-scrapmetal_gameplay_1-thumb.png" alt="Scrap Metal Game Play" /></a></li> <li><a href="https://tylorallison.github.io/images/game-scrapmetal_deathmatch.png"><img data-caption="Scrap Metal Deathmatch Selection" src="https://tylorallison.github.io/images/game-scrapmetal_deathmatch-thumb.png" alt="Scrap Metal Deathmatch Selection" /></a></li> <li><a href="https://tylorallison.github.io/images/game-scrapmetal_title_board.png"><img data-caption="Scrap Metal Title Board" src="https://tylorallison.github.io/images/game-scrapmetal_title_board-thumb.png" alt="Scrap Metal Title Board" /></a></li> </ul> </div> 2018-05-28T00:00:00+00:00