<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Blog Tom Rijnbeek]]></title><description><![CDATA[A blog discussing topics about game design and development; not afraid to dig a little deeper.]]></description><link>https://tomrijnbeek.me/blog/</link><image><url>https://tomrijnbeek.me/blog/favicon.png</url><title>Blog Tom Rijnbeek</title><link>https://tomrijnbeek.me/blog/</link></image><generator>Ghost 5.13</generator><lastBuildDate>Tue, 14 Apr 2026 09:49:41 GMT</lastBuildDate><atom:link href="https://tomrijnbeek.me/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Master servers for network games]]></title><description><![CDATA[To make it easy for players to connect to each other in online games, master servers are often used to negotiate those connections. This post explains how to write your own.]]></description><link>https://tomrijnbeek.me/blog/master-servers-for-network-games/</link><guid isPermaLink="false">631898ead25b520c39b35b9c</guid><category><![CDATA[networking]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 24 Jun 2020 18:25:13 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>There are plenty of complexities involved with writing a game that has online multiplayer. Even if you solve all the challenges to keep the game state in sync between all your players, there is one problem left: how do the players connect in the first place?</p>
<p>Usually we begin by using direct IPs. Many older games actually worked this way, as it is something that doesn&apos;t need somebody to negotiate the connection. Using IPs doesn&apos;t really scale well, though. It only works if you already have a friend to play with, they are technical enough to find their IP, make sure their router forwards to their local machine, and disable the firewall on that specific port. This is hardly release-worthy in the era where in most games, connecting is as simple as right-clicking a name in your friends list to join them.</p>
<p>We can avoid all this complexity by only ever having a single endpoint we need to talk to. If we could have a server that manages everything for us, we&apos;d be good.</p>
<p>There are two models here: dedicated servers, and master servers. On a dedicated server, the game simulation actually runs on a server we control, and just receives the inputs from and sends the results to the players. This is a model often used by games where cheating is important to prevent, or where there is no clear lobby owner.</p>
<p>Master servers only negotiate connections between clients. Once the connection is made, they back out and let the clients figure it out themselves. The actual game simulation usually ends up being done using a host-client model (where the host simulates everything), or a peer-to-peer model (where each client simulates their own things). Today, we are only going to look at how a master server works.</p>
<h2 id="masterservers">Master servers</h2>
<p>Let me try to explain master servers using a metaphor. Connections are like a piece of rope connected to two metal cans through which you can talk to each other. In a direct connection, you try to throw over the can to your buddy to talk directly, but you have to know where they are, and the firewall has to be out of the way. In the master server model, you throw your can to the master server instead, which often has an obvious position (e.g. a web address or an IP coded into the game logic) and a firewall open to these connections.</p>
<p>Once the connection with the master server is initiated, a host can tell the master server that is has a lobby. The master server will then keep track of that locally. If clients ask for open lobbies, the master server can respond. When the client decides to join a certain lobby, the interesting part happens: since the two sides of the connection have initiated a connection already, there&apos;s an exact address on each side, and a hole through the firewall to let messages through. The master server sends one can on each end of the rope to each of the peers through the existing connection, and a direct connection is established. From then onwards, the peers communicate directly, and the master server can drop the connections itself. This makes master servers especially relevant for games that can&apos;t afford to maintain dedicated servers, since the computational power needed is very limited.</p>
<p>This process is often called the <em>NAT punchthrough</em> or a <em>hole punch</em>.</p>
<h2 id="theprotocol">The protocol</h2>
<p>The first step in implementing a master server is to decide on a protocol with which your clients will talk to the master server. You could use text, JSON files, or some binary format you come up with. For <a href="https://github.com/beardgame/td">Bearded.TD</a>, a tower defence game I am currently working on, I decided to go with Google&apos;s <a href="https://developers.google.com/protocol-buffers/">protocol buffers</a>, which are a protocol for sending serialized messages, with converters for many languages out there, (<a href="https://developers.google.com/protocol-buffers/docs/csharptutorial">including C#</a>). This would give me the freedom to work with other languages in the future if needed.</p>
<h2 id="theimplementation">The implementation</h2>
<p>For the implementation, we will be using Lidgren.Network. If you need an introduction to how it works, I recommend <a href="https://www.genericgamedev.com/tutorials/lidgren-network-an-introduction-to-networking-in-csharp-games/">this blogpost</a> on GameDev<t>.</t></p>
<p>The first step is to initialize the <code>NetPeer</code> so we are available for connections.</p>
<pre><code class="language-csharp">public void Run() {
    var config = new NetPeerConfiguration(options.ApplicationName) {
        Port = options.Port
    };
    config.EnableMessageType(NetIncomingMessageType.UnconnectedData);
    peer = new NetPeer(config);
    peer.Start();

    // Do stuff here
}
</code></pre>
<p>Note that we enable <code>UnconnectedData</code> as a message type. This allows us to receive payloads without having to set up a permanent connection.</p>
<p>We could set the master server up to respond only when it receives a message using an event-based system, but I haven&apos;t been able to get it set up so that we receive an event from Lidgren whenever a network message comes in. Instead, we will just use a small loop. The added advantage this gives us, is that it allows us to regularly check if the registered lobbies are still active.</p>
<pre><code class="language-csharp">public void Run() {
    // Set up code here

    while (true) {
        while (peer.ReadMessage(out var msg)) {
            handleIncomingMessage(msg);
        }

        updateLobbies();

        Thread.Sleep(100);
    }
}
</code></pre>
<p>Now the only thing left to do is to handle incoming messages, and update the lobbies. Let&apos;s start with the incoming messages: we are going to need some boilerplate to deal with all the different message types Lidgren sends, and the request types we set up ourselves in the protocol file.</p>
<pre><code class="language-csharp">private void handleIncomingMessage(NetIncomingMessage msg) {
    switch (msg.MessageType) {
        case NetIncomingMessageType.UnconnectedData:
            var request = Proto.MasterServerMessage.Parser.ParseFrom(
                msg.ReadBytes(msg.LengthBytes));
            handleIncomingRequest(request, msg.SenderEndPoint);
            break;

        // Other message types
    }
}
</code></pre>
<p>Based on the type of the message that comes in, we may need to do different things. For example, we want to add the lobbies to a central database of lobbies (I use a dictionary for that), and we will also need a way to return a list of all current lobbies.</p>
<p>Let&apos;s take a look at a <code>registerLobby</code> method:</p>
<pre><code class="language-csharp">private void registerLobby(
        Proto.RegisterLobbyRequest request, IPEndPoint endpoint) {
    var lobby = new Lobby(
        request.Lobby,
        new IPEndPoint(
            new IPAddress(
                request.Address.ToByteArray()), request.Port), // internal
        endpoint // external
    );

    lobbiesById.Add(request.Lobby.Id, lobby);
}
</code></pre>
<p>You can see we keep track of the lobbies in a dictionary by their ID. This allows us to keep track of which lobby is which. It is important that active lobbies keep pinging the master server, since that allows us to get rid of lobbies that weren&apos;t active for some time.</p>
<p>The interesting part here is the IP endpoints we keep track of. This is needed for the hole punching later. The endpoint Lidgren gives us for this message is the external IP. We will also need the IP endpoint as the client sees it (the internal IP), so we actually have the clients send that information as part of the request. This is how the client code will send the necessary information to the master server:</p>
<pre><code class="language-csharp">var request = new Proto.RegisterLobbyRequest {
    Lobby = lobbyInfo,
    Address = ByteString.CopyFrom(
        NetUtility.GetMyAddress(out _).GetAddressBytes()),
    Port = 24680 /* can be any free port */
};
</code></pre>
<p>It is worth mentioning that we never actually set up a persistent connection with the master server. All data is sent without that, which is why we needed to support the <code>UnconnectedData</code> message type earlier.</p>
<pre><code class="language-csharp">peer.SendUnconnectedMessage(msg, masterServerEndPoint);
</code></pre>
<p>For sending the lobbies to the clients from the master server, the setup is very similar. The client sends a request to the master server that it wants to receive the lobbies, and the master server starts writing them to the endpoint that requested it.</p>
<p>The interesting part comes when we want to actually initiate a connection, i.e. do the hole punch. To initiate a connection, the client that wants to connect to a lobby sends its own endpoints, but it also sends a connection token. It doesn&apos;t matter much what the token is, so you could hardcode it.</p>
<p>Now that we have both endpoints, we can do the actual NAT punchthrough in the master server. Luckily, Lidgren.Network actually has built-in NAT punchthrough capabilities. It can be done using the <code>Introduce</code> method in the master server.</p>
<pre><code class="language-csharp">private void introduceToLobby(Proto.IntroduceToLobbyRequest request, IPEndPoint endpoint)
{
    if (lobbiesById.TryGetValue(request.LobbyId, out var lobby))
    {
        var clientInternal = new IPEndPoint(
            new IPAddress(request.Address.ToByteArray()), request.Port);
        peer.Introduce(
            lobby.InternalEndPoint,
            lobby.ExternalEndPoint,
            clientInternal,
            endpoint,
            request.Token);
    }
    else
    {
        // Deal with lobby not found
    }
}
</code></pre>
<p>When this is successful, the client will get a network message of type <code>NatIntroductionSuccess</code> (so make sure you enable this message type on the <code>NetPeer</code>!). When receiving that message, the client can initiate a normal <code>Connect</code>, for which it should use the endpoint from the message it just got, since that allows it to use the hole the introduction made.</p>
<h2 id="conclusion">Conclusion</h2>
<p>This was a really quick run-through on how to build your own master server. To keep this post to a reasonable length, I had to cut out a lot of details. If anything is still unclear, I recommend you take a look at the full implementation of the Bearded.TD master server. You can find the logic that lives in the actual game <a href="https://github.com/beardgame/td/tree/develop/src/Bearded.TD/Networking/MasterServer">here</a>, and the master server itself <a href="https://github.com/beardgame/td/tree/develop/src/Bearded.TD.MasterServer">here</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[You don't write enough tests]]></title><description><![CDATA[How certain can we be that code is correct if we don't write tests that assume the worst of ourselves?]]></description><link>https://tomrijnbeek.me/blog/you-dont-write-enough-tests/</link><guid isPermaLink="false">631898ead25b520c39b35b99</guid><category><![CDATA[code quality]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 10 Jun 2020 18:42:02 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h2 id="achallenge">A challenge</h2>
<p>Before I get to the meat of this post, I have a challenge for you. I highly recommend you trying it before reading the rest of this post. The challenge is meant for a pair, but you can do it by yourself by assuming both roles - it will not be as powerful a lesson.</p>
<p>Pick a simple problem. Something like the <a href="https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life">Conway&apos;s Game of Life</a> works well, but any problem of similar complexity will do. In your pair, pick one person who will write the tests; the other person will write the implementation. The process is as follows:</p>
<ul>
<li>The tester writes a small test that makes the code go red.</li>
<li>The implementer writes the <em>easiest possible</em> code to make the code go green again.</li>
</ul>
<p>Note that the easiest path to green code isn&apos;t necessarily the correct implementation. Let me give an example of a hypothetical fixed size list class:</p>
<pre><code class="language-csharp">[Fact]
public void Count_ReturnsCorrectSize() {
    var list = new FixedSizeList(10); // make a list with size 10

    Assert.Equal(list.Count, 10);
}
</code></pre>
<p>A very straightforward test. There is also a very straightforward implementation:</p>
<pre><code class="language-csharp">class FixedSizeList {
    public int Count = 10;

    public FixedSizeList(int _) {}
}
</code></pre>
<p>Is this bad code? Yeah. Does it pass the tests? Yeah, and that&apos;s really what matters in this exercise.</p>
<p>Now it is up to the tester to write tests in such a way that the easiest possible way to make the code green is to actually implement the correct solution. Repeat until you&apos;ve had enough, or until the correct implementation exists. For an added challenge, the tester could choose not to look at the implementation at all, and rely on the tests only to create a correct solution.</p>
<p>Once you&apos;re ready to see what you can learn from this challenge, read on!</p>
<h2 id="thelazyevilprogrammer">The lazy evil programmer</h2>
<p>The exercise above is one of the exercises I do with the candidates of <a href="https://www.coderetreat.org/">code retreats</a> that I host, and I tend to call the session &quot;lazy evil programmer&quot;. Almost without failure, people have a lot of fun with this exercise. For myself, doing this the first time changed my perception on tests forever.</p>
<p>At the end of the session, look at the ratio of test code to production code. In the most likely case, you test code will be anywhere from three to five times as big as the production code, if not more than that. This is often much more than people write in usual setups. Why is that?</p>
<p>Under normal circumstances, we believe we&apos;ll do a pretty good job at implementing the correct solution. Going back to the example above: if we test that our <code>FixedSizeList</code> works for size 10, we can reasonably assume it works for other numbers as well, right? Sure, we&apos;ll maybe cover <code>0</code>, a negative number, and if we&apos;re feeling particularly scrutinizing, <code>int.MaxValue</code> as well, but after that we move on. Can we really assure our test works with all inputs though?</p>
<p>In the exercise outlined above, the implementing programmer is actually an antagonist, and we can&apos;t trust them to do the right thing. If I had written the code myself, in an attempt to make the correct implementation, it would be pretty easy to take the tests as enough evidence of the correctness of my code. This is where the big assumption comes in: I trust my own capabilities in writing the correct code.</p>
<p>Most of the time, programmers write the correct code. However, to err is human, and mistakes will slip in from time to time. Many people agree that if we cannot prove the code is not correct through testing, it may as well not exist (something that the language <a href="https://github.com/munificent/vigil">Vigil</a> tries to solve). Tests often only sample some parts of our code. <a href="http://www.commitstrip.com/en/2017/02/08/where-are-the-tests/">This comic</a> summarises it pretty well. So why are we satisfied with only some tests, and covering the rest by <s>magic</s> trust in ourselves, often misplaced? As programmers, we are not actively trying to be lazy or evil, but I still believe tests should be written with that assumption. Unintentially, we may be making mistakes or shortcuts, and tests catch those.</p>
<h2 id="whatteststowrite">What tests to write</h2>
<p>This section discusses some strategies for testing, so if you want to try the exercise at the start of this blog post for yourself, this is your final warning to avoid some spoilers.</p>
<p>We can&apos;t write a unit test for each possible input. If you tried the challenge above, you may have found that simple unit tests tend to not quite cut it after some time. Especially when the methods become more complex, trying to find a set of covering cases is extremely difficult. This is where we have to turn towards other testing methods.</p>
<p>Unit tests tend to have a fairly standard format: arrange input, act on the test subject, assert that the actual output matches the expected output. To make sure that we don&apos;t build code that works only for some inputs (namely the tested ones), but all possible inputs, we can randomly sample all the possible inputs by generating random inputs. The challenge many people run into here is that if you start making the input randomised, the test also doesn&apos;t know what the expected outcome is. A common anti-pattern here is to implement the entire solution again in the tests and compare the outputs, but who&apos;s to say the test implementation doesn&apos;t contain the same bug as the actual implementation (especially since they are usually written by the same person)? What works better in these cases is to verify that certain properties on the output hold.</p>
<p>As an example, let&apos;s assume our hypothetical <code>FixedSizeList</code> can be sorted. If we generate a random set of numbers for in the list, we wouldn&apos;t want to compare the output to an exact other list, since we don&apos;t have a way to generate that (putting aside our trust in <code>Array.Sort</code>). However, a sorted list has one property that is very easy to check: each number is larger than or equal than the previous.</p>
<pre><code class="language-csharp">[Fact]
public void Sorted_ReturnsSortedList()
{
    var size = random.NextInt(0, int.MaxValue);
    var list = new FixedSizeList(size);
    for (var i = 0; i &lt; size; i++) {
        list.Add(random.NextInt());
    }

    var sortedList = list.Sorted();

    for (int i = 0; i &lt; size - 1; i++) {
        Assert.True(sortedList[i] &lt;= sortedList[i + 1]);
    }
}
</code></pre>
<p>The test is relatively simple to write, and all of a sudden it is really unlikely that the <code>Sorted</code> function is implemented wrongly.</p>
<p>A second way to test beyond unit tests is to focus on system tests. In Conway&apos;s Game of Life for example, there is a repeating pattern called a <em>glider</em>. This is a set of alive cells that after four iterations repeats itself, but is moved one tile to the right and one tile to the bottom. This is a very fragile pattern, and any bug in your Game of Life implementation will most likely break this pattern. Yet the outcome is incredibly predictable, so it&apos;d be easy to write a test that takes the glider as input, does 400 iterations, and matches the expected output (which is the same glider, just moved 100 tiles). It is likely to cover all your code branches.</p>
<p>Finally, the challenge posed above itself can provide a good solution to the problem as well: don&apos;t write your own tests. If you forget a boundary condition in your code, you are likely to also forget to test for that boundary condition. Because you know what you are testing for, you know exactly what code you do and don&apos;t have to write, or vice versa if you are writing the implementation before tests. By having a different person write the tests, you lose all preconceptions about what is and isn&apos;t correct about the code, and have a larger chance of catching actual bugs.</p>
<h2 id="whataboutcoveragetools">What about coverage tools?</h2>
<p>One final note before we move on to the conclusion. One tool that has been created to give us programmers more trust in tests, is test coverage. Test coverage is represented as the percentage of lines of code that are executed as part of your tests. While this is an incredibly powerful - and generally under-used - tool, like any tool, we need to be careful about how we use it too. A test running a certain line of code doesn&apos;t mean that the correctness of that line is tested as well. This is the danger of coverage tools: if we blindly trust them, we may stop thinking critically, and we&apos;re back at being lazy and evil.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Once we stop being able to trust ourselves to write the right code, we realise that we actually need to write many more tests than we usually do to guarantee the correctness of our implementation. This shows that a large part of the evidence of our code&apos;s correctness is missing. The only way to truly be certain our code works is to assume to worst, and put maximum effort into making sure that every single bit of code is covered by tests. Randomized testing using property tests, system testing, and having a different person write your tests are among some of the solutions that can help catch more bugs in your code. In the end, code that isn&apos;t tested can&apos;t be trusted, whether it be written by a lazy evil programmer, or just a lazy one.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Player feedback and creative processes]]></title><description><![CDATA[Game developers rely on player feedback to make good products, but how far can we go before our creative vision comes in peril?]]></description><link>https://tomrijnbeek.me/blog/player-feedback-and-creative-processes/</link><guid isPermaLink="false">631898ead25b520c39b35b9b</guid><category><![CDATA[game design]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 20 May 2020 15:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>User feedback is an important part of any software development. Software is only successful if it is used, and people will only use software if it does what the user wants. Very few (successful) tech companies will have built their product without talking to their users first. Especially with the internet connecting everybody online, it&apos;s never been easier to get in contact with your users, whether that is through feedback forms or reading rants on Reddit. With all these extra avenues for users to let themselves be heard, there has also grown an expectation that developers listen to that feedback and act on it. Is that really always the right thing to do for a developer though?</p>
<p>Software exists to fill a user&apos;s need. It takes a certain skillset and mindset - both of which can be learned and practised - to understand those needs, and build a solution that addresses them. What I have often seen in online examples of &quot;user feedback&quot; is users sitting on the expert&apos;s chair and explaining their problem in the form of a solution. Should we, as developers, adopt those solutions? Not blindly. As a metaphor: a person may complain about having a headache, but the headache is merely a symptom for something else. The same holds for user feedback: if a user is annoyed with some part of your program, that may be because there is a larger underlying problem with it. A good designer knows how to look for these root causes, or at least how to ask the right questions to get closer. This is why user feedback is often gathered from directed surveys or interviews, rather than sourced from social media, as this allows more of a deep-dive into the root causes of users&apos; frustrations.</p>
<p>I wanted to specifically talk about games. On the surface, games are just another piece of software: they are written with code, and they have a purpose (generally to entertain). However, in many ways games are the antithesis to software. Game development, and game design in particular, is an inherently creative process. You can&apos;t really quantify fun, if fun is even the primary purpose of a game. Games are a form of expression, not too dissimilar from any other form of art.</p>
<p>Games still rely on feedback for success. In almost any case, we want our games to be played by as many people as possible, and so we want to build something that appeals to our audience. Throughout the entire development process, one question will always come up: how much of our own creative vision are we willing to sacrifice to address player feedback? Often compromises have to be found, or some freedom is given to players to let them enjoy the game how they want, while leaving open the option to play the game as the developer intended it.</p>
<p>When done well, I consider player feedback a privilege. People caring enough about your game to have an opinion, to want to help to improve it, is a reward in its own. When done poorly though, player feedback can be a deprevation of the creative process. In particular when player feedback is forced upon the designer from a publisher or somebody in a similar role.</p>
<p>I want to take a recent example that actually inspired this post: the introduction of so-called Player Agency Groups for the game RuneScape. A representative out of the player community was put on the payroll to work with the developer to address some of the game&apos;s pain points. While working together with players on a game can be very rewarding, I cannot help but imagine how I would feel if I were paired up with a player and told to work with them to make the game better. This would be a player without design experience, which comes back to my earlier point that finding actual problems often comes with experience. Not only would I have to deal with that, but there is also the point that making games is a creative process: there is often more than one solution, and sometimes what a player sees as a problem, is actually an important part of the game. A common knowledge in game development is that players don&apos;t know what they really want. If you asked them if they wanted to get gold or experience or what have you ten times as fast, they&apos;d probably say yes. However, these values have to be carefully chosen and adjusted to find the right balance of engagement. Players will often also want <em>their</em> particular playstyle be advantageous over others. Designing a good game is finding the balance between all these problems, which is often more of an art than a science.</p>
<p>The best example of player feedback that I have experienced is discussing the difficulty of Roche Fusion. From the get-go, it was meant to be a hard game. While more accessible than an average bullet hell, we definitely wanted to stay true to its legacy that the games are challenging at least. A lot of players struggled with that, and asked for the game to be made easier. We added an easy difficulty mode in the end. To this day, people believe that the normal difficulty mode should be called hard, and the easy difficulty normal, but in this case we decided to stay true to how we think the game was meant to be designed.</p>
<p>Player feedback, and user feedback in general, is a complex topic. It is probably best summarised as &quot;can&apos;t live with it, can&apos;t live without it&quot;. I do believe that a certain level of entitlement has developed among players who expect to be heard, and it is up to developers to find the right balance between listening to that feedback, and trusting both their own creative vision and experience as a designer to make the right decisions. However, it is in our collective interests to realise that - while we are all entitled to opinions - making games is both a craft and a creative process, and we should respect a designer&apos;s skills and vision.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[When are there too many extension methods?]]></title><description><![CDATA[Extension methods are a useful tool in writing concise and readable code, but there is a point in which they become overused.]]></description><link>https://tomrijnbeek.me/blog/when-are-there-too-many-extension-methods/</link><guid isPermaLink="false">631898ead25b520c39b35b9a</guid><category><![CDATA[design patterns]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 06 May 2020 19:29:41 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Recently I noticed that in a codebase I worked on, the canonical way to format money had changed from a global <code>formatMoney</code> function to an extension method on money: <code>money.format</code>. A special formatter, <code>formatMoneyWithoutCurrency</code>, suffered an equal fate. Almost immediately, something felt off. The <code>Money</code> class was up to that point purely functional, but this extension method started adding methods to the class&apos; public interface that were rendering based.</p>
<p>There is nothing fundamentally wrong with having a <code>format</code> method on a class, but it does expand a class&apos; responsibilities beyond its original scope of being a functional type. In most languages, formatting is generally done by creating an instance of some sort of formatter, and giving the value you want to format as input. The formatter&apos;s responsibility is exactly to turn a value into a string, whereas the original class&apos; responsibility is to just do whatever it actually does (being money in the example above).</p>
<p>The global <code>formatMoney</code> method is in fact a helper method. If we got rid of the abstraction, we would get something similar to <code>MoneyFormat.forLocale(currentLocale).format(money)</code> instead. However, for performance reasons we want to cache the creation of the formatter, and for consistency reasons, we want to make sure every instance of the formatting uses the same basic formatting configuration, hence the helper method.</p>
<p>The abstraction to a global method makes sense, though I would argue in hindsight that having the money formatter be a global getter <code>moneyFormatForLocale</code> instead would make the formatting much more explicit. It is also in line with, say, <code>Numberformat.percentage.format(num)</code>. One can always discuss what is the best way to do it, and there is no single right answer, but I hope we can all agree on one thing: there already is a workable solution without extension methods for formatting money that <em>reveals intent</em> (it is clear what it is trying to do with the money), is <em>extensible</em> (I could easily have a completely different formatter for my money if I want), and avoids <em>unnecessary repetition of code</em> (I only need to specify the format I want). So where did the extension method come in?</p>
<h2 id="whatareextensionmethodsgoodfor">What are extension methods good for?</h2>
<p>Let&apos;s take a step back and look at when it actually <em>does</em> make sense to use extension methods. Extension methods are probably most commonly known to be part of C#. Older versions of C# noteworthily does not have the ability to add behaviour to interfaces. This is still something that makes sense to do in some circumstances. LINQ for example is a query language that can be used on all <code>IEnumerable</code> instances in C#, and is completely built with extension methods. This avoids situations such as on the equivalent of LINQ in Java - streams - where you need to specifically call a <code>stream()</code> method on your collection to enter &quot;query mode&quot;, and in the end turn it back into a collection to pass it further.</p>
<p>Extension methods can also be used to expand types (not just interfaces) for which the source isn&apos;t necessarily under your control, without having to create a subclass for that type. Subclasses allow you to add functionality too, but you don&apos;t get the functionality unless you wrap the parent class instances, which may be returned to you from the same library that defines it, and you can&apos;t inherit from structs.</p>
<p>Extension methods have allowed for libraries in C# that significantly increase code readability and developer velocity, such as for example the aforementioned <a href="https://docs.microsoft.com/en-us/dotnet/api/system.linq">System.Linq</a>, and <a href="https://fluentassertions.com/">FluentAssertions</a>. Their usefulness has made it so that other relatively modern languages such as Kotlin and Dart have also adopted them. Is it always necessarily a good thing to use extension methods though?</p>
<p>A final use case that extension methods can be used for, is to add domain-specific behaviour to a generic class. It is basically adding a bit of extra flavour to an existing helper class. For example, we may have something that helps us create notifications, a <code>NotificationFactory</code>. This type is widely useful, but within a certain domain we maybe want to build a notification with a very specific style very often. Having a <code>notificationFactory.createForDomain()</code> method, exposing only a subset of parameters, could be incredibly helpful. A solution without extension methods would be to use inheritance, or - even better - composition. An extension method takes away that need, as we don&apos;t really need to store additional state. That probably summarises what extension methods are: a substitute for inheritance or composition where no additional state is needed.</p>
<h2 id="whatareextensionmethodsnotgoodfor">What are extension methods NOT good for?</h2>
<p>While an extension method is not defined within the type that the extension method is defined on, it is important to remember that the extension method will still be part of its public interface to some extent. That is why one should still consider whether an extension method actually makes sense there. Extension methods shouldn&apos;t increase the responsibilities or alter the purpose of the type. If you need a type to behave differently, a new type is more appropriate.</p>
<p>Extension methods should also not be used for behaviour that is not unique defined for the type the method is on. To come back to my <code>format</code> example: formatting a price is not a single, uniquely defined behaviour. Not only is it locale-dependent, it is also context-dependent. The fact that a <code>formatWithoutCurrencyCode</code> also exists already exposes this problem. While using a formatter class using the type as input is more verbose, it is also more explicit, and leaves programmers open to implement other formatting methods if so required. Compare a formatting method to for example a <code>reversed</code> extension method on a list. Reversing a list is well defined, and make sense as part of the public interface of a list.</p>
<h2 id="shouldwestilluseextensionmethods">Should we still use extension methods?</h2>
<p>Extension methods are a useful part of the languages. They make it possible to quickly add new methods to a type&apos;s public interface. Because we don&apos;t actually open the file the type is in, we may be less primed to critically think about whether the type is really the right place to put a new method on. We should scrutinise the addition of an extension method as much as a normal method: does it fall within the responsibilities of the type, and is the method well defined?</p>
<p>Another class of extension methods that exists is the type that makes code read more like natural language. <a href="https://fluentassertions.com/">FluentAssertions</a> allows you to write test assertions of the form <code>actual.Should().Be(expected)</code>, which reads nicely, but one could argue that <code>Should</code> is hardly part of the class&apos;s responsibility (nor perhaps a well-defined behaviour). Based on the arguments presented above, I believe <a href="https://github.com/google/truth">Google&apos;s Truth</a> framework in Java does it slightly better with syntax as <code>assertThat(actual).equals(expected)</code>.</p>
<p>All in all, when to use or not use extension methods comes down to personal preference, much like any code pattern. That doesn&apos;t mean it is not important to blindly add new methods to a type because it&apos;s an extension method. At the very least, extension methods should not add ambiguous behaviour, such as formatting, to a type. As with everything: extension methods provide great power to the programmer, and with great power comes great responsibility.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Stable solutions for general orbits in games]]></title><description><![CDATA[Using the power of maths, we solve any two-body orbital system for games without any decay.]]></description><link>https://tomrijnbeek.me/blog/stable-solutions-for-general-orbits-in-games/</link><guid isPermaLink="false">631898ead25b520c39b35b98</guid><category><![CDATA[game physics]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 22 Apr 2020 19:32:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In the <a href="https://tomrijnbeek.me/blog/keplers-equation-for-games/">previous post</a>, we saw how to do the math to get velocities and positions along an elliptical orbit. To translate this into game code though, there is still one problem we have to solve. So far we have spoken about apoapsis and periapsis as determining the shape of the ellipse that describes the orbit, but it doesn&apos;t uniquely identify the orbit itself. As you can see in the image below, the rendered orbits have the same periapsis and apoapsis, but they still do not coincide. The orbit has been rotated around the primary, as it were.</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/04/05-orbits-arguments.png" alt="05-orbits-arguments" loading="lazy"></p>
<p>To uniquely identify orbits, we need one more parameter, namely where the orbit reaches periapsis. We talk about the <em>longitude of periapsis</em> $\varpi$ with respect to some reference direction. For our code, we will assume the positive x-axis as our reference direction.</p>
<p>We assume that the satellite is at periapsis at some instant $t_{0}$, and we now know that the relative angle with the x-axis at periapsis is $\varpi$. This means we have our starting point, and from there we can use Kepler&apos;s equations to calculate the rest.</p>
<h2 id="currentpositioninellipticalorbit">Current position in elliptical orbit</h2>
<p>In the <a href="https://tomrijnbeek.me/blog/keplers-equation-for-games/">previous post</a>, we showed how to get the current true anomaly based on the elapsed time, the eccentricity, and mean anomaly of the orbit. As a reminder, the mean anomaly is the average change in the direction of the satellite around the primary, so it is just $2\pi$ (a full rotation) divided by the period. To find the period of an elliptical orbit, we look at our good old friend Kepler again. In particular, Kepler&apos;s Third Law says:</p>
<p>$$T = 2\pi \sqrt{\frac{a^3}{GM}}.$$</p>
<p>$T$ is the period in seconds, $a$ is the semi-major axis of our orbit as before, and $GM$ the gravitational constant multiplied with the mass of the primary. As with circular orbits before, as a game developer you have the option to choose these however you want to make things look as you want to. The mean anomaly is thus:</p>
<p>$$\mu = \frac{2\pi}{T} = \frac{2\pi}{2\pi \sqrt{\frac{a^3}{GM}}} = \frac{1}{\sqrt{\frac{a^3}{GM}}}$$</p>
<p>We now have a plan: whenever we create a new orbit in our game, with a primary, periapsis, apoapsis, $t_0$, and longitude of periapsis, we first need to calculate a few derivative properties: the eccentricity, the semi-major and semi-minor axes, the centre of the ellipse, and the mean anomaly. These values don&apos;t change. From that point onward, we use Kepler&apos;s equation to calculate the position ever frame.</p>
<p>The full code can be found below.</p>
<pre><code class="language-csharp">static class Constants {
    public const double G = 1;
}

sealed class Body {
    public Vector2 Position { get; }
    public double Mass { get; }
}

sealed class Orbit {
    private readonly Body primary;
    private readonly double tPer;
    private readonly double eccentricity;
    private readonly double semiMajorAxis;
    private readonly double semiMinorAxis;
    private readonly Vector2 ellipseCenterOffset;
    private readonly double meanAnomaly;
    private readonly Matrix2 longitudeTransform;

    public Orbit(
            Body primary, double periapsis, double apoapsis, double tPer, double longitudePer) {
        this.primary = primary;
        this.tPer = tPer;

        eccentricity = (apoapsis - periapsis) / (apoapsis + periapsis);
        semiMajorAxis = 0.5 * (periapsis + apoapsis);
        semiMinorAxis = semiMajorAxis * Math.Sqrt(1 - eccentricity * eccentricity);
        ellipseCenterOffset = (periapsis - semiMajorAxis) * Vector2.UnitX;
        meanAnomaly = 1 / Math.Sqrt(
            semiMajorAxis * semiMajorAxis * semiMajorAxis / (Constants.G * primary.Mass));
        longitudeTransform = Matrix2.CreateRotation(longitudePer);
    }

    public Vector2 PositionAtTime(double currentTime)
    {
        // Calculate the true anomaly using Kepler&apos;s equation.
        var trueAnomaly = trueAnomalyAtTime(currentTime);

        // We calculate the distance vector from the primary to the satellite
        // under the assumption that longitude of periapsis is 0.
        var preRotatedOffset = ellipseCenterOffset + new Vector2(
            semiMajorAxis * Math.Cos(trueAnomaly),
            semiMinorAxis * Math.Sin(trueAnomaly));

        // We then rotate the point around based on the longitude of periapsis.
        var rotatedOffset = longitudeTransform.Transform(preRotatedOffset);

        // Finally, we translate this offset to be in the space of the primary.
        return primary.Position + rotatedOffset;
    }

    private double trueAnomalyAtTime(double currentTime)
    {
        const numIterations = 30; // Change depending on expected eccentricities.
        
        // Pre-calculate the current mean motion because it doesn&apos;t change.
        var M = (currentTime - tPer) * meanAnomaly;
        // Make an initial guess based on the eccentricity of the orbit.
        var E = eccentricity &gt; 0.8 ? Math.PI : M;

        for (int i = 0; i &lt; numIterations; i++)
        {
            var numerator = eccentricity * Math.Sin(E) + M - E;
            var denominator = eccentricity * Math.Cos(E) - 1;
            E = E - numerator / denominator;
        }

        return E;
    }
}
</code></pre>
<p>That is a lot of code, but it is all derived directly from the equations we&apos;ve seen over the past few posts. There is one new trick I have applied to deal with the longitude of periapsis though. Calculating offsets is hard when your ellipse is not axis-aligned. That is why for all the calculations, I just pretend our primary is the origin, and the periapsis lies along the positive x-axis. When we calculated the difference vector from the primary to the satellite in this normalized space, we transform it by first rotating around the origin, and then calculating the final position by adding the rotated difference vector to the primary&apos;s position.</p>
<h2 id="thethirddimension">The third dimension</h2>
<p>We spent five posts getting to the point of being able to describe an elliptical orbit in code. Surely, doing this in three dimensions will be much harder! Nothing less would be the case. All the equations still hold up in three dimensions. As a matter of fact, we have assumed that the physical rules of a 3D world hold up, and that we are merely working in a two-dimensional projection of the 3D world. This is also exactly why the step from 2D to 3D is relatively straightforward: we just have to put our code to work in a specific plane in that 3D world.</p>
<p>In orbital mechanics, we don&apos;t only have a reference for the longitude of periapsis, we also have a reference plane. In games we could easily pick the xy-plane, in the real world it is often the plane that is set out by the primary&apos;s equator. Our orbit will either coincide with the plane, or it will pass the plane through two points. The angle the orbit makes with the reference plane is called the <em>inclination</em>. The points in which the satellite passes through the reference plane are called the <em>ascending node</em> (when the orbit goes from below the plane to above it) and <em>descending node</em> (when the orbit goes in the other direction).</p>
<p>Earlier we used the longitude of periapsis to determine the orientation of the orbit, but in practice, we don&apos;t actually use that parameter. Instead, we use something called the <em>longitude of the ascending node</em> $\Omega$. Instead of measuring the angle between the reference direction and the periapsis, we measure the angle between the reference direction and the ascending node. To then fix where the periapsis is, we express the position of the periapsis as the <em>argument of periapsis</em> $\omega$ (yes they&apos;re both omegas, I didn&apos;t choose the symbols). The argument of periapsis is the angle between the ascending node and the periapsis measured in the orbital plane. The reason it is chosen this way is that with third dimensions, the periapsis no longer necessarily lies in the reference plane, meaning that calculating the angle between a reference direction and the periapsis becomes an undefined problem.</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/04/05-orbits-full.png" alt="05-orbits-full" loading="lazy"></p>
<p>To implement the longitude of periapsis in our code, we used a transformation that kept the math simple by moving the primary to the origin, and the periapsis on the positive x-axis. We can do the same thing for inclination. Remember that transformations are always applied &quot;backwards&quot; or right to left, so the steps in the code become:</p>
<ul>
<li>Rotate <code>rotatedOffset</code> around the z-axis (so it rotates in the reference plane xy) by the argument of periapsis. Our orbit now represents how the orbit looks with the orbital plane as reference plane, and the ascending node at the positive x-axis.</li>
<li>Rotate <code>rotatedOffset</code> around the x-axis by the inclination. We now have our actual orbit, but the ascending node is not yet in the right position.</li>
<li>Rotate <code>rotatedOffset</code> around the z-axis, since the xy-plane now the actual reference plane, by the longitude of the ascending node.</li>
</ul>
<p>Since these three rotations never change, we can precalculate the resulting matrix, and just translate our result once. That makes our final code:</p>
<pre><code class="language-csharp">static class Constants {
    public const double G = 1;
}

sealed class Body {
    public Vector2 Position { get; }
    public double Mass { get; }
}

sealed class Orbit {
    private readonly Body primary;
    private readonly double tPer;
    private readonly double eccentricity;
    private readonly double semiMajorAxis;
    private readonly double semiMinorAxis;
    private readonly Vector2 ellipseCenterOffset;
    private readonly double meanAnomaly;
    private readonly Matrix2 orbitTransform;

    public Orbit(
            Body primary, double periapsis, double apoapsis,
            double tPer, double inclination, double longitudeAscNode, double argPeriapsis) {
        this.primary = primary;
        this.tPer = tPer;

        eccentricity = (apoapsis - periapsis) / (apoapsis + periapsis);
        semiMajorAxis = 0.5 * (periapsis + apoapsis);
        semiMinorAxis = semiMajorAxis * Math.Sqrt(1 - eccentricity * eccentricity);
        ellipseCenterOffset = (periapsis - semiMajorAxis) * Vector2.UnitX;
        meanAnomaly = 1 / Math.Sqrt(
            semiMajorAxis * semiMajorAxis * semiMajorAxis / (Constants.G * primary.Mass));
        orbitTransform = createOrbitalTransformMatrix(
            inclination, longitudeAscNode, argPeriapsis);
    }

    private Matrix3 createOrbitalTransformMatrix(
            double inclination, double longitudeAscNode, double argPeriapsis) {
        // Rotate the periapsis in position assuming the ascending node is the
        // positive x-axis.
        var periapsisTransform = Matrix3.CreateAxisRotation(Vector3.UnitZ, argPeriapsis);
        // Rotate the orbit around the x-axis to give it its inclination.
        // We use the negative rotation here to make sure the x-axis is the
        // ascending node.
        var inclinationTransform = Matrix3.CreateAxisRotation(Vector3.UnitX, -inclination)
        // Finally, rotate the orbit so that the ascending node is in the right
        // position as well.
        var ascNodeTransform = Matrix3.CreateAxisRotation(Vector3.UnitZ, longitudeAscNode);

        // Transformations are applied right to left, so make sure we multiply
        // them in that order.
        return ascNodeTransform * inclinationTransform * periapsisTransform;
    }

    public Vector2 PositionAtTime(double currentTime)
    {
        // Calculate the true anomaly using Kepler&apos;s equation.
        var trueAnomaly = trueAnomalyAtTime(currentTime);

        // We calculate the distance vector from the primary to the satellite
        // under the assumption that longitude of periapsis is 0.
        // Note that the z-coordinate is 0, since the orbit is in the xy-plane.
        var preRotatedOffset = ellipseCenterOffset + new Vector3(
            semiMajorAxis * Math.Cos(trueAnomaly),
            semiMinorAxis * Math.Sin(trueAnomaly),
            0);

        // We then rotate the point in 3D space to apply inclination and the
        // correct orientation of the periapsis and ascending node.
        var rotatedOffset = orbitTransform.Transform(preRotatedOffset);

        // Finally, we translate this offset to be in the space of the primary.
        return primary.Position + rotatedOffset;
    }

    private double trueAnomalyAtTime(double currentTime)
    {
        const numIterations = 30; // Change depending on expected eccentricities.
        
        // Pre-calculate the current mean motion because it doesn&apos;t change.
        var M = (currentTime - tPer) * meanAnomaly;
        // Make an initial guess based on the eccentricity of the orbit.
        var E = eccentricity &gt; 0.8 ? Math.PI : M;

        for (int i = 0; i &lt; numIterations; i++)
        {
            var numerator = eccentricity * Math.Sin(E) + M - E;
            var denominator = eccentricity * Math.Cos(E) - 1;
            E = E - numerator / denominator;
        }

        return E;
    }
}
</code></pre>
<p>For bonus points, since all the transformations are rotations, you could switch to using a quaternion instead to speed up that step, but that part is left as an exercise to the reader.</p>
<h2 id="retrogradeorbits">Retrograde orbits</h2>
<p>For the last few posts, we&apos;ve been silently assuming that the orbits move counter-clockwise. If our primary also rotates around its axis counter-clockwise, we call these <em>prograde</em> orbits (i.e. the direction of the rotation of the primary matches the direction of the orbit). Prograde orbits are much more common than orbits in the opposite direction: <em>retrograde</em> orbits (i.e. orbits that move in the opposite direction as that of rotation of the primary). However, now that we have inclinations, retrograde orbits are a breeze to implement. To make an orbit traverse backwards, we just rotate the entire orbit by 180 degrees around some point.</p>
<h2 id="conclusion">Conclusion</h2>
<p>It took us several blog posts to get to this point, but we are now able to implement orbits using a somewhat analytical approach. We started out by seeing how integration methods can cause our orbital bodies to drift, and eventually just fly off the screen due to accumulated error. While recalculating the position of a satellite every frame is more work, starting from scratch each frame means we don&apos;t carry over our previous errors. The satellite may still not be in exactly the right position, but at least the error will not increase in magnitude as our simulation runs longer.</p>
<p>This solution is great, and maybe even necessary, for games that rely on stable orbits, but the solution doesn&apos;t scale. Moving from a two-body orbital system to a three- or even <em>n</em>-body orbital system is nearly impossible, and it is likely you will need to fall back on the physical simulation by frame method in those cases.</p>
<p>In case you want to read any of the past posts on this topic, below is a list of all posts in this series.</p>
<ul>
<li><a href="https://tomrijnbeek.me/blog/there-are-errors-in-you-game-physics-math/">Errors in your game physics math</a></li>
<li><a href="https://tomrijnbeek.me/blog/implementing-circular-orbirts-analytically/">Implementing circular orbits analytically</a></li>
<li><a href="https://tomrijnbeek.me/blog/math-behind-elliptical-orbits/">The math behind elliptical orbits</a></li>
<li><a href="https://tomrijnbeek.me/blog/keplers-equation-for-games/">Solving Kepler&apos;s equation for elliptical orbits in games</a></li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Solving Kepler's equation for elliptical orbits in games]]></title><description><![CDATA[To solve elliptical orbits analytically, we need to be able to solve Kepler's equation. This post discusses two possible approaches.]]></description><link>https://tomrijnbeek.me/blog/keplers-equation-for-games/</link><guid isPermaLink="false">631898ead25b520c39b35b97</guid><category><![CDATA[game physics]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 18 Mar 2020 15:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h2 id="introduction">Introduction</h2>
<p>Last time, we looked at how we can express elliptical orbits analytically. The problem is that to get the velocity on any point of the elliptical orbit, we need to solve an equation - <em>Kepler&apos;s equation</em> - which has no closed form solution.</p>
<p>While Kepler&apos;s equation doesn&apos;t have a nice direct solution, it does have a different nice property: numerical attacks work pretty well for it. That means we can use some of our knowledge about dealing with hard analytical problems on this single equation.</p>
<h2 id="fixedpointiteration">Fixed point iteration</h2>
<p>Let&apos;s look at Kepler&apos;s Equation again:</p>
<p>$$E - e \sin{E} = (t - t_\text{per}) \cdot n.$$</p>
<p>We can rearrange this slightly to turn it into a more common form:</p>
<p>$$E = e \sin{E} + (t - t_\text{per}) \cdot n.$$</p>
<p>Remember that $E$ is the value we&apos;re trying to solve for. If only we knew the value of $e \sin{E}$ we could solve this equation!</p>
<p>The first thing we can try is falling back on the most rudimentary of solving methods: guessing. If we have a guess for $E$, we can try filling it in on the right-hand side of the equation. This gives us a new value for $E$ on the left which is a better guess than our previous one. For example, let&apos;s start by guessing $0$. The next guess will be $E_1$ and is given by:</p>
<p>$$E_1 = (t - t_\text{per}) \cdot n.$$</p>
<p>Choosing $0$ as first guess causes the first term to be eliminated from the equation, leaving a pretty straightforward thing to calculate. If your eccentricity $e$ is close to zero - that is, if your orbit is near circular - the first part of the equation will not dominate the result either way. That makes sense, because with just some slight changes in notation, the equation above is just the equation for angle with the periapsis over time in a circular orbit:</p>
<p>$$\theta(t) = (t - t_\text{per}) \cdot \omega.$$</p>
<p>Since we&apos;re trying to solve our equation for actual elliptical orbits, we&apos;re not done yet. We can take our initial guess and fill it into Kepler&apos;s equation to get a better guess. In general:</p>
<p>$$E_{n+1} = e \sin{E_n} + (t - t_\text{per}) \cdot n.$$</p>
<p>Given enough iterations, this will converge to the actual solution for $E$. The number of iterations you need depend on your eccentricity $e$. For high eccentricities, you will need many iterations to find a close enough solution. This is the original solution Kepler used in the 17th century.</p>
<p>The code for this is relatively straight-forward:</p>
<pre><code class="language-csharp">double SolveKeplerThroughIteration(double e, double t, double tPer, double n)
{
    const numIterations = 30; // Change depending on expected eccentricities.

    // Pre-calculate the current mean motion because it doesn&apos;t change.
    var M = (t - tPer) * n;
    var E = 0;

    for (int i = 0; i &lt; numIterations; i++)
    {
        E = e * Math.Sin(E) + M;
    }

    return E;
}
</code></pre>
<p>Instead of using a fixed number of iterations, you could also consider iterating until you find the difference between $E_n$ and $E_{n+1}$ to be small enough to live with. However, note that this potentially introduces a variable cost in your game update loop, so bounding the number of iterations is always a good idea.</p>
<h2 id="newtonsmethod">Newton&apos;s method</h2>
<p>The fixed point iteration method is easy to implement and generally returns accurate results relatively quickly. It is a pretty dumb way to iterate though. This is something that is solved in Newton&apos;s method. It still starts with a guess that we will use to get closer to the final answer, but the choice of the next guess is done differently.</p>
<p>We could rearrange Kepler&apos;s equation once again.</p>
<p>$$e \sin{E} + (t - t_\text{per}) \cdot n - E = 0.$$</p>
<p>The left part could be considered a function $f(E)$. The values $E$ for which a function $f(E)$ is zero are called the <em>roots</em> of the function. In this case, finding the root of $f(E)$ gives us a solution for Kepler&apos;s equation.</p>
<p>The reason we&apos;re taking this roundabout approach to solving Kepler&apos;s equation, is because finding roots for functions is a problem mathematicians have been trying to solve for a long time, and there are ways to do that in a smarter way than trying out arbitrary values. One of these methods, and probably the most famous of these approaches, is Newton&apos;s method. This method also works with an iterative approach, but the different iterations $E_n$ are defined in a different way.</p>
<p>Assuming an initial guess $E_0$, the following guesses are given by</p>
<p>$$E_{n+1} = E_n - \frac{f(E_n)}{f&apos;(E_n)}.$$</p>
<p>Newton&apos;s method uses the rate of change, given by the derivative, to make more educated guesses as to where the function is going. For our version of Kepler&apos;s equation, we get the following expression for $E_{n+1}$:</p>
<p>$$E_{n+1} = E_n - \frac{e \sin{E} + (t - t_\text{per}) \cdot n - E}{e \cos{E} - 1}.$$</p>
<p>For most eccentricities $e &lt; 0.8$, $E_0 = t - t_\text{per}$ is a good starting point. If your eccentricities go beyond $0.8$, you can use $E_0 = \pi$.</p>
<p>Translating this into code doesn&apos;t look too different from the code above.</p>
<pre><code class="language-csharp">double SolveKeplerThroughNewtonIteration(double e, double t, double tPer, double n)
{
    const numIterations = 30; // Change depending on expected eccentricities.
    
    // Pre-calculate the current mean motion because it doesn&apos;t change.
    var M = (t - tPer) * n;
    // Make an initial guess based on the eccentricity of the orbit.
    var E = e &gt; 0.8 ? Math.PI : M;

    for (int i = 0; i &lt; numIterations; i++)
    {
        var numerator = e * Math.Sin(E) + M - E;
        var denominator = e * Math.Cos(E) - 1;
        E = E - numerator / denominator;
    }

    return E;
}
</code></pre>
<p>Another advantage of this approach is that you are trying to get a function as close to $0$ as possible, which makes it easy to change the exit condition of your loop based on the current output. Just fill in your current $E_n$ in $f$ and check if it&apos;s smaller than your desired difference.</p>
<h2 id="backtothephysicalworld">Back to the physical world</h2>
<p>Now that we have ways to solve for $E$, we can turn back to our physical world and attempt to turn this into a position we can use to update and/or draw our game world. In the previous post, we established that the position on an ellipse is given by</p>
<p>$$\begin{pmatrix}x \ y\end{pmatrix} = \begin{pmatrix}a \cos E \ b \sin E\end{pmatrix}.$$</p>
<p>This position is relative to the centre of the ellipse, which is not the same as the position of our primary, which is located in one of the foci of our ellipse.</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/03/02-orbits-ellipse.png" alt="02-orbits-ellipse" loading="lazy"></p>
<p>In the next post I&apos;ll show how the math can be made to work out, and we&apos;ll also introduce a third dimension to talk about inclined orbits.</p>
<h2 id="closingwords">Closing words</h2>
<p>In this post we looked at two ways to solve Kepler&apos;s equation, which is needed to keep track of orbits in an analytical way. The reason we started with the analytical approach was to avoid numerical errors, so falling back on a numeric approach for elliptical orbits seems to to defeat the point. However, for orbits we tried avoiding the numerical approaches for a very particular reason: they make stable orbits deviate and either diverge or converge over time. This is because the outcome of our numerical approach is used in the following frame, so our error adds up. For elliptical orbits on the other hand, we use numerical approaches to turn our source truth variables into something we can display, but the errors that slip in don&apos;t propagate to following frames.</p>
<p>There may still be floating point imprecision errors in keeping track of the current time, but the only problem that will introduce is that our satellite may not quite move at the right velocities around the orbit any longer. It will still be in one of the positions on the correct orbit though, and this orbit will never deteriorate. For many games, keeping the orbits constant is important, and this solution solves that problem.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The math behind elliptical orbits]]></title><description><![CDATA[Orbits are often not perfectly round, but rather elliptical. Elliptical orbits vary not just in distance, but also velocity, making the formulas all the more complicated!]]></description><link>https://tomrijnbeek.me/blog/math-behind-elliptical-orbits/</link><guid isPermaLink="false">631898ead25b520c39b35b96</guid><category><![CDATA[game physics]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 04 Mar 2020 15:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><h2 id="introduction">Introduction</h2>
<p>Coincidences rarely happen in physics, and perfectly circular orbits statistically don&apos;t exist.</p>
<p>The centripetal force will either be larger or smaller than the gravitational force at any given point in the orbit. This is what leads to elliptical orbits. When the centripetal force is larger, the satellite will start moving away from the primary. As the satellite does so, the velocity vector will no longer be orthogonal to the gravitational acceleration. Consequently, the satellite starts losing speed and the centripetal force gets less. Since the satellite is moving further away, the gravitational force also becomes less. If the satellite has too much energy, the gravitational force may never be enough to outweigh the centripetal force, which causes a hyperbolic trajectory. However, if the satellite lose enough energy for the gravitational force to become the strongest, the satellite will start falling back to the primary. This is when the opposite process happens, as gravity causes the satellite&apos;s velocity to increase, and the process repeats. We have our elliptical orbit.</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/02/02-orbits-apoperi.png" alt="02-orbits-apoperi" loading="lazy"></p>
<p>The point where the satellite is closest to the primary is called the <em>periapsis</em>. This is the point where the satellite has the most energy. The point in the orbit where the satellite is furthest from the primary is called the <em>apoapsis</em>, and this is where the energy of the satellite is the lowest. You may have heard terms such as apohelion and perigee before. Most known bodies have there own suffixes for the highest and lowest points of orbits around them. Apogee and perigee are the highest and lowest points in an orbit around Earth, apohelion and perihelion the highest and lowest points in an orbit around the Sun.</p>
<h2 id="ellipses">Ellipses</h2>
<p>It is no surprise that the equations for elliptical orbits are more complex than the orbital ones. As you may have guessed, an elliptical orbit is an ellipse where the primary is one of the foci. To understand elliptical orbits, we need to understand ellipses first.</p>
<p>Just as the circular orbits are described by a circle around its center, elliptical orbits are described by an ellipse around two foci. In a circle each point has the same distance to the center; in an ellipse each point yields the same result if you add up the distances to the two foci (sg. focus). The line through the two foci is called the <em>semi-major axis</em>, and the line through the center of the ellipse orthogonal to the semi-major axis is the <em>semi-minor</em> axis. The distance from the center to the ellipse along the semi-major and semi-minor axis are denoted by $a$ and $b$ respectively. See the following image for a representation of these concepts:</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/02/02-orbits-ellipse.png" alt="02-orbits-ellipse" loading="lazy"></p>
<p>The distance from the center to one of the foci $c$ is called the <em>linear eccentricity</em>. If you divide this by the distance $a$, you get the <em>eccentricity</em> of the ellipse, which describes how stretched the ellipse is. Eccentricity is a commonly used term to describe orbits. As the orbit approaches a circular orbit more and more, the focus will come closer to the center of the ellipse, and the eccentricity will tend towards 0.</p>
<p>For orbits, we are often not given the values $a$ and $b$, but the apoapsis and periapsis heights. However, as you can see, if you add up apoapsis and periapsis heights, you should get exactly $2a$. We also know the centre of the ellipse (it&apos;s exactly between apoapsis and periapsis), and thus we can calculate the distance between the centre and the primary to obtain $c$. We now have $a$ and $c$ which together allow us to calculate the eccentricity $e$, and now we have all we need to start expressing this orbit using the power of math!</p>
<h2 id="position">Position</h2>
<p>Remember that for circular orbits, we were able to determine the position over the parameter $\theta$ as:</p>
<p>$$\begin{pmatrix}x \ y\end{pmatrix} = r \begin{pmatrix}\cos \theta \ \sin \theta\end{pmatrix}.$$</p>
<p>For elliptical orbits, we can do something very similar:</p>
<p>$$\begin{pmatrix}x \ y\end{pmatrix} = \begin{pmatrix}a \cos E \ b \sin E\end{pmatrix}.$$</p>
<p>Instead of using a single radius $r$, we use $a$ and $b$ instead to represent that the ellipse has a different size horizontally as vertically. I also replaced the parameter $\theta$ with $E$. As opposed to what you may think, $E$ is not actually the angle between the semi-major axis and the line through the center of the ellipse and the point on the ellipse.</p>
<p>Since circles are easier to reason about, we use a circle to help us with ellipses. We create a circle which shares a centre with the ellipse, and has radius $a$ so it touches the ellipse in its two furthest points.</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/02/02-orbits-anomalies.png" alt="02-orbits-anomalies" loading="lazy"></p>
<p>The angle $E$ we are talking about is the angle between the center of the ellipse and the vertical projection of the point on the ellipse to this circle (blue in the image above), and is called the <em>eccentric anomaly</em>. The actual angle between the point on the ellipse and the semi-major axis (red in the image above) is called the <em>true anomaly</em> and often denoted by $\nu$.</p>
<h2 id="velocity">Velocity</h2>
<p>We can now draw an orbit, but we still haven&apos;t figured out the velocity of the satellite at any point in the orbit. Unlike circular orbits, the orbital speed of the satellite will change over time. This is where it gets a bit tricky. In circular orbits, the satellite moves at a constant rate, so we can increase $\theta$ at a constant rate, but for elliptical orbits we need to do more work to express $E$ as a function of time passed.</p>
<p>We can find the velocity $v$ of an object at any point in the orbit given $E$, but this isn&apos;t enough to give us the rate of change of $E$. We could use this to approximate the rate of change of $E$ by dividing among the radius of the auxilary circle to get the angular velocity. This may be a close enough approximation for a lot of applications.</p>
<p>For the velocity we have the following equation:</p>
<p>$$\tan{\frac{v}{2}} = \sqrt{\frac{1 + e}{1 - e}}\tan{\frac{E}{2}}.$$</p>
<p>For an approximate velocity of $E$, we&apos;d find:</p>
<p>$$\tan{(\pi a \dot{E})} \approx \sqrt{\frac{1 + e}{1 - e}}\tan{\frac{E}{2}}.$$</p>
<p>Or in a direct expression:</p>
<p>$$\dot{E} \approx \frac{\arctan{\left(\sqrt{\frac{1 + e}{1 - e}}\tan{\frac{E}{2}}\right)}}{2 \pi a}.$$</p>
<p>Here all variables are as before, and $e$ is the eccentricity of the ellipse.</p>
<p>Of course as this is an approximation it is prone to artefacts. For relatively small $e$ and most use cases in, say, games, this may be acceptable if the update steps are small. If we want to make sure that the orbit takes the correct amount of time though, we have to go a step further and introduce the concept of <em>mean motion</em> $n$. The mean motion is simply the average change of $E$ over time. This is simply $2 \pi$ divided by the period of the orbit.</p>
<p>Now we have all the concepts to build <em>Kepler&apos;s equation</em>:</p>
<p>$$E - e \sin{E} = (t - t_\text{per}) \cdot n.$$</p>
<p>In this equation, $t_\text{per}$ is the time at which the satellite passes through the periapsis of the orbit.</p>
<p>There we have it. For any given point in time $t$ we can find the eccentric anomaly $E$, convert that into the position, and we&apos;re done. Sadly, solving Kepler&apos;s equation isn&apos;t a thing we can easily do, as there isn&apos;t a so-called closed-form solution, which would mean we could express $E$ as some expression as we did with the approximation earlier. There are several ways around that, which we will explore in the next post, where we will also be looking at how to go about writing the code for elliptical orbits.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Implementing circular orbits analytically]]></title><description><![CDATA[When updating an object in orbit every frame, you will slowly but surely accumulate an error compared to where you are supposed to be. This post looks at how to analytically calculate where a body is supposed to be.]]></description><link>https://tomrijnbeek.me/blog/implementing-circular-orbirts-analytically/</link><guid isPermaLink="false">631898ead25b520c39b35b94</guid><category><![CDATA[game physics]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 19 Feb 2020 15:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In my last post I discussed different integration methods using a body orbiting its parent as an example. In this post, I want to shift to focus to how orbits actually work, and give you a look into how you might implement orbits analytically.</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/02/01-integration-orbit.png" alt="A satellite orbiting the primary" loading="lazy"></p>
<p>In this series of posts, we will exclusively look at two-body systems. That means we have a larger body (<em>the primary</em>) with a smaller body (<em>the satellite</em>) orbiting around it. This is already a gross simplification of reality: a satellite orbits a primary because of the gravity the primary exerts on the satellite. The satellite is generally much smaller, but it does also exert gravity on the primary. When two bodies are in orbit, they are in fact both rotating around a central point, their shared center of mass.</p>
<p>The bigger the ratio between the masses of the bodies, the smaller the effect on the primary. In the case of the Earth-Moon system for example, Earth is so much more heavy than the Moon, that the center of mass of the Earth-Moon system is very close to the center of mass of Earth itself. We can thus approximate the orbit of the Moon around Earth by assuming the Moon does not contribute to the center of mass of the system.</p>
<p>For these posts, we won&apos;t be going as far as detangling Einstein&apos;s laws of relativity, and so we will be looking at Newtonian graphics. We will always express the position of the satellite assuming the primary is in the origin of our coordinate system.</p>
<h2 id="circularorbit">Circular orbit</h2>
<p>In the simplest case, the satellite moves in a perfect circle around the primary at a distance $r$.</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/02/02-orbits-circular.png" alt="A circular orbit" loading="lazy"></p>
<p>If we wanted to represent the position of the satellite over time, we could make use of the sine and cosine functions.</p>
<p>$$\begin{pmatrix}x \\ y\end{pmatrix} = r \begin{pmatrix}\cos \theta \\ \sin \theta\end{pmatrix}.$$</p>
<p>Here $\theta$ is the angle of the satellite with the x axis of our coordinate system at any point in time. What remains is determining how this angle changes over time.</p>
<p>In the case of a perfectly circular orbit, the force pulling the satellite towards the primary (gravity $F_G$) is equal to the force trying to push the satellite away from it (centripetal force $F_c$). By putting the functions for both these forces on either side of the equals sign, we can derive the orbital velocity at a certain distance $r$:</p>
<p>$$F_G = \frac{G \cdot M_{s} \cdot M_{p}}{r^2} = \frac{M_s \cdot v^2}{r} = F_c.$$</p>
<p>Here $G$ is the gravitational constant. The nice thing about writing games is that we can choose the gravitational constant to be anything we want, even 1. $M_s$ is the mass of the satellite, and $M_p$ the mass of the primary, which we&apos;ll start denoting with $M$ going forward, as $M_s$ cancels out on both sides of the equation. Again, when writing games, you can just choose these values to be whatever you want. Since functions will (almost) always have $GM$ as a single clause, it&apos;s not a bad idea to choose $G$ to be 1 to simplify some of your logic. Often, the term $GM$ is simplified and denoted as $\mu$.</p>
<p>Let&apos;s cross out the parts of the equations we can:</p>
<p>$$\frac{G \cdot M}{r} = v^2.$$</p>
<p>This leads us to the following function for the orbital velocity of a circular orbit.</p>
<p>$$v = \sqrt{ \frac{GM}{r}  }.$$</p>
<p>We can transform the actual velocity into an angular velocity $\omega$ as well, which gives us the following function:</p>
<p>$$\omega = \sqrt{ \frac{GM}{r^3} }.$$</p>
<p>Finally, transforming this into code (assuming $G=1$) is straightforward:</p>
<pre><code class="language-csharp">void Init() {
    theta = 0;
    radius = (primary.Position - position).Length;
    angularVelocity = Math.Sqrt(primary.Mass / (radius * radius * radius))
}

void Update(float elapsedSeconds) {
    theta += elapsedSeconds * angularVelocity;
    position = radius * new Vector2(Math.Cos(theta), Math.Sin(theta));
}
</code></pre>
<h2 id="positionasafunctionoftime">Position as a function of time</h2>
<p>The function and code above calculates our orbital position incrementally. We keep track of our current angle, and each frame increment that. Another way to represent orbits though is as a function over time. For circular orbits, we know that our angular velocity over time is a constant function:</p>
<p>$$\omega(t) = \sqrt{ \frac{GM}{r^3} }.$$</p>
<p>Our angle over time is our angular velocity multiplied by the total time that has passed:</p>
<p>$$\theta(t) = t \cdot \omega(t).$$</p>
<p>Since we know how to transform $\theta$ into an actual position, we now have a function that gives the position at any given time:</p>
<p>$$\begin{pmatrix}x \\ y\end{pmatrix}(t) = r \begin{pmatrix}\cos ( \theta(t) ) \\ \sin ( \theta(t) )\end{pmatrix}.$$</p>
<p>Translating this into code is left as an exercise to the reader.</p>
<h2 id="conclusion">Conclusion</h2>
<p>As you can see, the math for circular orbits works out pretty nicely. This is not the case when we start pulling in a third dimension, or if orbits stop to be perfectly circular. We&apos;ll be looking at those in future posts.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Errors in your game physics math]]></title><description><![CDATA[The fact that we use fixed time steps in most game programming can lead to our actual solution deviating from our desired solution over time. This post explores how we can fix that problem.]]></description><link>https://tomrijnbeek.me/blog/there-are-errors-in-you-game-physics-math/</link><guid isPermaLink="false">631898ead25b520c39b35b93</guid><category><![CDATA[game physics]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 05 Feb 2020 15:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>It is not uncommon for games to contain some form of game physics. The most common - and probably most intuitive - of those, is gravity. In a simplified situation where we ignore air resistance, gravity applies a constant acceleration to an object (no matter its mass), which increases its downward velocity.</p>
<p>Gravity can also occur over longer distances: between orbiting bodies. I want to take orbits as an example today, since they lend themselves to some nice simplifications. When we talk about orbits, we often talk about a small body (<em>the satellite</em>) orbiting a static large body (<em>the primary</em>).</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/01/01-integration-orbit.png" alt="01-integration-orbit" loading="lazy"></p>
<p>The principle of an orbit is the same as normal gravity, but we need to be a bit more careful about the direction of our vectors. The primary pulls on the satellite, causing an acceleration that&apos;s orthogonal to the satellite&apos;s current velocity. The satellite&apos;s velocity is therefore altered so its trajectory curves towards the primary, but never enough to fall down.</p>
<h2 id="theproblem">The problem</h2>
<p>In games, we generally don&apos;t simulate constant acceleration. Instead, we update the state of our universe in fixed time steps. The code for updating the satellite could look something like this:</p>
<pre><code class="language-csharp">void Update(float elapsedSeconds) {
    velocity += acceleration * elapsedSeconds;
    position += velocity * elapsedSeconds;
}
</code></pre>
<p>The exact orbiting speed depends on the distance, the bodies&apos; masses and the gravitational constant, which we can ignore. Instead we just choose an orbiting speed. In fact, we can just always assume that the current velocity vector is the orthogonal of the vector pointing from the satellite to the primary. In 2D, that makes our code:</p>
<pre><code class="language-csharp">void Update(float elapsedSeconds) {
    toPrimary = (primary.Position - position).Normalized();
    // We use the clockwise orthogonal as velocity direction,
    // causing a counter-clockwise orbit.
    velocity = speed * new Vector2(toPrimary.Y, -toPrimary.X);
    position += velocity * elapsedSeconds;
}
</code></pre>
<p>The exact velocity calculation does not matter for our topic today, in so far as that it is the derivative of the position as a function in time. This means that we can express the calculation above as mathematical equation too:</p>
<p>$$p(t_0 + \epsilon) = p(t_0) + \epsilon \cdot \dot p (t_0).$$</p>
<p>The position at some time $t_0 + \epsilon$ for the elapsed time $\epsilon &gt; 0$ is the position at $t_0$ plus the velocity at $t_0$ multiplied by the elapsed time.</p>
<p>What we have actually done here is applying the <em>Euler method</em> to use a differential equation. In each step, we approximate the movement of the satellite that happened in the period between last frame and the current. We do this by using a constant velocity during this time period. However, in reality, the velocity constantly changes to arc the object towards the primary. We end up changing the position of the satellite slightly further away from the primary than the expected order.</p>
<p>In most cases, this may not matter than much. However, if your game expects orbits for example to be stable, you&apos;ll find that if you leave the game running long enough, satellites will slowly move away further and further from their primary. You can see this in the image below:</p>
<p><img src="https://tomrijnbeek.me/blog/content/images/2020/01/01-integration-error.png" alt="01-integration-error" loading="lazy"></p>
<p>In the animation above, the speeds and time steps have been exaggerated to highlight the error. In normal circumstances the error will be much less egregious, but it is there nonetheless. Let&apos;s look into a few ways we can fix this.</p>
<h2 id="smallersteps">Smaller steps</h2>
<p>When we talk about the speed of a game, we usually quickly arrive at talking about FPS - frames per second. FPS is inherently a graphics concept, and talks about how often we can render the game to the screen per second. Often FPS is linked to the numbers of updates per second - UPS. It is not uncommon for games to render more frames per second than the refresh rate of the monitor can handle, especially in games that are fast-paced, to reduce the input lag for its players.</p>
<p>If we have a game where our rendering takes significantly slower than our updating, it doesn&apos;t make much sense to link those two together. In general, the major bottleneck of the rendering is the GPU, while the updating is mostly limited by the CPU resource. By separating the update loop from the rendering loop, we can try to squeeze as many update cycles as possible out of the CPU. That way we reduce the time steps, and the error over time.</p>
<p>Sometimes it&apos;s not possible or practical to separate the actual loops, but that doesn&apos;t prevent us from doing more update cycles than rendering cycles. When we have a game running at 60 FPS, we can always decide to split the time per frame in two, and do two update cycles with half the time step every frame. This means we are effectively updating at 120 UPS. This has the drawback that if the update cycle takes too long, we can&apos;t keep up the 60 FPS, and we&apos;ll slow down the rendering as well.</p>
<p>Reducing the time deltas will reduce the error build-up over time. This makes sense, since mathematically taking the limit of the time delta approaching 0 gives us the actual result we want. However, getting a small delta is not always possible, especially if we keep in mind that less powerful machines may have less UPS than a massive gaming rig. Let&apos;s look at some mathematical tricks that we may use instead.</p>
<h2 id="midpointmethod">Midpoint method</h2>
<p>The source of the error in the Euler method is that we look at the derivative of the position (which is the velocity) at $t_0$. The velocity changes over time, and this is not accounted for. Instead of looking at the derivative at $t_0$, we can look at the derivative at $t_0 + \epsilon / 2$. The idea is that instead of assuming that our velocity over time can be approximated by a constant velocity, we assume that our acceleration (the change in velocity) can be approximated by a constant acceleration instead. By looking at the velocity at $t_0 + \epsilon / 2$, the intuition is that we take the average velocity over our time delta, which gives a closer approximation. In a mathematical expression, this is expressed as:</p>
<p>$$p(t_0 + \epsilon) = p(t_0) + \epsilon \cdot \dot p (t_0 + \frac{\epsilon}{2}).$$</p>
<p>The problem is that we don&apos;t know the value of $\dot p (t_0 + \epsilon/2)$. However, we can always use our original trick of using the Euler method to find $p (t_0 + \epsilon/2)$ and calculate the derivative in that point.</p>
<p>This method is named the midpoint method after the fact that we are looking at the midpoint of our time segment to get the derivative to use. Let&apos;s take a quick look at how this could look in code:</p>
<pre><code class="language-csharp">void Update(float elapsedSeconds) {
    var velocityAtCurrent = velocityAt(position);
    var midpoint = position + velocityAtCurrent * elapsedSeconds * .5f;
    var velocityAtMidpoint = velocityAt(midpoint);
    position += velocityAtMidpoint * elapsedSeconds;
}

Vector2 velocityAt(Vector2 pos) {
    toPrimary = (primary.Position - pos).Normalized();
    return speed * new Vector2(toPrimary.Y, -toPrimary.X);
}
</code></pre>
<p>We see that we need to do roughly double the calculations, but we get more precision in return.</p>
<h2 id="heunsmethod">Heun&apos;s method</h2>
<p>We have looked at the velocity at $t_0$ and $t_0 + \epsilon / 2$, but what about the velocity at $t_0 + \epsilon$? If we use the velocity at $t_0$, we get a result (this is the Euler method), and if we use $t_0 + \epsilon$ we get a different result. It is very likely that our actual result is somewhere between these two. Heun&apos;s method is based on this. It uses the Euler method to calculate a first approximation. Then we calculate the velocity at this new point, and use it to make another approximation. We average our two approximation and use that as a final solution. This is a bit easier to express in code than in math, so let&apos;s start there:</p>
<pre><code class="language-csharp">void Update(float elapsedSeconds) {
    var velocityAtCurrent = velocityAt(position);
    var eulerPosition = position + velocityAtCurrent * elapsedSeconds;
    var velocityAtEulerPos = velocityAt(eulerPosition);
    var heunPosition = position + velocityAtEulerPos * elapsedSeconds;

    position = .5f * (eulerPosition + heunPosition);
}

Vector2 velocityAt(Vector2 pos) {
    toPrimary = (primary.Position - pos).Normalized();
    return speed * new Vector2(toPrimary.Y, -toPrimary.X);
}
</code></pre>
<p>Below is an attempt to describe this mathematically:</p>
<p>$$<br>
\begin{aligned}<br>
p_{\text{Euler}}(t_0 + \epsilon) &amp; = p(t_0) + \epsilon \cdot \dot p (t_0) , \<br>
p_{\text{Heun}}(t_0 + \epsilon) &amp; = p(t_0) + \epsilon \cdot \dot p_{\text{Euler}} (t_0 + \epsilon) , \<br>
p(t_0 + \epsilon) &amp; = \frac{p_{\text{Euler}}(t_0 + \epsilon) + p_{\text{Heun}}(t_0 + \epsilon)}{2} .<br>
\end{aligned}<br>
$$</p>
<h2 id="analytical">Analytical</h2>
<p>We can also take a different approach entirely. Instead of approximating our physics, we could use the exact analytical solution as well. For a circular orbit, we just need to be able to describe a point moving along a circle, which is fairly straightforward. For eccentric orbits things get a bit more complicated, and we&apos;ll discuss this in a follow-up post.</p>
<p>In general, analytical solutions are impractical. While we may be able to calculate simple systems (two body orbital systems, a single falling object), it becomes impractical for larger problems.</p>
<h2 id="closingwords">Closing words</h2>
<p>Integration methods have the problem that they accumulate errors. The small error we introduce in one frame will live on in all future frames of our simulation. In orbital mechanics, this often leads to drift, and causes stable orbits to eventually become unstable. In many practical cases, the physical simulations don&apos;t take long enough, or the effects aren&apos;t noticeable enough that we need to worry about it, but in some cases we need to do a bit of extra work to make things look right.</p>
<p>Orbits are a feeble thing, as they are easy to make unstable with an accumulated error. That&apos;s why we will spend the next few blog posts picking apart the math behind orbital mechanics, and try to build an implementation of orbits that can keep running for an indefinite amount of time.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[The Fun Factor]]></title><description><![CDATA[What makes a game fun? If you dig deep into the game mechanics, you will find that if you have a good core game loop, the rest follows.]]></description><link>https://tomrijnbeek.me/blog/the-fun-factor/</link><guid isPermaLink="false">631898ead25b520c39b35b95</guid><category><![CDATA[game design]]></category><category><![CDATA[prototyping]]></category><category><![CDATA[roche fusion]]></category><dc:creator><![CDATA[Tom Rijnbeek]]></dc:creator><pubDate>Wed, 22 Jan 2020 15:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Every game starts with an idea. Often, this idea represents the core mechanic in your game. Games often have a core game loop that represents the foundation of all the rest of the gameplay. Core game loops can be simple (e.g. match-3 in Candy Crush) or complex (e.g. drop &gt; collect loot &gt; kill others &gt; survive in battle royale games such as PUBG or Fortnite). There is but one fundamental truth that is important though: if your core game loop isn&apos;t fun, your game won&apos;t be fun.</p>
<h2 id="anexamplerochefusion">An example: Roche Fusion</h2>
<p>Let&apos;s rewind a bit. Roche Fusion is an almost-casual space shoot&apos;em mixed with roguelite elements such as full procedural generation and a unique upgrade path in each game. Below you can see screenshots both of the released version, and also a screenshot of one of the first prototypes.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://tomrijnbeek.me/blog/content/images/2020/01/Prototype_Teaser-_Beard_Game_Moment.jpg" width="1280" height="720" loading="lazy" alt srcset="https://tomrijnbeek.me/blog/content/images/size/w600/2020/01/Prototype_Teaser-_Beard_Game_Moment.jpg 600w, https://tomrijnbeek.me/blog/content/images/size/w1000/2020/01/Prototype_Teaser-_Beard_Game_Moment.jpg 1000w, https://tomrijnbeek.me/blog/content/images/2020/01/Prototype_Teaser-_Beard_Game_Moment.jpg 1280w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://tomrijnbeek.me/blog/content/images/2020/01/rf3-1.jpg" width="1280" height="720" loading="lazy" alt srcset="https://tomrijnbeek.me/blog/content/images/size/w600/2020/01/rf3-1.jpg 600w, https://tomrijnbeek.me/blog/content/images/size/w1000/2020/01/rf3-1.jpg 1000w, https://tomrijnbeek.me/blog/content/images/2020/01/rf3-1.jpg 1280w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Screenshots from Roche Fusion prototype and final version respectively.</figcaption></figure><!--kg-card-begin: markdown--><p>The screenshots show that the final version is much more polished and visually appealing, but there is even more that the screenshots don&apos;t show: a full soundtrack, dozens of enemy types and behaviours, many different upgrades, eight playable ships with completely different playstyles, and more. Here comes the kicker though: I could fire up that early prototype, and still have fun. The graphics were simple, the enemies programmed to follow an exact predefined pattern of behaviours, and it was enough to prove to us that people would have fun playing this game.</p>
<p>Of course all the layers of juice, content, and other fanciness made Roche Fusion (or beardgame, as we called it back them) from something that was merely fun into a production-ready game. Yet, I cannot imagine that all the layers that we added would have made a successful game if our earliest prototype hadn&apos;t been so much fun to play already.</p>
<h2 id="gamejams">Game jams</h2>
<p>It may be hard to believe this as a truth, but I think there is another phenomenon that makes this truth even more convincing: game jams. Game jams are contests where developers are giving a limited time (often 48 or 72 hours) to make a game from scratch. It comes as no surprise that in that time you don&apos;t have the time to add a whole lot of content or to build a graphics engine that will define games for the next decade. Game jams often have simple graphics, simple sounds (if you are lucky), and can usually not be considered as much more than a proof of concept. In other words, game jam games are often just the core game loop with a thin layer of fanciness to make the whole package presentable.</p>
<p>There is only one game jam game that I am really satisfied with. The game started out with a silly idea, and after a day of fun, it just didn&apos;t work when playing. I made the hard call to start over the second day, and ended up with something that was extremely simple but a lot of fun. Once the core game mechanic had been revised, everything just clicked in place.</p>
<h2 id="thelesson">The lesson</h2>
<p>I think of all the lessons I have learned during my career as game developer, this truth has been one of the most important. It is also the most difficult to put in practice. Game design is a creative process after all, and inspiration comes and goes.</p>
<p>To make things even harder, you can&apos;t know for sure whether a game mechanic is fun or not without implementing it and trying it out. You could compare it to other creative processes, such as painting: in your mind a colour combination might work really well, but as soon as your paint it, it may not look as well as you intended. If you practice more and more, you&apos;ll build up an intuition for what works and what doesn&apos;t. There will still be times where you&apos;re wrong though, game design will never become more certain than a series of educated guesses.</p>
<p>Luckily there is a fairly easy fix for this: iteration. Build prototypes to see what works and what doesn&apos;t. Not only does this help you to find ideas that work, it&apos;s also good practice: it will help you build an intuition, but it will also make you more able to distinguish what separates a good idea from a bad one. Game design is as much about dropping ideas as it is about generating them. Participating in game jams is a great tool to force yourself to work on an idea that you can drop after just a couple of days.</p>
<p>My current project, simply called <a href="https://github.com/beardgame/td">TD</a>, started from just such a (self-imposed) game jam. We spent a weekend building a prototype for a tower defence in a hexagonal grid with some novel ideas. Afterwards we played the game, and despite there not being any graphics or more than a single type of tower, it was fun. We decided to keep the idea and we&apos;re now two years in. If the game had been dull, we&apos;d have dropped it and tried something else.</p>
<p>So to summarise: games stand or fall based on their core mechanics. If the foundation of your game isn&apos;t fun, then no matter how much prettiness you add on top, you&apos;ll never turn the game into something great. Determining whether your core mechanics are fun can only be done by building them. One day you&apos;ll stumble upon a great idea you can actually invest time in making great, knowing that your game has got that Fun Factor.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>