<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Damian Connolly | divillysausages.com</title>
    <description>divillysausages.com is the site of Damian Connolly, an Irish (game) developer currently living in Bordeaux, France. Languages include AS3, C#, JavaScript, TypeScript, ObjC, C++, Groovy, Dart, Flutter and any other buzzword that currently floats his boat</description>
    <link>https://divillysausages.com/</link>
    <atom:link href="https://divillysausages.com/feed.xml" rel="self" type="application/rss+xml" />
    
      <item>
        <title>Moving on</title>
        <description>&lt;p&gt;I'm a little bit late doing this (it's been over 6 years since my last update 😅), but after nearly 10 years at &lt;a href=&quot;http://triplefun.com/&quot; class=&quot;external&quot;&gt;TripleFun&lt;/a&gt;, then &lt;a href=&quot;https://www.betclic.fr/&quot; class=&quot;external&quot;&gt;Betclic&lt;/a&gt;, I'm moving on and joining &lt;a href=&quot;https://www.spendesk.com/&quot; class=&quot;external&quot;&gt;Spendesk&lt;/a&gt; as a &lt;a href=&quot;https://divillysausages.com/cv#work-spendesk&quot;&gt;Senior Back-End Engineer.&lt;/a&gt;&lt;/p&gt;
&lt;img src=&quot;https://divillysausages.com/files/spendesk.png&quot; alt=&quot;Spendesk logo&quot; width=&quot;925&quot; height=&quot;198&quot;&gt;
&lt;p&gt;It's a bit weird to think &lt;em&gt;&quot;10 years&quot;&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;10 whole years&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;It's such a long time, but it doesn't feel that long when I think about it. Time has flown.&lt;/p&gt;
&lt;p&gt;TripleFun was a journey and a half - multiple different games across different technologies (Flash, native with Obj-C, then Unity) culminating in &lt;a href=&quot;https://divillysausages.com/2016/06/05/microgolf-masters/&quot;&gt;Microgolf Masters&lt;/a&gt;, a game which ultimately touched a few million players. Along the way, I made a lot of mistakes, learned how to fix them, and started on the continuous learning path that I've been able to benefit from since.&lt;/p&gt;
&lt;p&gt;I started with backend code, learned how to handle servers on AWS with the help of some very talented people, learned &lt;em&gt;a ton&lt;/em&gt; about admining a game of that size, handled the support for a number of years (if one thing teaches you about the game you're making, handling support for it is that thing), learned about tools, and statistics, and analytics, and prototyping. In short, if you want to have a hand in everything, join a startup.&lt;/p&gt;
&lt;p&gt;When our CEO, Nicolas, went back to Betclic, he brought TripleFun with him, and we slowly merged into a larger whole. Over time, the focus became less on making games and more on using our unique skills to benefit the mothership. We made a lot of prototypes touching on a lot of subjects, and again, thanks to some amazing people, I was able to add a few more skills to my belt, most notably everything serverless (AWS, Lambda, DynamoDB) and later &lt;a href=&quot;https://flutter.dev/&quot;&gt;Flutter&lt;/a&gt; (which, in my opinion, should be the first and only step if you're looking to develop apps - seriously, I ❤ this tech).&lt;/p&gt;
&lt;p&gt;I guess the flow of different projects and technologies contributed to that feeling of time flying by. It's interesting that the more you learn, the more you realise that all the flaws in everything that you did previously could be handled sooooo much easier with the knowledge and tech that you have now. But I suppose the nature of things is to keep building and keep making the next thing that bit better.&lt;/p&gt;
&lt;p&gt;So, after nearly 10 years, I'm starting a new position in a completely new domain, learning a new way of doing things. Everything is different, and the experience has been humbling to say the least 😅 (Imposter Syndrome is real 😂). TripleFun and Betclic have been fantastic places to work, and I wouldn't be where I am now without them. I know they'll have the greatest success (anyone looking at Betclic's transformation over the last number of years will agree), and I want to thank Nicolas Beraud for the opportunity that came my way back in 2013.&lt;/p&gt;
&lt;p&gt;Let's go 🚀&lt;/p&gt;</description>
        <pubDate>Mon, 23 Jan 2023 20:40:00 +0000</pubDate>
        <link>https://divillysausages.com/2023/01/23/moving-on/</link>
        <guid isPermaLink="true">https://divillysausages.com/2023/01/23/moving-on/</guid>
      </item>
    
      <item>
        <title>Microgolf Masters: Tips, tricks, cheats, &amp;amp; hacks</title>
        <description>&lt;p&gt;&lt;a href=&quot;https://divillysausages.com/2016/06/05/microgolf-masters/&quot;&gt;Microgolf Masters&lt;/a&gt; is our realtime multiplayer golf available on iOS and Android. Want to know how a simple way to get unlimited gold and diamonds? Read on...&lt;/p&gt;
&lt;p&gt;TL;DR: You can't.&lt;/p&gt;
&lt;p&gt;Like most devs, I occasionally put the name of our game into Google to see what comes up. Sometimes I fall on reviews, sometimes I get Let's Play videos on YouTube, but often I find sites that promise you the world for nothing. Normally, the obviously auto-generated layout of these sites should be enough to let you know that it's not exactly legit, but recently I came across a site that was quite well made, to the point where it got me thinking, &lt;em&gt;Hey, hang on a minute...&lt;/em&gt;&lt;/p&gt;
&lt;img src=&quot;https://divillysausages.com/files/mastersImages/tip_tricks_cheats.jpg&quot; alt=&quot;Microgolf Masters: tips, tricks, &amp;amp; cheats site&quot; width=&quot;600&quot; height=&quot;615&quot; /&gt;
&lt;p&gt;The thing is, and I want there to be no doubt about this:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;There is no way for an external service to give you free gold or diamonds&lt;/strong&gt;&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;There is also no way for someone to modify the game client to give you free money&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;See, everything in Masters goes through the server and the DB. When you win anything, it's the server that gives it to you. When you buy anything, it's the server that says if you can afford it. When you connect to the game, we download your details from the server, so there's nothing on the client to change. Even if you &lt;em&gt;did&lt;/em&gt; modify the memory variables to give yourself more cash on the client, you wouldn't be able to actually &lt;em&gt;do&lt;/em&gt; anything with it, because the server rules all; it knows what you actually have.&lt;/p&gt;
&lt;p&gt;The DB itself isn't accessible from the public web, only through internal routing, so no-one can connect to the DB to update your info.&lt;/p&gt;
&lt;p&gt;Except us, obviously.&lt;/p&gt;
&lt;p&gt;If you go to a site and they ask you to run something, it won't work. If they ask you to install something on your phone, it won't work. If they ask you to install something on your PC, it &lt;em&gt;really&lt;/em&gt; won't work.&lt;/p&gt;
&lt;p&gt;And in the best traditions of &lt;em&gt;there's no such thing as a free lunch,&lt;/em&gt; these sites aren't doing this out of the gratitude of their hearts or just for the lols. Best case scenario, they're driving you towards a page full of ads, worst case scenario, you've just installed malware on your computer or are now part of a botnet.&lt;/p&gt;
&lt;p&gt;So this is a Public Service Announcement: don't fall for this please. They can be convincing, even to us. But be safe and ask yourself if the possibility of getting some free coins in an online game outweighs the probability of putting something bad on one of your devices (hint: it doesn't).&lt;/p&gt;
&lt;p&gt;If you've ever run one of these - and not just for our game - then the first thing you need to do is to run a scan of your system. If you find anything, then I'd recommend &lt;a href=&quot;https://www.piriform.com/ccleaner/download&quot; class=&quot;external&quot;&gt;CCleaner&lt;/a&gt; or &lt;a href=&quot;https://malwarebytes.com/&quot;&gt;Malwarebytes&lt;/a&gt; to get rid of it.&lt;/p&gt;
&lt;p&gt;Stay safe, players.&lt;/p&gt;</description>
        <pubDate>Sun, 04 Dec 2016 23:40:00 +0000</pubDate>
        <link>https://divillysausages.com/2016/12/05/microgolf-masters-tips-tricks-cheats-hacks/</link>
        <guid isPermaLink="true">https://divillysausages.com/2016/12/05/microgolf-masters-tips-tricks-cheats-hacks/</guid>
      </item>
    
      <item>
        <title>Microgolf Masters</title>
        <description>&lt;div id=&quot;alsoLinks&quot;&gt;
	&lt;ul&gt;
		&lt;li&gt;Microgolf Masters&lt;/li&gt;
		&lt;li&gt;&lt;a href=&quot;https://divillysausages.com/2016/06/05/microgolf-masters-images/&quot;&gt;Images&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;This one was a while in coming - our current &lt;a href=&quot;http://triplefun.com/&quot; class=&quot;external&quot;&gt;TripleFun&lt;/a&gt; game: Microgolf Masters.&lt;/p&gt;
&lt;a href=&quot;https://itunes.apple.com/app/id1045436888&quot; class=&quot;external&quot;&gt;&lt;img src=&quot;https://divillysausages.com/files/mastersImages/logo.jpg&quot; alt=&quot;Microgolf Masters logo screen&quot; width=&quot;320&quot; height=&quot;569&quot; /&gt;&lt;/a&gt;
&lt;h2 id=&quot;about&quot;&gt;About the game&lt;/h2&gt;
&lt;p&gt;Masters is the sequel to &lt;a href=&quot;https://divillysausages.com/2015/05/24/microgolf-challenge/&quot;&gt;MicroGolf Challenge&lt;/a&gt;, itself the sequel of MicroGolf, so it's interesting to see the evolution of the game as time has gone by. As of the time of writing, MicroGolf Challenge has been removed from sale to stop any confusion amongst players.&lt;/p&gt;
&lt;p&gt;While &lt;em&gt;Challenge&lt;/em&gt; was a good game and enjoyed modest success, there were problems with the inherit conception of the gameplay and monetisation, so after meeting with other CEOs of mobile games companies, Nicolas decided that the best course of action would be to release a new game. That way, we can make any number of breaking changes with the previous one without suffering the wrath of an existing playerbase.&lt;/p&gt;
&lt;p&gt;And there were a hell of a lot of changes. Here are just some of the differences:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;More granular upgrade system (In &lt;em&gt;Challenge&lt;/em&gt; we underestimated just how quickly players would progress). Balls can now also give boosts&lt;/li&gt;
	&lt;li&gt;Progression through courses was replaced by a League system (previously named &lt;em&gt;Rank&lt;/em&gt;). Winning matches gained you crowns. Win enough crowns and you pass to the next League. The top League is Masters, where the ranking is ELO based.&lt;/li&gt;
	&lt;li&gt;A new &lt;em&gt;season&lt;/em&gt; system. Every week, a new season begins. You win rewards based on how you placed in the previous season, and your League would be reset (depending on where you finish, it'd be reset more or less)&lt;/li&gt;
	&lt;li&gt;A new solo mode, where players are presented with a new course every day. Get the best score of the day for your League, and you win rewards, as well as a medal for your profile&lt;/li&gt;
	&lt;li&gt;8-player Tournament mode, where players face off in 1v1 matches until there's one winner. Amongst other things, that winner gets a trophy for their profile&lt;/li&gt;
	&lt;li&gt;4-player free-for-alls, or 1v3, where 4 players play on the same course, which is pretty fun&lt;/li&gt;
	&lt;li&gt;A Card system. As you play, you'll win or buy Cards, which can be used for all sorts of things, such as getting new balls, upgrading your stats, getting Boosters, etc&lt;/li&gt;
	&lt;li&gt;Boosters! In games, you're allowed to bring 2 boosters with you. These are special items, that used at the right time, can turn the tide of the game. Some of the Boosters included are extra Strength/Accuracy, Confusion, Anchor, Invincibility, Assassin, and Unsinkable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;More updates are coming down the line. At the minute, we're finishing up a big performance update, to get the game back up under 60fps and silky-smooth, and after that we're planning on a teams feature, where players can face off against each other in 2v2 matches.&lt;/p&gt;
&lt;h2 id=&quot;dev&quot;&gt;A bit about the development&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;Challenge&lt;/em&gt; was our first Unity game, so by the time we tackled &lt;em&gt;Masters&lt;/em&gt; we had a bit more experience and could tell what was working and what wasn't. Although we were working from the same codebase, we profited a bit at the start of development to restructure and refactor anything that wasn't up to scratch.&lt;/p&gt;
&lt;p&gt;One thing we set from the start, which I'm glad about, it focused solely on Android development, so we were only on one platform from the get-go. This was a &lt;em&gt;huge&lt;/em&gt; timesaver in the long run, as, if you don't know about it, the time to release on different platforms is wildly different.&lt;/p&gt;
&lt;p&gt;Though it's better nowadays, iOS would routinely take ~2 weeks to validate a release. As you're working in sprints of about 2 to 3 weeks, this meant that you had to support the version in dev, the version in pre-prod (that was currently being validated), and the version in prod. So anytime there's a bug, there was a 2 to 4 week delay before we could fix it (once you've figured out if it's already been fixed or if it's a regression).&lt;/p&gt;
&lt;p&gt;Android, on the other hand, has a release window of a few hours. The difference is day and night. Release in the morning, then monitor, and you still have time to scramble a fix if necessary. As an anecdote, we were cleaning up plugins and requested permissions, and I removed one that inadvertently changed the path to the &lt;em&gt;Application storage directory&lt;/em&gt;, which is where we saved the player's save file, including their login details. So I borked &lt;em&gt;every single player that wasn't signed in with Facebook&lt;/em&gt;. Needless to say, that's a good feeling. Happily, the files weren't gone, so once I'd found the problem, two releases in the same day later, everyone was back up and running. That would have been impossible on iOS.&lt;/p&gt;
&lt;p&gt;Another thing that we did was enlist the help of fellow Unity devs &lt;a href=&quot;http://www.betomorrow.com/homepage_en/&quot; class=&quot;external&quot;&gt;BeTomorrow&lt;/a&gt;. They're based just down the road from us, and we had help from Ga&amp;euml;l Lequeux and Alix Fumoleau, both of whom brought their Unity expertise to the table and helped us out with the UI and perf.&lt;/p&gt;
&lt;p&gt;We're also using the services of &lt;a href=&quot;http://logmatic.io/&quot; class=&quot;external&quot;&gt;Logmatic.io&lt;/a&gt;, which is a company specialised in logging and metrics. We had our own solution, but we'd always have to wait a day before the logs were available in our backend. With Logmatic, all logs come in in real-time, and we can do some pretty deep searches. This is a life-saver when it comes to support and bug-hunting. As a result, the majority of support questions can be resolved in around about a minute from the time we open the mail.&lt;/p&gt;
&lt;h2 id=&quot;release&quot;&gt;The release&lt;/h2&gt;
&lt;p&gt;Masters was in soft launch on Android for quite a long time before we pushed it on iOS. All in all, there were around 25 versions released on Android while we added, fixed, and polished new features.&lt;/p&gt;
&lt;p&gt;When we did release on iOS, it was to a pretty awesome feature. We ended up on the home page of the US iTunes store and ended up in the top 10 overall, and top 5 for sports and strategy. On the french store, we hit #1 and #2 for sports and strategy respectively. Considering the other games on offer and their respective advertising budgets, this was huge!&lt;/p&gt;
&lt;img src=&quot;https://divillysausages.com/files/mastersImages/top_sports_fr.jpg&quot; alt=&quot;Top free sports on the iOS French store&quot; width=&quot;490&quot; height=&quot;300&quot; /&gt;
&lt;p&gt;The effect of this was pretty immediate; we were gaining around 50k players per day. Our server started crying, but the effect in game was pretty impressive. Searching for games was immediate. Even tournaments, which would normally take a bit longer to fill up, were instant.&lt;/p&gt;
&lt;p&gt;On our end, aside from the heady rush that comes with topping the charts and having a &lt;em&gt;huge&lt;/em&gt; influx of players, we were scrambling about trying to make sure everything kept running. Our poor server guy, Camille, got about 3 hours of sleep until we could nail down a deadlock situation that was happening if players did specific things in the tournament. Once you start getting big, the law of large numbers takes over, and what can happen, &lt;em&gt;will&lt;/em&gt; happen.&lt;/p&gt;
&lt;p&gt;All in all, we can't really complain. To be featured at all is a massive gain, and can easily make a company. The fact that we were able to build and release so quickly on Android meant that we were coming to iOS with a pretty solid app that had already proven gameplay, so it was well received.&lt;/p&gt;
&lt;p&gt;One mistake we did make around this time was to reset the League of all existing players. The rationale was that we'd just made a big change in how the League system worked, and we'd start all players off on the same foot, but it obviously could never been the case. Old players figured themselves unjustly treated (players love getting new things, but absolutely &lt;em&gt;hate&lt;/em&gt; losing them, especially if they've felt they've worked for it), and we got slammed in the ratings. New players, on the other hand, were coming in, and right away facing off against seasoned opponents with much higher performance and better balls, so they were getting creamed in games. For them, they didn't know what was going on, so it was just like the game was badly balanced, and I'd say we lost a good number because of it. I think the day after that update when live, I must have answered over 200 emails on the subject :D&lt;/p&gt;
&lt;h2 id=&quot;lessons&quot;&gt;Lessions learned&lt;/h2&gt;
&lt;p&gt;With every game, we learn something new, so here are a few of the take-aways that have stood out for me:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;strong&gt;Unless you have a specific reason not to, focus on Android first.&lt;/strong&gt; While for &lt;em&gt;Masters&lt;/em&gt; the ratio is currently something like 6:1 in termes of iOS:Android, focusing on Android first allowed us to nail a lot of the functionality and bugs before releasing mass market. Once iOS is in the measure, expect your effective dev time to double&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Sometimes all you need is a little presentation change.&lt;/strong&gt; When we first released tournaments, we had maybe 1-2 a day, simply because it was so hard to get 8 players together at once. Players would join, see no-one, then leave. To get into a tournament, it wasn't uncommon to wait for &lt;em&gt;half an hour&lt;/em&gt; or just get very lucky. We were discussing all sorts of solutions, each of which non-negligible in terms of development. In the end however, we went with a simple &quot;Starts in...&quot; countdown that would automatically show over the tournament button on the client for 5 mins every half an hour. No server integration or fancy logic, just a simple label. Result: the next day, we had over 400 tournaments played&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Don't use Scenes:&lt;/strong&gt; I can't count the number of problems we've had because we've used Scenes in our game. If you're just doing a hard switch, then maybe you can get away with it, but the minute you start trying to do transitions, you're opening yourself up to a world of hurt (all special classes (e.g. Android Native) need to be marked &lt;code class=&quot;language-csharp&quot;&gt;DontDestroyOnLoad&lt;/code&gt;, you'll have to create your own destroy-all-old-GameObjects logic, only to discover some wonderful consequences (&lt;a href=&quot;http://forum.unity3d.com/threads/unity-iaps-processpurchase-not-calling-after-purchase-only-after-app-restart.403671/#post-2633514&quot; class=&quot;external&quot;&gt;UnityIAPs, I'm looking at you&lt;/a&gt;), in an old version of Unity, destroying a Scene cleared out all statics declared while that scene was active, etc)&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;In the quest for performance, you'll find yourself going against the Unity grain as often as not.&lt;/strong&gt; When you first start in Unity, everything is components-this, and components-that. After a while, you see that you've about 10k active objects in your scene and they could all be replaced with simple non-component code. You'll also find that the default shader for 2D sprites is &lt;em&gt;suuuuuuuuper slow&lt;/em&gt;. Write your own. &lt;a href=&quot;https://divillysausages.com/2016/01/21/performance-tips-for-unity-2d-mobile/&quot; class=&quot;external&quot;&gt;I wrote a whole post of different performance tips for Unity&lt;/a&gt;.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Getting featured by Apple is a &lt;em&gt;big deal&lt;/em&gt;.&lt;/strong&gt; Overnight we added maybe 700k players. This was just after our server guy went on holidays and you suddenly find your server falling over under the load, or threading/race conditions/deadlocks now happen with frightening regularity. Sorry for ruining your holiday, Camille :D&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Invest in your support tools.&lt;/strong&gt; As previously mentioned, Logmatic.io soon proved themselves pretty useful in helping find and solve problems, but the other end of the stick is being able to do something about it. With each release, we've beefed up the support tools. Everything happens through restlets for ease of use. Some of the things you should be able to do:
	&lt;ul&gt;
		&lt;li&gt;Search for players by key, ID, nickname, transaction ID. Generally, anything unique. If you're not returning the full Player object, then you should at least have all the important info&lt;/li&gt;
		&lt;li&gt;Gift them hard and soft currency, in-game items, you name it. If they can get it in the game, you need to be able to gift it&lt;/li&gt;
		&lt;li&gt;Change their XP, level, or League. This is also useful when testing, and you have gated content&lt;/li&gt;
		&lt;li&gt;Change their stats, win-loss ratio, medals, trophies. Again, also useful for testing leaderboard/profile display&lt;/li&gt;
		&lt;li&gt;Have a way to transfer/recover accounts, as people &lt;em&gt;will&lt;/em&gt; lose access to theirs, and they'll sooner quit that start all over again&lt;/li&gt;
		&lt;li&gt;Push new content, and reload it into memory without having to restart the server. We can do this with pretty much any of the data objects in the game, so adding/changing/fixing content is relatively easy&lt;/li&gt;
		&lt;li&gt;Assign A/B groups if you have A/B testing&lt;/li&gt;
		&lt;li&gt;Turn on and off features of the game, such as debug logs, A/B testing, access to non-essential features such as leaderboards etc&lt;/li&gt;
	&lt;/ul&gt;&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Know why your players are spending and what they buying.&lt;/strong&gt; Without a doubt, I think the most important feature of the game is the ranking leaderboards. The people that will get the most addicted to your game will be the ones at the top, and they'll generally make repeated purchases to keep their advantage&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Analytics and metrics are super important.&lt;/strong&gt; While there are a lot of debates over gut- vs data- driven game design, you can't deny the usefulness of seeing the immediate results of your choices. At the very least your data should be set up at a cohort level, so you can effectively measure if you've improved in the right areas.&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Be careful touching monetisation.&lt;/strong&gt; Early on in development we modified the economy of the game to make things cheaper. Woah boy, was that a mistake. Aside from tanking our revenue to about a third of 
what it was, we'd never gotten so many emails. &lt;em&gt;People were complaining that things were cheaper.&lt;/em&gt; Once you think about it, it makes sense: those that have purchased, especially just before the update went live, immediately had their purchase devalued. After the update, the same purchase would net you about twice the amount of in-game items. This was especially bad with players that had made repeat purchases, as they felt that everything they'd spent was now worthless, even though they were technically our most valued customers. Interestingly enough, a small percentage of players were also angry because &lt;em&gt;it now let other players reach their level easier&lt;/em&gt;. When the economy is tight and IAPs are relatively expensive, purchasing something gives you a distinct advantage (better equipment etc), so some players were enjoying topping the charts simply because their purchases put them out of reach of most players. It's something to think about.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;play&quot;&gt;Play Microgolf Masters&lt;/h2&gt;
&lt;p&gt;Microgolf Masters is available on both &lt;a href=&quot;https://itunes.apple.com/app/id1045436888&quot; class=&quot;external&quot;&gt;iOS&lt;/a&gt; and &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.triplefun.Masters&quot; class=&quot;external&quot;&gt;Android&lt;/a&gt;, so go give it a shot. Once you're in on it, it's very competitive. We've got the whole floor where we work playing it after lunch and 4-player mode is genuinely fun when you're all together.&lt;/p&gt;
&lt;p&gt;You can also check out some &lt;a href=&quot;https://divillysausages.com/2016/06/05/microgolf-masters-images/&quot;&gt;images&lt;/a&gt; of the project.&lt;/p&gt;
&lt;a href=&quot;https://divillysausages.com/2016/06/05/microgolf-masters-images/&quot; class=&quot;external&quot;&gt;&lt;img src=&quot;https://divillysausages.com/files/mastersImages/home.jpg&quot; alt=&quot;Playing Microgolf Masters&quot; width=&quot;320&quot; height=&quot;529&quot; /&gt;&lt;/a&gt;</description>
        <pubDate>Sat, 04 Jun 2016 23:40:00 +0000</pubDate>
        <link>https://divillysausages.com/2016/06/05/microgolf-masters/</link>
        <guid isPermaLink="true">https://divillysausages.com/2016/06/05/microgolf-masters/</guid>
      </item>
    
      <item>
        <title>Microgolf Masters Images</title>
        <description>&lt;div id=&quot;alsoLinks&quot;&gt;
	&lt;ul&gt;
		&lt;li&gt;&lt;a href=&quot;https://divillysausages.com/2015/06/05/microgolf-masters/&quot;&gt;Microgolf Masters&lt;/a&gt;&lt;/li&gt;
		&lt;li&gt;Images&lt;/li&gt;
	&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;Some images from Microgolf Masters, available for iOS and Android.&lt;/p&gt;
&lt;div id=&quot;microgolf&quot;&gt;&lt;/div&gt;
&lt;script type=&quot;text/javascript&quot;&gt;picViewer(&quot;masters&quot;, &quot;microgolf&quot;);&lt;/script&gt;</description>
        <pubDate>Sat, 04 Jun 2016 23:10:00 +0000</pubDate>
        <link>https://divillysausages.com/2016/06/05/microgolf-masters-images/</link>
        <guid isPermaLink="true">https://divillysausages.com/2016/06/05/microgolf-masters-images/</guid>
      </item>
    
      <item>
        <title>I'm an author!</title>
        <description>&lt;p&gt;Things have been a bit quiet on the site at the minute, as aside from working on Microgolf Masters (which I'll write about soon), I wrote a book!&lt;/p&gt;
&lt;p&gt;It's called &lt;a href=&quot;https://damianconnolly.com/books/shepherds_awakening/&quot; class=&quot;external&quot;&gt;Shepherds: Awakening&lt;/a&gt; and it'll be out on Amazon soon in eBook, then soft-cover formats.&lt;/p&gt;
&lt;p&gt;To go with that, I've created another site, &lt;a href=&quot;https://damianconnolly.com/&quot; class=&quot;external&quot;&gt;damianconnolly.com&lt;/a&gt; to act as my author persona (this being my developer one). I'd originally grabbed the domain a long time ago and it was serving to redirect to this site, but I figured it was best to separate out the two seeing as the perspective audiences could be quite different.&lt;/p&gt;
&lt;img src=&quot;https://divillysausages.com/files/dcDotCom.png&quot; alt=&quot;damianconnolly.com&quot; width=&quot;800&quot; height=&quot;493&quot; /&gt;
&lt;p&gt;On a side note, a big shout-out to my hosting provider &lt;a href=&quot;http://www.siteground.com/recommended?referrer_id=22257&quot; class=&quot;external&quot;&gt;SiteGround&lt;/a&gt;, who once again have been a huge help. When I had a question, I opened a support ticket, and literally &lt;em&gt;9 minutes&lt;/em&gt; laters, they'd answered and fixed it.&lt;/p&gt;
&lt;p&gt;I'm currently setting up a mailing list, so if you'd like to get yourself an advanced reading copy of &lt;a href=&quot;https://damianconnolly.com/books/shepherds_awakening/&quot; class=&quot;external&quot;&gt;Shepherds: Awakening&lt;/a&gt; for &lt;strong&gt;FREE&lt;/strong&gt;, then head on over to the site!&lt;/p&gt;</description>
        <pubDate>Mon, 16 May 2016 17:06:00 +0000</pubDate>
        <link>https://divillysausages.com/2016/05/16/im-an-author/</link>
        <guid isPermaLink="true">https://divillysausages.com/2016/05/16/im-an-author/</guid>
      </item>
    
      <item>
        <title>Leaving Jekyll behind</title>
        <description>&lt;p&gt;&lt;strong&gt;Update 23/01/2023:&lt;/strong&gt; &lt;em&gt;After about 6 years of leaving this project as-is (I haven't been very active updating the site 😂), I finally wanted to get the finger out and make the site automatically build and release whenever I pushed a commit, so I put on my big-boy pants and spent the weekend updating all the dependencies, removing those that were no longer necessary, and generally turning it into a project that can be run via CI/CD. See the section at the end for details.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A while ago, I posted on how I was &lt;a href=&quot;https://divillysausages.com/2015/03/29/leaving-drupal-behind/&quot;&gt;moving away from Drupal towards Jekyll&lt;/a&gt;, thus changing from a dynamic site to a static one. Well, over Christmas, I upgraded my 8 year old laptop for a spanking new &lt;a href=&quot;http://www.dell.com/ie/p/xps-13-9350-laptop/pd&quot; class=&quot;external&quot;&gt;Dell XPS 13&lt;/a&gt; (which is great btw) and in the process of reinstalling all my programs, getting my different dev environments set up, and generally keeping my computer as fluff-free as possible, I took a good look at what was actually necessary.&lt;/p&gt;
&lt;p&gt;Jekyll uses both Ruby and Python. I had originally installed Ruby a long time ago to test some Ruby on Rails stuff, back when it was the hot stuff, so it influenced my decision to install Jekyll in the first place. For my new laptop, I didn't want to install both languages just to generate my site, and firing up my old laptop just to do a build seemed like a pain (once you go SSD, you never go back), so something new was in order.&lt;/p&gt;
&lt;h2 id=&quot;heckle&quot;&gt;Enter heckle&lt;/h2&gt;
&lt;p&gt;I &lt;em&gt;do&lt;/em&gt; do Node.js development though, and in searching around for a Node.js Jekyll replacement, I came across &lt;a href=&quot;https://github.com/marijnh/heckle&quot; class=&quot;external&quot;&gt;heckle&lt;/a&gt;, by &lt;a href=&quot;https://github.com/marijnh&quot; class=&quot;external&quot;&gt;marijnh&lt;/a&gt;. It seemed pretty straight-forward and did everything I was looking for, with the exception that it uses Mold as a template language instead of Liquid. Hmmm...&lt;/p&gt;
&lt;h2 id=&quot;liquid-node&quot;&gt;Enter liquid-node&lt;/h2&gt;
&lt;p&gt;Hunting around some more, I came across &lt;a href=&quot;https://github.com/sirlantis/liquid-node&quot; class=&quot;external dead-link&quot;&gt;liquid-node&lt;/a&gt; (changed to &lt;a href=&quot;https://github.com/docs/liquid&quot; class=&quot;external&quot;&gt;liquid&lt;/a&gt;), by &lt;a href=&quot;https://github.com/sirlantis&quot; class=&quot;external&quot;&gt;sirlantis&lt;/a&gt;. Liquid-node is a port of the Liquid template engine to Node.js, using Promises for all the asyncy stuff. Two points stood out on the &lt;em&gt;features&lt;/em&gt; panel: &quot;&lt;em&gt;Based on original Ruby code&lt;/em&gt;&quot; and &quot;&lt;em&gt;High test coverage&lt;/em&gt;&quot;. Seems like it would do the job just nicely.&lt;/p&gt;
&lt;h2 id=&quot;jekylljs&quot;&gt;JekyllJS&lt;/h2&gt;
&lt;p&gt;I started a project (TypeScript), based off of heckle, using liquid-node, and named it &lt;a href=&quot;https://bitbucket.org/divillysausages/jekylljs&quot; class=&quot;external&quot;&gt;JekyllJS&lt;/a&gt;, the idea being that you could take your existing Jekyll site, run it through JekyllJS, and it'd produce the same content.&lt;/p&gt;
&lt;p&gt;All in all, development went surpisingly smoothly. I found another Node.js module, &lt;a href=&quot;https://github.com/nodeca/js-yaml&quot; class=&quot;external&quot;&gt;js-yaml&lt;/a&gt; to handle reading the &lt;em&gt;_config.yml&lt;/em&gt; and each post's frontMatter, and with heckle's code as a reference, I was able to get something up and running pretty quickly.&lt;/p&gt;
&lt;p&gt;Until I hit an &lt;code class=&quot;language-bash&quot;&gt;Unknown tag: &quot;{% highlight %}&quot;&lt;/code&gt; Exception anyway. I guess Liquid has been updated since the Node.js port happened.&lt;/p&gt;
&lt;h2 id=&quot;code&quot;&gt;Highlighting code&lt;/h2&gt;
&lt;p&gt;I have a lot of code samples across my site, and you can set up Jekyll to use Pygments or Rouge to handle this for you. I didn't find a Node.js equivalent, so I settled for &lt;a href=&quot;https://highlightjs.org/&quot; class=&quot;external&quot;&gt;highlight.js&lt;/a&gt;, which happily allows itself to be installed as a Node.js module to allow for server-side pre-rendering (as opposed to each client highlighting the code on each page load).&lt;/p&gt;
&lt;p&gt;Liquid-node allows you to write your own custom tags, so I wrote one for the &lt;code class=&quot;language-liquid&quot;&gt;{% highlight %}&lt;/code&gt; tag that takes the chunk of text, and pipes it through highlight.js. The results are obviously different from what Pygments or Rouge generate, but I can live with the difference - as an added bonus, the CSS is smaller.&lt;/p&gt;
&lt;h2 id=&quot;differences&quot;&gt;Other differences&lt;/h2&gt;
&lt;strike&gt;&lt;p&gt;I also had to hack a patch for the &lt;code class=&quot;language-liquid&quot;&gt;{% include %}&lt;/code&gt; tag, as the liquid-node version doesn't allow for tokens in order to use dynamic includes, which I use for adding comments and files to posts. The patch doesn't handle &lt;a href=&quot;http://jekyllrb.com/docs/templates/#includes&quot; class=&quot;external&quot;&gt;assigning variables&lt;/a&gt;, but there's &lt;a href=&quot;https://github.com/sirlantis/liquid-node/issues/37&quot; class=&quot;external&quot;&gt;an issue open for it&lt;/a&gt;, so maybe it'll happen in the future. It's included at the end of the post (as liquid-node is written in coffeescript and I don't know that). Just replace the &lt;em&gt;include.js&lt;/em&gt; file in the &lt;em&gt;node_modules/liquid-node/lib/liquid/tags/&lt;/em&gt; folder after all the dependencies are installed.&lt;/p&gt;&lt;/strike&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/docs/liquid/issues/42&quot; class=&quot;external&quot;&gt;It looks like this has been fixed&lt;/a&gt;, though as I changed how I was doing things, it was no longer an issue though. I did have to rewrite &lt;code class=&quot;language-liquid&quot;&gt;{% include_if_exists %}&lt;/code&gt; tag though, which I used to automatically include files and comments for posts when the relevant include exists. In any case, as &lt;code&gt;liquid&lt;/code&gt; is no longer actively maintained, you're probably better off moving to &lt;a href=&quot;https://github.com/harttle/liquidjs&quot; class=&quot;external&quot;&gt;liquidjs&lt;/a&gt;, but I didn't have the heart to changing rendering engines after getting everything working again 😅. The update to the &lt;code class=&quot;language-liquid&quot;&gt;{% include %}&lt;/code&gt; tag does mean that you need to drop the filetype from your includes. So &lt;code class=&quot;language-liquid&quot;&gt;{% include footer.html %}&lt;/code&gt; becomes &lt;code class=&quot;language-liquid&quot;&gt;{% include footer %}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;When I added Markdown support using the &lt;a href=&quot;https://github.com/chjj/marked&quot; class=&quot;external&quot;&gt;marked node.js library&lt;/a&gt; I found that it was escaping all quotes for all reasons, including normal text (&quot;don't&quot;, &quot;can't&quot;, &quot;I'm&quot;, etc). I don't particularly like this behaviour (it's still valid HTML though), so I added a fix around it. Just replace the &lt;em&gt;marked.js&lt;/em&gt; file - included at the end of the post - in the &lt;em&gt;node_modules/marked/lib/&lt;/em&gt; folder if you want to do the same.&lt;/p&gt;
&lt;p&gt;Another difference is the way that dates are parsed. Normally in your frontMatter, you'd specify a date like so:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;date:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2016-01-23 19:50:00&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;+0100&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and Jekyll would treat this date as-is, i.e. +1hr after GMT. JavaScript however, takes into account Romance Daylight Time, which handles the extra hour for daylights time. This means that if you're in the EU and you use CET during the winter, you'll use CEST during the summer, or during the last Sunday of March to the last Sunday of October, and &lt;code class=&quot;language-js&quot;&gt;+0100&lt;/code&gt; will act as &lt;code class=&quot;language-js&quot;&gt;+0200&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; d1 = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Date&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;2016-03-01 00:00:00 +0100&amp;quot;&lt;/span&gt; ) &lt;span class=&quot;hljs-comment&quot;&gt;// Tue Mar 01 2016 00:00:00 GMT+0100 (Romance Standard Time)&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; d2 = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Date&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;2016-04-01 00:00:00 +0100&amp;quot;&lt;/span&gt; ) &lt;span class=&quot;hljs-comment&quot;&gt;// Fri Apr 01 2016 01:00:00 GMT+0200 (Romance Daylight Time)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Depending on when you've published your post (i.e. the &lt;code class=&quot;language-yaml&quot;&gt;date&lt;/code&gt;), this behaviour can change the rendered day, or even month, which may or may not be a problem for you. I've not found a way around this behaviour, but if it's affecting you, you can try changing the &lt;code class=&quot;language-yaml&quot;&gt;date&lt;/code&gt; set in your frontMatter.&lt;/p&gt;
&lt;p&gt;As for &lt;a href=&quot;http://jekyllrb.com/docs/frontmatter/#predefined-global-variables&quot; class=&quot;external&quot;&gt;categories&lt;/a&gt;, I don't use them in my site, so I've no idea how they're supposed to render, so I didn't include them in JekyllJS.&lt;/p&gt;
&lt;p&gt;The final thing I came across was when using &lt;code class=&quot;language-liquid&quot;&gt;{% if %}&lt;/code&gt; checks. I was checking to see if a variable was set in one of my templates using &lt;code class=&quot;language-liquid&quot;&gt;{% if someVar == null %}&lt;/code&gt;. For liquid-node, I had to change this to &lt;code class=&quot;language-liquid&quot;&gt;{% if someVar == undefined %}&lt;/code&gt; or simply &lt;code class=&quot;language-liquid&quot;&gt;{% if someVar %}&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;config&quot;&gt;The JekyllJS config&lt;/h2&gt;
&lt;p&gt;JekyllJS comes with a &lt;a href=&quot;https://github.com/lorenwest/node-config&quot; class=&quot;external&quot;&gt;simple config&lt;/a&gt; file, &lt;em&gt;config/default.json&lt;/em&gt;, meaning you can change a few things without having to re-compile the project. It looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;src&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;404&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;/404/&amp;quot;&lt;/span&gt;
	&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;meta&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;keywords&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;default,keywords,here&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;description&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;default description&amp;quot;&lt;/span&gt;
	&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;opengraph&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;fb:admins&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;XXXXX&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;og:type&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;article&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;og:image&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;/img/open_graph.png&amp;quot;&lt;/span&gt;
	&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;highlight&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;parentClassName&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;highlight&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;shouldWrap&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;server&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;hljs-number&quot;&gt;4000&lt;/span&gt;
	&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which translates as&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;src.404&lt;/code&gt;: the path for the file to serve as the 404 page, if you have one&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;meta.keywords&lt;/code&gt;: the default meta keywords to add to each page if the page doesn't specify any. Set to null or leave out to ignore this&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;meta.description&lt;/code&gt;: the default meta description to add to each page if the page doesn't specify one. If null or left out, then the description in &lt;em&gt;_config.yml&lt;/em&gt; will be used (if set)&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;opengraph.fb:admins&lt;/code&gt;: the ID of the Facebook user that you want to associate as the admin of the page. If null, then it's not added to the page&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;opengraph.og:type&lt;/code&gt;: the default type for each page, unless overwritten. If null and not overwritten, then it's not added to the page&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;opengraph.og:image&lt;/code&gt;: the image to use as the default OpenGraph share. If null and not overwritten, then it's not added to the page&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;highlight.parentClassname&lt;/code&gt;: the classname for the parent node for highlighted code. Use &lt;code class=&quot;language-json&quot;&gt;highlight&lt;/code&gt; to keep with the Jekyll generated code, or &lt;code class=&quot;language-json&quot;&gt;hljs&lt;/code&gt; to use the highlight.js version&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;highlight.shouldWrap&lt;/code&gt;: Should we wrap our generated code in a div? Use &lt;code class=&quot;language-json&quot;&gt;true&lt;/code&gt; to keep with the Jekyll generated code, or &lt;code class=&quot;language-json&quot;&gt;false&lt;/code&gt; to use the highlight.js version (in which case the &lt;code class=&quot;language-json&quot;&gt;highlight.parentClassname&lt;/code&gt; will be applied to the &lt;code-language-html&gt;code&lt;/code-language-html&gt; tag&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-json&quot;&gt;server.port&lt;/code&gt;: the port to use when serving the site&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Create this config file in the root your site project (e.g. as &lt;code&gt;_siteGenConfig.json&lt;/code&gt;). When executing JekyllJS, the code will take that folder as the site path to start discovering and parsing files.&lt;/p&gt;
&lt;h2 id=&quot;running&quot;&gt;Running JekyllJS&lt;/h2&gt;
&lt;p&gt;I compared the generated site using an SVN diff, and aside from the Date and difference in highlighted code, it matches out. If you try it and see something, let me know.&lt;/p&gt;
&lt;strike&gt;&lt;p&gt;As for running it, you can generate your site using&lt;/p&gt;&lt;/strike&gt;
&lt;strike&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node app.js | &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;./node_modules/.bin/bunyan&amp;quot;&lt;/span&gt; -o short&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/strike&gt;
&lt;strike&gt;&lt;p&gt;from a command line. The bit at the end is just running the output through Bunyan, which is the logging lib I use in the project.&lt;/p&gt;&lt;/strike&gt;
&lt;strike&gt;&lt;p&gt;If you want to test your site, JekyllJS will also serve it for you via&lt;/p&gt;&lt;/strike&gt;
&lt;strike&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node app.js serve | &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;./node_modules/.bin/bunyan&amp;quot;&lt;/span&gt; -o short&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/strike&gt;
&lt;strike&gt;&lt;p&gt;which will start a local webserver under the port set in the config. It'll also handle 404 errors with the specified URL.&lt;/p&gt;&lt;/strike&gt;
&lt;p&gt;I've completely changed how to run JekyllJS and generate your site in lieu of it supporting CI/CD environments. Previously, you executed code from the JekyllJS folder, which didn't make any sense. Now, you create your config file in the root path of your project, then create a new npm project and install it as a dependency:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install --save git@bitbucket.org:divillysausages/jekylljs.git&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once that's done, you can create scripts that will generate and build your site, and optionally serve it locally, using &lt;code&gt;npx&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;scripts&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;build&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;npx jekylljs ./_siteGenConfig.json&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;serve&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;npx jekylljs ./_siteGenConfig.json serve&amp;quot;&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When running this in a CI/CD environment, simply deploy the &lt;code&gt;_site&lt;/code&gt; folder to S3.&lt;/p&gt;
&lt;h2 id=&quot;get&quot;&gt;Get JekyllJS&lt;/h2&gt;
&lt;p&gt;You can find the code &lt;a href=&quot;https://bitbucket.org/divillysausages/jekylljs&quot; class=&quot;external&quot;&gt;over on Bitbucket&lt;/a&gt;, and you can install the dependencies using &lt;code class=&quot;language-bash&quot;&gt;npm install&lt;/code&gt;. Also on site is the liquid code that I use to generate my &lt;a href=&quot;https://divillysausages.com/tags/&quot;&gt;tags&lt;/a&gt;, &lt;a href=&quot;https://divillysausages.com/archive/&quot;&gt;archive&lt;/a&gt;, and &lt;a href=&quot;https://divillysausages.com/feed.xml&quot;&gt;feed.xml&lt;/a&gt; if it's something interesting to you.&lt;/p&gt;</description>
        <pubDate>Sun, 24 Jan 2016 18:50:00 +0000</pubDate>
        <link>https://divillysausages.com/2016/01/24/leaving-jekyll-behind/</link>
        <guid isPermaLink="true">https://divillysausages.com/2016/01/24/leaving-jekyll-behind/</guid>
      </item>
    
      <item>
        <title>Performance tips for Unity 2d mobile</title>
        <description>&lt;blockquote&gt;&lt;strong&gt;Update 09/2018:&lt;/strong&gt; As this is one of my more popular posts, I've updated it with even more tips that I've learned in the meantime. At the time of writing the update, we're using Unity 2018.2.&lt;/blockquote&gt;
&lt;p&gt;At the minute I'm looking into performance with &lt;a href=&quot;https://divillysausages.com/2016/06/05/microgolf-masters/&quot;&gt;our latest game&lt;/a&gt;, so I figured I'd gather together all the different tips that I ended up using to get our game running back up at 60fps.&lt;/p&gt;
&lt;p&gt;You see, it &lt;em&gt;used to&lt;/em&gt; run at 60fps no problem, but as development continued and the Unity versions piled up, occasional hiccups became more and more obvious, so it was time to dig down and figure out what was going on behind the scenes. &lt;strike&gt;We're currently Android only (as it's a &lt;em&gt;ton&lt;/em&gt; quicker to iterate and get everything working as it should), but&lt;/strike&gt; The latest Unity version (5.3 as of - original - writing) seemed to exacerbate the performance problems on this.&lt;/p&gt;
&lt;h2 id=&quot;game&quot;&gt;Our game&lt;/h2&gt;
&lt;p&gt;There's no one silver bullet to optimisation, and what works for one person mightn't work for another, so with this in mind, this is our game constitutes of:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;It's a 2D golf game, for mobile, using basic Sprites&lt;/li&gt;
	&lt;li&gt;It uses multiple scenes, and the new Unity UI&lt;/li&gt;
	&lt;li&gt;In the game scene, the world map is built with individual tiles
		&lt;blockquote&gt;While the levels are still built with individual tiles, we now render all the static tiles to a single &lt;code class=&quot;language-csharp&quot;&gt;RenderTexture&lt;/code&gt;, which sharply reduces the number of draw calls&lt;/blockquote&gt;
	&lt;/li&gt;
	&lt;li&gt;It's multiplayer, so you get CPU spikes as messages come and go
		&lt;blockquote&gt;We've since moved the compression/decompression and serialisation/deserialisation to different threads, so while there's still the issue of memory etc, there's less of an impact on the framerate. In the long term, we'd like to move to Unity JSON and the built-in compression etc in .Net 4.5, but it requires some work&lt;/blockquote&gt;
	&lt;/li&gt;
	&lt;li&gt;We don't use any lights, multiple cameras, etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It goes without saying that the performance issues were found using the profiler, so if you have it, it's something you should be using.&lt;/p&gt;
&lt;p&gt;Also, these tips are mainly focused on 2D. Your mileage may vary.&lt;/p&gt;
&lt;h2 id=&quot;basics&quot;&gt;The dumb stuff&lt;/h2&gt;
&lt;p&gt;Let's get the dumb stuff out of the way first. These are the simple things you need to do, and don't need much discussion.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Set your &lt;code class=&quot;language-csharp&quot;&gt;Application.targetFrameRate&lt;/code&gt; to 60. By default on mobile, it's 30&lt;/li&gt;
	&lt;li&gt;Otherwise you can turn on &lt;code class=&quot;language-csharp&quot;&gt;vSyncCount&lt;/code&gt; in the quality settings. Apparently, on some Android devices, with Android 5, you can render at more than 60fps, so if you don't force vSync, you run the risk of an unresponsive device, as it's rendering too quickly, so it's possible that future versions of Unity will force this
	&lt;blockquote&gt;&lt;p&gt;I originally misunderstood the first two points. I'd assumed that &lt;code class=&quot;language-csharp&quot;&gt;Application.targetFrameRate&lt;/code&gt; was the &lt;em&gt;&quot;code way&quot;&lt;/em&gt; of enforcing vSync. Because we were switching from 60fps in-game to 30fps in the menus (good for battery life), we switched off vSync and just used &lt;code class=&quot;language-csharp&quot;&gt;Application.targetFrameRate&lt;/code&gt;. However, they're two very different things, and what ended up happened was that the CPU was issuing frames faster than the GPU could handle them, as we ended up with intermittent stuttering. Enforcing vSync makes the CPU wait. Basically, the framerate went from &lt;a href=&quot;https://imgur.com/a/lE0LedF&quot; class=&quot;external&quot;&gt;https://imgur.com/a/lE0LedF&lt;/a&gt; to this &lt;a href=&quot;https://imgur.com/a/EPq6q7T&quot; class=&quot;external&quot;&gt;https://imgur.com/a/EPq6q7T&lt;/a&gt;.&lt;/p&gt;
	&lt;p&gt;Now, I'd &lt;strong&gt;highly&lt;/strong&gt; recommend enforcing vSync. Unity &lt;a href=&quot;https://forum.unity.com/threads/gfx-presentframe-taking-4x-as-long-but-only-for-intermittent-periods.538254/#post-3584222&quot; class=&quot;external&quot;&gt;doesn't recommend using both vSync and Application.targetFrameRate at the same time&lt;/a&gt; though, as it could lead to CPU frame drift. If you'd like the full story, you can &lt;a href=&quot;https://forum.unity.com/threads/gfx-presentframe-taking-4x-as-long-but-only-for-intermittent-periods.538254/&quot; class=&quot;external&quot;&gt;find the thread here&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;/li&gt;
	&lt;li&gt;When testing, test on an actual device - you can run on the desktop to make sure it doesn't crash, but it won't tell you a tap about what's happening on the device&lt;/li&gt;
	&lt;li&gt;If you're not using &lt;a href=&quot;https://en.wikipedia.org/wiki/Object_pool_pattern&quot; class=&quot;external&quot;&gt;object pools&lt;/a&gt;, use object pools&lt;/li&gt;
	&lt;li&gt;Use something like &lt;a href=&quot;https://www.codeandweb.com/texturepacker&quot; class=&quot;external&quot;&gt;TexturePacker&lt;/a&gt; to pack your images into one, so you can get &lt;a href=&quot;http://docs.unity3d.com/Manual/DrawCallBatching.html&quot; class=&quot;external&quot;&gt;Dynamic Batching&lt;/a&gt; going&lt;/li&gt;
	&lt;li&gt;Remove empty callbacks from components. If they're there, even if there's nothing in them, they'll get called, which slows you down&lt;/li&gt;
	&lt;li&gt;Cache components you use regularly. Ditto for GameObjects. No &lt;code class=&quot;language-csharp&quot;&gt;GameObject.Find()&lt;/code&gt; when you can get away with it, especially not in &lt;code class=&quot;language-csharp&quot;&gt;Update/FixedUpdate&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;Disable/Disactivate GameObjects/Components when you don't need them, especially if they have &lt;code class=&quot;language-csharp&quot;&gt;Update/FixedUpdate&lt;/code&gt; calls&lt;/li&gt;
	&lt;li&gt;If using physics, don't move &lt;code class=&quot;language-csharp&quot;&gt;Rigidbody2D&lt;/code&gt; yourself (i.e. directly setting the postion), &lt;em&gt;especially&lt;/em&gt; if they've been marked as static. The physics engine doesn't like and will let you know. If you need to, disable the GameObject, set the position using the &lt;code class=&quot;language-csharp&quot;&gt;Transform&lt;/code&gt; (there's currently a bug when setting the &lt;code class=&quot;language-csharp&quot;&gt;Rigidbody2D.position&lt;/code&gt; while the GameObject is deactivated), then re-activate it&lt;/li&gt;
	&lt;li&gt;Watch your poly count. We had a pretty high-resolution ball, which was fine with 1/2 players, but we'll be releasing soon with 1v3/teams, so all of a sudden your poly count doubles&lt;/li&gt;
	&lt;li&gt;If you have lights in your project, but don't need shadows, or don't need shadows on everything, turn off the &lt;em&gt;Cast shadows/Receive shadows&lt;/em&gt; checkboxes on the relevant &lt;code class=&quot;language-csharp&quot;&gt;MeshRenderers&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;Don't use Coroutines when a simple timer will do the trick. Coroutines are &lt;em&gt;heavy&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After that comes all the things that require a bit more effort on your part.&lt;/p&gt;
&lt;p&gt;Let's go a bit deeper.&lt;/p&gt;
&lt;h2 id=&quot;gc&quot;&gt;Control the garbage collector&lt;/h2&gt;
&lt;p&gt;One of the worst things that can happen in the middle of playing is the GC kicking in. You'll usually notice if it looks like your rendering is skipping every so often.&lt;/p&gt;
&lt;p&gt;Now the important thing to remember is that the GC only kicks in &lt;em&gt;when the application requests more memory&lt;/em&gt;. The default behaviour is to try and free some up before asking for more from the OS. This means that if you instantiate everything you need up front, and you have no other memory allocations during play, you'll never have a problem with the GC.&lt;/p&gt;
&lt;p&gt;If you're using object pools, easy, right?&lt;/p&gt;
&lt;p&gt;The killer is the hidden allocations. These can be big or small, but they build up until eventually your application requests another memory page, resulting in the GC crapping all over your game.&lt;/p&gt;
&lt;p&gt;Now you might see something like a few hundred bytes every frame, and think, &quot;No big deal&quot;. At 60fps, 100 bytes becomes ~6KB a second, ~350KB a minute. It all adds up.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Using a &lt;code class=&quot;language-csharp&quot;&gt;foreach&lt;/code&gt; will allocate 32B of memory for the enumerator. While it might not sound like much, a couple of these inside an &lt;code class=&quot;language-csharp&quot;&gt;Update/FixedUpdate&lt;/code&gt; and you'll feel it&lt;/li&gt;
	&lt;li&gt;Adding or removing a callback from a delegate will allocate 104B of memory. I've not seen a way around this outside of directly calling the method (introduces dependencies etc), or, depending on your use case (e.g. UI needs to show the player's health), you can use &lt;a href=&quot;https://www.youtube.com/watch?v=6vmRwLYWNRo&quot; class=&quot;external&quot;&gt;ScriptableObjects&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Strings allocate memory as you create them. Keep a special eye out for concatenation - &lt;code class=&quot;language-csharp&quot;&gt;&quot;Hello &quot; + &quot;world&quot;&lt;/code&gt; - as in this case you're actually allocating 3 strings here, one for each part, and one for the final result (strings are immutable). This is pretty easy to do if your calling &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log()&lt;/code&gt; a lot. Using &lt;code class=&quot;language-csharp&quot;&gt;StringBuilder&lt;/code&gt; or the &lt;code class=&quot;language-csharp&quot;&gt;String.Format()&lt;/code&gt; where you need to&lt;/li&gt;
	&lt;li&gt;Know the difference between classes and structs and when to use them. Classes are created on the heap, while structs are passed by value. Be especially careful of Lists of structs&lt;/li&gt;
	&lt;li&gt;If you see &lt;code class=&quot;language-csharp&quot;&gt;SendMouseEvents&lt;/code&gt; showing up, then you're hitting a dumb bug with a &lt;code class=&quot;language-csharp&quot;&gt;GetComponent()&lt;/code&gt; call. &lt;a href=&quot;https://github.com/Unity-Technologies/UnityCsReference/blob/master/Runtime/Export/MouseEvents.cs#L55&quot; class=&quot;external&quot;&gt;There's some code in the MouseEvents class&lt;/a&gt; that does a lookup for a &lt;code class=&quot;language-csharp&quot;&gt;GUILayer&lt;/code&gt; component on your camera. Normally &lt;code class=&quot;language-csharp&quot;&gt;GetComponent()&lt;/code&gt; calls are cached, however Unity can't cache it when it's &lt;code class=&quot;language-csharp&quot;&gt;null&lt;/code&gt;, meaning you get hit with it every frame. Solution: add a &lt;code class=&quot;language-csharp&quot;&gt;GUILayer&lt;/code&gt; to every camera in the scene. You'll get a warning that it's a deprecated class, but there ya go&lt;/li&gt;
	&lt;li&gt;If you see &lt;code class=&quot;language-csharp&quot;&gt;IMGUI&lt;/code&gt; showing up in your GC panel, that's the legacy Unity UI code. Comment out any &lt;code class=&quot;language-csharp&quot;&gt;OnGUI&lt;/code&gt; calls that you have running outside of the Editor platform&lt;/li&gt;
	&lt;li&gt;Pre-size any &lt;code class=&quot;language-csharp&quot;&gt;Lists&lt;/code&gt; and &lt;code class=&quot;language-csharp&quot;&gt;Dictionaries&lt;/code&gt;, even if it's a complete guess. The default limit is 0 when it's created, then 4 when something's added to it. Due to the way list resizing works, if you're adding a number of elements, you'll get hit multiple times with the resize-and-copy operation. Print out the max sizes so you get a feel for what you need&lt;/li&gt;
	&lt;li&gt;If you're using JSON, Unity's built-in JSON is &lt;em&gt;miles&lt;/em&gt; ahead of other solutions, such as JSON.Net, both in terms of speed and memory. Use it where you can. There are some downsides, such as having to workaround derived types (deserialise common field (e.g. &lt;code class=&quot;language-csharp&quot;&gt;&quot;classname&quot;&lt;/code&gt;), the create the right class) and not supporting &lt;code class=&quot;language-csharp&quot;&gt;Dictionaries&lt;/code&gt; (though if you know the names of your keys, make it a straight object), but the benefits make up for it&lt;/li&gt;
	&lt;li&gt;Instantiate &lt;code class=&quot;language-csharp&quot;&gt;GameObjects&lt;/code&gt; directly into their parent rather than creating, then adding. It's &lt;em&gt;much&lt;/em&gt; quicker, and uses a lot less memory&lt;/li&gt;
	&lt;li&gt;A lot of APIs can allocate memory in the background. For example, calling &lt;code class=&quot;language-csharp&quot;&gt;Collision2D.contacts&lt;/code&gt; generates a new &lt;code class=&quot;language-csharp&quot;&gt;List&lt;/code&gt; each time. Use &lt;code class=&quot;language-csharp&quot;&gt;GetContacts()&lt;/code&gt; instead. Ditto for &lt;code class=&quot;language-csharp&quot;&gt;Input.touches&lt;/code&gt; and &lt;code class=&quot;language-csharp&quot;&gt;Input.getTouches()&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;Be careful of things like getting/setting &lt;code class=&quot;language-csharp&quot;&gt;GameObject.name&lt;/code&gt;, as it allocates memory with each access, as it's calling native code behind the scenes&lt;/li&gt;
	&lt;li&gt;Similarly, if you need to compare a &lt;code class=&quot;language-csharp&quot;&gt;GameObject.tag&lt;/code&gt; with anything, use &lt;code class=&quot;language-csharp&quot;&gt;GameObject.CompareTag()&lt;/code&gt; rather than a &lt;code&gt;==&lt;/code&gt;, as for the same reason, it's accessing native code&lt;/li&gt;
	&lt;li&gt;If you're not using &lt;a href=&quot;https://docs.unity3d.com/Manual/OcclusionCulling.html&quot; class=&quot;external&quot;&gt;occlusion culling&lt;/a&gt; (possible for a 2D game where everything is on the screen), then turn it off by setting &lt;code class=&quot;language-csharp&quot;&gt;useOcclusionCulling = false&lt;/code&gt; on your &lt;code class=&quot;language-csharp&quot;&gt;Camera&lt;/code&gt; components&lt;/li&gt;
	&lt;li&gt;Beware of unnecessarily heavy code - e.g. checking for duplicates when adding objects to a list. If you're doing this at the start of the game, when you're populating your data structures, and you know there'll be no collisions yet, turn off the check temporarily, as it's a massive waste of time otherwise&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the &lt;em&gt;CPU Usage&lt;/em&gt; of the profiler, you can sort by &lt;em&gt;GC Alloc&lt;/em&gt; column to find out who's allocating and when. Removing as many of these allocations as possible means your game will have a smoother ride. Some will be outside of your control (such as physics engine allocations), but we can live with those.&lt;/p&gt;
&lt;p&gt;In the &lt;em&gt;Memory Area&lt;/em&gt; of the profiler, you can also take a snapshot of the current memory state, which you can use to help you see if there's a memory leak somewhere.&lt;/p&gt;
&lt;p&gt;You can also call the GC yourself, using &lt;code class=&quot;language-csharp&quot;&gt;System.GC.Collect()&lt;/code&gt;. Do this when you can afford to. We do it, for example, at the end of a shot, when the ball has finished moving, but before the next player has taken their turn. We allow a collection here, which lowers the chance of it happening where it would be visible (such as when balls are moving).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://jacksondunstan.com/articles/4644&quot; class=&quot;external&quot;&gt;It's possible to turn off Unity's GC&lt;/a&gt;, but it's undocumented and doesn't work on iOS, so I wouldn't recommend it. You also run the risk of having your app crash because of an out-of-memory error.&lt;/p&gt;
&lt;h2 id=&quot;logging&quot;&gt;Logging&lt;/h2&gt;
&lt;p&gt;In relation to one of the previous points, you'll need to remove any calls to &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log()&lt;/code&gt; etc that you have. This is because:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code class=&quot;language-csharp&quot;&gt;LogStringToConsole()&lt;/code&gt; - the method called to print out your message is &lt;em&gt;super slow&lt;/em&gt;. Even if you're not listening for it (i.e. have &lt;em&gt;adb&lt;/em&gt; or whatever connected), it'll still get called&lt;/li&gt;
	&lt;li&gt;As well as that, when profiling, you need to select &lt;em&gt;Development build&lt;/em&gt;, so the log message also grabs a stack trace (similar to how you'd see it in the Unity editor), which is even slower. You'll see a spike for every message when profiling
		&lt;blockquote&gt;One of the reasons that it's super slow is that it's grabbing a stack trace on both the C# side and the native side. The latest versions of Unity allow you to set this behaviour to &lt;em&gt;None/Script only/Full&lt;/em&gt; for each log type.&lt;/blockquote&gt;
	&lt;/li&gt;
	&lt;li&gt;As well as &lt;em&gt;that&lt;/em&gt;, creating the message to log will entail lots of string concatenation and memory allocation, especially if you've overridden &lt;code class=&quot;language-csharp&quot;&gt;ToString()&lt;/code&gt; (and you should), and your logs are something like &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log( &quot;Player &quot; + player  + &quot; is doing something in game &quot; + game );&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;&quot;No problem&quot;&lt;/em&gt;, you say, &lt;em&gt;&quot;I have my own log function that wraps &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log()&lt;/code&gt;, so I only need to set a &lt;a href=&quot;http://docs.unity3d.com/Manual/PlatformDependentCompilation.html&quot; class=&quot;external&quot;&gt;compilation constant&lt;/a&gt; inside there to early return out and not print anything!&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Sure, that'll solve the call itself, but a call to an empty function will still allocate the memory for the string message passed, giving you eventual garbage collection problems.&lt;/p&gt;
&lt;p&gt;In the end, you have &lt;strike&gt;two&lt;/strike&gt; &lt;strike&gt;three&lt;/strike&gt; &lt;em&gt;four&lt;/em&gt; solutions (James and Max from the comments showed a pretty neat way of removing &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log()&lt;/code&gt; calls):&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Wrap each call to &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log()&lt;/code&gt; in a compilation constant so that the call doesn't exist when you do a production build&lt;/li&gt;
	&lt;li&gt;Before you build, do a &lt;em&gt;Find and Replace&lt;/em&gt; or run a script to comment then all out&lt;/li&gt;
	&lt;li&gt;Use the &lt;a href=&quot;https://msdn.microsoft.com/en-us/library/4xssyw96(v=vs.90).aspx&quot; class=&quot;external&quot;&gt;C# Conditional attribute&lt;/a&gt;. It's essentially a neater, more elegant way of doing conditional compliation. Inside a class, add the attribute to a &lt;code class=&quot;language-csharp&quot;&gt;static void&lt;/code&gt; function. If the define is declared, then the function, &lt;em&gt;and all its calls&lt;/em&gt; are included. If not, it's like they never existed:
	&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#&lt;span class=&quot;hljs-keyword&quot;&gt;define&lt;/span&gt; USE_LOGS // NOTE: changed in 5.5 - see note below&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;using&lt;/span&gt; System;
&lt;span class=&quot;hljs-keyword&quot;&gt;using&lt;/span&gt; System.Diagnostics;

&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Trace&lt;/span&gt;
{
    [&lt;span class=&quot;hljs-meta&quot;&gt;Conditional(&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;USE_LOGS&amp;quot;&lt;/span&gt;)&lt;/span&gt;]
    &lt;span class=&quot;hljs-function&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title&quot;&gt;Msg&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;string&lt;/span&gt; msg&lt;/span&gt;)&lt;/span&gt;
    {
        Debug.Log(msg);
    }
}

&lt;span class=&quot;hljs-comment&quot;&gt;// elsewhere in the code - if USE_LOGS is defined, you&amp;#x27;ll see this output, otherwise,&lt;/span&gt;
&lt;span class=&quot;hljs-comment&quot;&gt;// it&amp;#x27;s stripped from the build&lt;/span&gt;
Trace.Msg( &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Hello world&amp;quot;&lt;/span&gt; );&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;NOTE: I'm leaving the previous code example up for historical reasons, but as of &lt;a href=&quot;https://forum.unity3d.com/threads/conditional-attribute-ignores-defines-in-5-5.447160&quot; class=&quot;external&quot;&gt;Unity 5.5, they've changed the way this works&lt;/a&gt;. Apparently the previous behaviour was a bug, and the &lt;code class=&quot;language-csharp&quot;&gt;#define&lt;/code&gt; now needs to be set where you *call your method*, not where it's declared. Because of the way Unity projects work, this leaves two options: 1) add a &lt;code class=&quot;language-csharp&quot;&gt;#define&lt;/code&gt; in every file, which is awkward, or 2) add your constants in the PlayerSettings &amp;gt; Scripting Define Symbols box. They go in the form &lt;code class=&quot;language-csharp&quot;&gt;USE_LOGS;SOME_OTHER_DEFINE&lt;/code&gt;.&lt;/em&gt;&lt;/li&gt;
	&lt;li&gt;&lt;strong&gt;Update May 2018:&lt;/strong&gt; Max makes a great point in the comments that you can blanket kill &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log&lt;/code&gt; calls by setting the &lt;code class=&quot;language-csharp&quot;&gt;Debug.unityLogger.logEnabled&lt;/code&gt; property to &lt;code class=&quot;language-csharp&quot;&gt;false&lt;/code&gt;. This is great for removing logs in code that you don't control (read: Plugins)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We originally went with the second option as it's easier in terms of dev (as I didn't know about the third one, but we now use that one as it's amazing), and you only need to run it just before a build, then Git revert the changes. On a Mac, you can make a shell script that goes something like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;grep -rl --null &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Debug.Log&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;./Assets/Scripts&amp;quot;&lt;/span&gt; | xargs -0 sed -i &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;s|Debug.Log|//Debug.Log|g&amp;#x27;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which uses &lt;em&gt;grep&lt;/em&gt; to list all the files containing &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log()&lt;/code&gt; calls, and pipes the result to &lt;em&gt;sed&lt;/em&gt; which comments them out, in place.&lt;/p&gt;
&lt;p&gt;There are a few things to bear in mind however:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;You need to be careful about unclosed conditional statements and the like, otherwise:
	&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt;( something )
	Debug.Log( &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Something happened&amp;quot;&lt;/span&gt; );
myObj.Foo();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
can become
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt;( something )
	&lt;span class=&quot;hljs-comment&quot;&gt;//Debug.Log( &amp;quot;Something happened&amp;quot; );&lt;/span&gt;
myObj.Foo();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
which leads to unexpected behaviour. I have another script that pulls out all the logs and the line before, that I can feed to a JavaScript tool that prints any non-wrapped log, so I can fix it (JS is great for building quick, crappy tools)&lt;/li&gt;
	&lt;li&gt;If you're using &lt;code class=&quot;language-csharp&quot;&gt;Application.logMessageReceived&lt;/code&gt; or &lt;code class=&quot;language-csharp&quot;&gt;Application.logMessageReceivedThreaded&lt;/code&gt; (e.g. if you're sending errors to the server), then watch out that you don't comment out the logs that you're interested in (you can tweak the regex to ignore &lt;code class=&quot;language-csharp&quot;&gt;LogError()&lt;/code&gt; calls, for example)&lt;/li&gt;
	&lt;li&gt;Some code uses &lt;code class=&quot;language-csharp&quot;&gt;UnityEngine.Log.Debug()&lt;/code&gt; instead, so check for that as well (and first)&lt;/li&gt;
	&lt;li&gt;If you're using the script, because it's changing the line &lt;em&gt;in place&lt;/em&gt;, it's like typing with the &lt;em&gt;Insert&lt;/em&gt; key selected, so &lt;code class=&quot;language-csharp&quot;&gt;Debug.Log( &quot;Hello&quot; )&lt;/code&gt; actually becomes &lt;code class=&quot;language-csharp&quot;&gt;//Debug.Log&quot;Hello&quot; );&lt;/code&gt;. As you're commenting this out, it doesn't really matter, but it's something to keep in mind if you want to adapt the script for something else&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;NOTE: That whole previous section is great, and I was reasonably pleased with myself when I finally got it working, but just use the third or fourth option :P&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;gi&quot;&gt;Remove Global Illumination&lt;/h2&gt;
&lt;p&gt;This was one of the more recent additions to Unity, and it seems to be turned on by default. As we're not doing anything relating to lighting, you can turn them all off (&lt;em&gt;Precomputed Realtime GI&lt;/em&gt;, &lt;em&gt;Based GI&lt;/em&gt;, &lt;em&gt;Fog&lt;/em&gt;, everything). You can find the option in the &lt;em&gt;Windows &gt; Lighting&lt;/em&gt; panel. You'll need to do it for each of your scenes.&lt;/p&gt;
&lt;p&gt;Depending on your Unity version, you might still see some spikes of GI in the profiler. This is apparently a known bug and is already fixed in an upcoming alpha.&lt;/p&gt;
&lt;h2 id=&quot;gles&quot;&gt;Force OpenGLES 2.0&lt;/h2&gt;
&lt;p&gt;Unity is optimised for OpenGLES 2.0, but will allow you to select 3.0. It's also set by default and kind of hidden in &lt;em&gt;Player Settings &gt; Other Settings&lt;/em&gt;, where you need to untick &lt;em&gt;Automatic Graphics API&lt;/em&gt; and remove OpenGLES3 from the list.&lt;/p&gt;
&lt;p&gt;It'll be selected if your phone supports it, but your performance will tank, so it's better to force 2.0. This one will obviously improve with time though.&lt;/p&gt;
&lt;blockquote&gt;To be transparent, we no longer specifically state OpenGLES 2.0. As far as I can tell, any issues have since been fixed.&lt;/blockquote&gt;
&lt;h2 id=&quot;buffers&quot;&gt;Set your colour/depth buffer&lt;/h2&gt;
&lt;p&gt;Make sure you're using the right sized colour/depth buffer. You shouldn't use the 32bit colour buffer unless you notice banding in your gradients. As our app is pretty much gradient free, it was an easy switch to gain a lot in memory. Ditto for the depth buffer. 2D games shouldn't need much in the way of a depth buffer, so you can bring it right down. You can find these in &lt;em&gt;Player Settings &gt; Resolution and Presentation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;NOTE: Unity 5 &lt;a href=&quot;https://unity3d.com/unity/whats-new/unity-5.0&quot; class=&quot;external&quot;&gt;seems to have done away with the ability to set your depth buffer&lt;/a&gt;. While it seems like the default is 16-bit (though I haven't found any confirmation of that so don't quote me on it), you can now only opt to disable it (and the Stencil buffer) altogether. You can also get a small bit more control using the &lt;a href=&quot;https://docs.unity3d.com/540/Documentation/ScriptReference/DepthTextureMode.html&quot; class=&quot;external&quot;&gt;DepthTextureMode&lt;/a&gt; property of the camera.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;id&quot;&gt;Set your quality settings&lt;/h2&gt;
&lt;p&gt;A few simple tweaks here can go a long way:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;First of all, disable any selections higher than the defaults (&lt;em&gt;Simple&lt;/em&gt; for mobile)&lt;/li&gt;
	&lt;li&gt;Set &lt;em&gt;Pixel Light Count&lt;/em&gt; to 0 as you're probably not using them&lt;/li&gt;
	&lt;li&gt;Set your &lt;em&gt;Texture Quality&lt;/em&gt; to as low as you can get away with. Even &lt;em&gt;Half Res&lt;/em&gt; on mobile is mostly unnoticable, and you'll gain about 75% memory&lt;/li&gt;
	&lt;li&gt;In a similar vein, disable mipmaps if you're not using them (i.e. scaling down), as this'll save you about 33% in memory&lt;/li&gt;
	&lt;li&gt;Turn off &lt;em&gt;Anisotropic Textures&lt;/em&gt; as you probably don't have any textures that you'll see at an oblique angle&lt;/li&gt;
	&lt;li&gt;Turn &lt;em&gt;Anti Aliasing&lt;/em&gt; off or set it to 2 passes, which should be enough to cover any jagged lines (only really noticable if you're using stuff like &lt;code class=&quot;language-csharp&quot;&gt;LineRenderers&lt;/code&gt; anyway, as most of the rest should be images
		&lt;blockquote&gt;Reading more into it, because most (all?) mobile GPUs use &lt;a href=&quot;https://developer.arm.com/graphics/developer-guides/tile-based-rendering&quot; class=&quot;external&quot;&gt;tile-based rendering&lt;/a&gt; like Mali or Vulcan, it means that anti aliasing is essentially free (up to 4x, I think)&lt;/blockquote&gt;
	&lt;/li&gt;
	&lt;li&gt;Turn off &lt;em&gt;Soft Particles&lt;/em&gt;. They're for blending properly into meshes, but that's a 3D problem&lt;/li&gt;
	&lt;li&gt;You can also play with the &lt;em&gt;Resolution Scaling Fixed DPI Factor&lt;/em&gt;, which renders the screen resolution below the device's native resolution, then stretches it up to fill the screen. It can make a big difference on high resolution devices, like the iPhone X&lt;/li&gt;
	&lt;li&gt;Turn off &lt;em&gt;Shadows&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The two big ones are texture quality &lt;strike&gt;and anti aliasing&lt;/strike&gt;. Texture quality directly impacts your memory and how long it takes to upload images to the GPU, &lt;strike&gt;while anti aliasing is applied to the entire screen, so on higher resolution devices it's going to be more expensive&lt;/strike&gt;. The less passes for that, the better.&lt;/p&gt;
&lt;h2 id=&quot;canvas&quot;&gt;Canvas and the EventSystem&lt;/h2&gt;
&lt;p&gt;We're using the new UI (and you should be as well), and every UI element needs a &lt;code class=&quot;language-csharp&quot;&gt;Canvas&lt;/code&gt; parent. By default, one is created when you add a new UI element, and by default, each &lt;code class=&quot;language-csharp&quot;&gt;Canvas&lt;/code&gt; adds it's own &lt;code class=&quot;language-csharp&quot;&gt;Graphics Raycaster&lt;/code&gt; so that you can hittest against it. More than one of these however, and you can expect your performance to tank, so remove any that you don't need. If you have the profiler, you'll see this come up as &lt;code class=&quot;language-csharp&quot;&gt;EventSystem&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;This doesn't seem to be much of an issue any more, however throwing all of your UI elements onto one &lt;code class=&quot;language-csharp&quot;&gt;Canvas&lt;/code&gt; &lt;strong&gt;does&lt;/strong&gt;. It all comes down to redraw, and dirty blocks.&lt;/p&gt;
&lt;p&gt;When a UI element moves or is changed, it's marked as &lt;em&gt;dirty&lt;/em&gt;, in order to be redrawn. A dirty element will also force its parent to rebuild its geometry, and so on up the line. So basically, if you have one moving element (e.g. a timer, or a health bar), you can end up redrawing the entire &lt;code class=&quot;language-csharp&quot;&gt;Canvas&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you have a &lt;em&gt;Sub-canvas&lt;/em&gt;, or a &lt;code class=&quot;language-csharp&quot;&gt;Canvas&lt;/code&gt; nested inside another &lt;code class=&quot;language-csharp&quot;&gt;Canvas&lt;/code&gt;, this will isolate their children from their parent. You can alternatively have multiple canvases.&lt;/p&gt;
&lt;p&gt;Generally speaking, you want all your static UI in one &lt;code class=&quot;language-csharp&quot;&gt;Canvas&lt;/code&gt; and all your dynamic stuff in another (or more, depending on refresh rates).&lt;/p&gt;
&lt;p&gt;&lt;a class=&quot;external&quot; href=&quot;https://unity3d.com/fr/learn/tutorials/topics/best-practices/fundamentals-unity-ui&quot;&gt;The Unity fundamentals on UI&lt;/a&gt; do an excellent job going in-depth with all of this.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Speaking of UI, unless you specifically want to click on an element, make sure to untoggle &lt;code class=&quot;language-csharp&quot;&gt;raycastTarget&lt;/code&gt;, otherwise that object'll be included for interaction. On UI heavy scenes, this can easily add up (we gained 10ms on our shop scene). If you're coming from flash, this is the equivalent of &lt;code class=&quot;language-actionscript&quot;&gt;mouseEnabled&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I've also read that you can get a performance hit from not having a material on UI elements. I didn't particularly notice any difference, but there are some default UI materials included, so you can add them if you want.&lt;/p&gt;
&lt;h2 id=&quot;shaders&quot;&gt;Use the right shaders&lt;/h2&gt;
&lt;p&gt;Speaking of materials, make sure you're using the right shader on your materials. The default &lt;code class=&quot;language-csharp&quot;&gt;Sprite-Default&lt;/code&gt; for 2D sprites handles transparency, which is not ideal. Transparency on mobile is a real buzz kill, especially if you have it on large areas of the screen. On the profiler you'll see this come up as something like &lt;code class=&quot;language-csharp&quot;&gt;renderForwardTransparent()&lt;/code&gt;. As we're using tiles, changing the default for a lot of them to a basic unlit brought the rendering time down significantly.&lt;/p&gt;
&lt;p&gt;This will obviously only work for solid sprites (i.e. square), and the fact of having a different material will split up Dynamic Batching, but the gains are worth it.&lt;/p&gt;
&lt;h2 id=&quot;text&quot;&gt;Watch out for Unity UI Text&lt;/h2&gt;
&lt;p&gt;Unity UI Text is pretty powerful, but it's not the fastest, and you need to be very careful how you use it. We have a lot of text on the screen, with different effects such as outline, and it was hitting performance bad. We've recently made the switch to the &lt;a href=&quot;https://www.assetstore.unity3d.com/en/#!/content/17662&quot; class=&quot;external&quot;&gt;Text Mesh Pro&lt;/a&gt; asset, and aside from the first frame where it's generating the texture, it's takes about half the time to render. This was a huge gain on some scenes. It's not a drop-in replacement though; you'll need to go through and replace your textfields one by one, but it's definitely worth it.&lt;/p&gt;
&lt;p&gt;If you want to stick with Unity Text anyway, then make sure:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code class=&quot;language-csharp&quot;&gt;raycastTarget&lt;/code&gt; is turned off; it's on by default, but you're probably hit-testing against a background anyway&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Rich Text&lt;/em&gt; is turned off, unless you're rendering HTML etc&lt;/li&gt;
	&lt;li&gt;Don't use &lt;em&gt;Best Fit&lt;/em&gt; unless you &lt;em&gt;really&lt;/em&gt; have to, or at most limit the min-max font size and only have 1 or 2 per screen. If your min font size is 10, and your max is 30, then what happens is Unity goes through the entire render cycle at 30, and checks if it fits. If it doesn't, then it goes down a notch and tries again, and so on. As you can imagine, with each render until you get the right size, this is pretty slow. Either fix your UI so you don't need to do this, or have one or two sizes in code and change them manually&lt;/li&gt;
	&lt;li&gt;This one comes from Jaysama in the comments: Avoid &lt;a href=&quot;http://docs.unity3d.com/430/Documentation/Components/class-Font.html&quot; class=&quot;external&quot;&gt;dynamic fonts&lt;/a&gt; if you can. Dynamic fonts can be useful if you're using a common font and trying to save on download size, or working with asian languages, where the resulting font texture can be quite large. Unity won't pre-generate a font texture, but will use the underlying OS to render the text on the fly. This obviously has performance implications, especially if your text changes often.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Update: Unity &lt;a href=&quot;https://blogs.unity3d.com/2017/03/20/textmesh-pro-joins-unity/&quot; class=&quot;external&quot;&gt;have announced that TextMeshPro will become an official part of Unity&lt;/a&gt;!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update: TextMeshPro is now built into Unity directly, and you can access it through the Unity Package Manager. If you already have it in your project (i.e. you bought the asset), then &lt;a href=&quot;https://forum.unity.com/threads/textmesh-pro-unity-2018-1.511748/&quot; class=&quot;external&quot;&gt;follow this guide on how to update your project&lt;/a&gt;. Fair warning, you lose easy access to the code, which is useful if you need to modify it (see the WebGL section below).&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;overdraw&quot;&gt;Watch out for overdraw&lt;/h2&gt;
&lt;p&gt;Overdraw is where the same pixel on the screen is hit multiple times. Aside from being redundant, when you combine it with transparent areas of sprites (and multiples thereof), it'll soon start to hurt. In an ideal world, you'd write to each pixel once, but this can be hard, especially with UI. In the &lt;em&gt;Scene&lt;/em&gt; view, there's a dropdown that will show you the overdraw for the current scene, so you know where to apply your efforts.&lt;/p&gt;
&lt;p&gt;Unity has a &lt;em&gt;polygon&lt;/em&gt; mode which will replace your square sprites with much tighter outlines. You have more verticies and polygons, but less overdraw, so it can be a big help. If you're using &lt;a href=&quot;&quot; class=&quot;external&quot;&gt;TexturePacker&lt;/a&gt; (and you should be), &lt;a href=&quot;https://www.codeandweb.com/texturepacker/tutorials/using-spritesheets-with-unity&quot; class=&quot;external&quot;&gt;then this is already built in for you&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another thing to keep in mind is draw ordering. Normally, we'd be used to drawing a large background covering the screen, then drawing other elements over it. On mobile, it can be better to draw your front elements first, then make use of the depth buffer when drawing the background. A depth check is much quicker than overwriting a pixel.&lt;/p&gt;
&lt;h2 id=&quot;drawCalls&quot;&gt;Draw calls, Dynamic Batching, etc&lt;/h2&gt;
&lt;p&gt;By default &lt;em&gt;Dynamic Batching&lt;/em&gt; should be activated (if not, you can find it in &lt;em&gt;Player Settings &gt; Other Settings&lt;/em&gt;). Basically it gathers up all sprites using the same material and renders them all in one draw call. There are a number of things that break batching, such as differing scales, tints, textures etc, so you might need to reorganise how you draw your elements, or combine textures into a spritesheet. A &lt;code class=&quot;language-csharp&quot;&gt;Text&lt;/code&gt; UI element will probably not be rendered with UI sprites, and for one example that we had, simply moving it from behind one Sprite to in front of it brought our draw calls from 3 to 7. Changing the &lt;code class=&quot;language-csharp&quot;&gt;Pos Z&lt;/code&gt; of the element will also probably break it.&lt;/p&gt;
&lt;p&gt;Each draw call you have means a context switch for the GPU, which is expensive on mobile. I'd say any more than 10 is probably too many, and more than 20 will give you problems. Basically get it as low as you can, either through batching or combining your models/textures.&lt;/p&gt;
&lt;p&gt;Static batching gives a better performance boost, but it means that you can't move, scale, or rotate anything that it's applied to. Unfortunately it also doesn't seem to work yet with &lt;code class=&quot;language-csharp&quot;&gt;SpriteRenderers&lt;/code&gt;, so it's only for meshes at the minute.&lt;/p&gt;
&lt;p&gt;If you use the &lt;a href=&quot;https://docs.unity3d.com/Manual/FrameDebugger.html&quot; class=&quot;external&quot;&gt;Frame Debugger&lt;/a&gt; to analyse your frames, most of the time it'll give you the reason why a batch change occurred.&lt;/p&gt;
&lt;h2 id=&quot;audio&quot;&gt;Set up your audio properly&lt;/h2&gt;
&lt;p&gt;An easy thing to overlook is your &lt;code class=&quot;language-csharp&quot;&gt;AudioClip&lt;/code&gt; settings.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;em&gt;Force To Mono&lt;/em&gt;: I'd always set this unless you have a specific reason not to. Unless the user is listening with headphones, most speakers are mono anyway&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Load In Background&lt;/em&gt;: Set this for every &lt;code class=&quot;language-csharp&quot;&gt;AudioClip&lt;/code&gt; that you don't need to play immediately. It'll push it on to another thread so it doesn't block the main one&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Preload Audio Data&lt;/em&gt;: Specifies whether it should be preloaded, or loaded when you first play it. For short SFX, set it, otherwise ignore.&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Load Type&lt;/em&gt;: &lt;em&gt;Decompress On Load&lt;/em&gt; will decompress it immediately, &lt;em&gt;Compressed in Memory&lt;/em&gt; will keep it compressed, but load it anyway, and &lt;em&gt;Streaming&lt;/em&gt; will only load what's necesssary to play, when you're playing it. For short SFX, I use &lt;em&gt;Decompress On Load&lt;/em&gt;, while setting music files to &lt;em&gt;Streaming&lt;/em&gt;&lt;/li&gt;
	&lt;li&gt;&lt;em&gt;Compression Format&lt;/em&gt;: &lt;em&gt;PCM&lt;/em&gt; is the best, but largest, memory-wise, &lt;em&gt;ADPCM&lt;/em&gt; is probably the best for short SFX, while &lt;em&gt;Vorbis&lt;/em&gt; is the best for music. &lt;span class=&quot;defunct&quot;&gt;On mobile, everything is loaded as &lt;em&gt;Vorbis&lt;/em&gt; anyway&lt;/span&gt;
		&lt;br&gt;
		&lt;em&gt;&lt;strong&gt;Update 20/04/2017:&lt;/strong&gt; As pointed out by Min Shin in the comments, I was actually reading the &lt;a href=&quot;https://docs.unity3d.com/Manual/AudioFiles.html&quot; class=&quot;external&quot;&gt;AudioFiles Manual page&lt;/a&gt; wrong. The actual text is:&lt;/em&gt;
		&lt;blockquote style=&quot;font-size:1em&quot;&gt;When audio is encoded in Unity the main options for how it is stored on disk is either PCM, ADPCM or Compressed. [...] The default mode is Compressed, where the audio data is compressed with either Vorbis/MP3 for standalone and mobile platforms, or HEVAG/XMA for PS Vita and Xbox One.&lt;/blockquote&gt;
		&lt;em&gt;which I took to mean that only Vorbis/MP3 is used on mobile, but it's rather that Vorbis/MP3 is used on mobile, *if you choose Compressed*.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Making a few simple changes brought our app time-to-Home-screen down from around 30s to under 15s, as well as freeing up a lot of memory.&lt;/p&gt;
&lt;h2 id=&quot;rendering&quot;&gt;Enable Multithreaded rendering&lt;/h2&gt;
&lt;p&gt;This point is Android only (it's always enabled if you're using Metal on iOS), but in &lt;em&gt;Player Settings &gt; Other Settings&lt;/em&gt; you'll see a toggle for &lt;em&gt;Multithreaded Rendering&lt;/em&gt;. This puts all rendering on another thread, freeing up your main thread for application logic, obviously a good thing.&lt;/p&gt;
&lt;p&gt;It comes with it's own caveat though, as depending on what you're going with your project, especially if you interacting with textures, it can give graphic glitches. The only thing to do is try it for your project and see if you get away with it. It might also not work on some devices, though I'm not sure if Unity turns if off automatically or not.&lt;/p&gt;
&lt;p&gt;If you see problems with it on certain devices, I almost say just disable that device, as the benefits are pretty awesome.&lt;/p&gt;
&lt;h2 id=&quot;scrolling&quot;&gt;Scrolling&lt;/h2&gt;
&lt;p&gt;Doing any scrolling in your game menus? The new Unity UI has a scroll component built in, but its performance can be horrible. Solution: scroll the camera itself - if necessary create a new camera with the right necessary rect. Moving a camera is a simple matrix change, so the rendering is the same, while moving via a &lt;code class=&quot;language-csharp&quot;&gt;ScrollRect&lt;/code&gt; seems to modify all the &lt;code class=&quot;language-csharp&quot;&gt;RectTransforms&lt;/code&gt; all the way down, meaning everything has to be recalculated, and if you've half-way complicated UI, this can be a huge drain. Scrolling the camera alone can easily make it twice as quick. You might need to jump through hoops to get mouse coordinates working properly, but a bit of extra code to get rid of janky scrolling is doable.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Update 08/2016: TFG Studios got in touch through the comments to show off their optimised ScrollView. It looks pretty nice, but you can &lt;a href=&quot;https://www.youtube.com/watch?v=FOQV8AA50Rw&quot; class=&quot;external&quot;&gt;check it out yourself on YouTube&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;break&quot;&gt;Know when to break from Unity&lt;/h2&gt;
&lt;p&gt;When you first come into Unity, everything is component-this and component-that, but sometimes you need to know when to break from Unity and do things a simpler, more direct way. As an example, when we first started making the map for the games, we did a naive approach where we use tiles and each tile has a component that helped with its behaviour; one for the tile properties (drag, etc), one for the physics, one for special considerations (e.g. flashing, or affecting the ball).&lt;/p&gt;
&lt;p&gt;This works for quick projects, but pretty soon you realise that you've about 10k objects in the game, and most of them are useless. The properties were split off into static variables, the physics were removed and a collision/trigger mesh generated by our level tool instead (so one &lt;code class=&quot;language-csharp&quot;&gt;PolygonCollider2D&lt;/code&gt; instead of one for each tile that needs it), and any special cases were merged where possible, so we'd have one loop rather than 50 &lt;code class=&quot;language-csharp&quot;&gt;Update()&lt;/code&gt; methods running.&lt;/p&gt;
&lt;p&gt;For the rendering, we also made use of a &lt;code class=&quot;language-csharp&quot;&gt;RenderTexture&lt;/code&gt; and Camera culling masks, so all those tiles would get rendered exactly once to the texture, then hidden. For some tiles we couldn't do this (e.g for the ones that break or flash), but for the vast majority we could, which meant that our map and border tiles are one simple mesh. And because the semi-transparent tiles are baked into the texture, we can apply one super fast non-transparent material to the mesh to get the quickest rendering possible.&lt;/p&gt;
&lt;h2 id=&quot;threads&quot;&gt;Use threads where you need to&lt;/h2&gt;
&lt;p&gt;Because our game is multiplayer, we use sockets via &lt;code class=&quot;language-csharp&quot;&gt;TcpClient&lt;/code&gt; to send and receive messages. Originally we were doing a synchronous write and an async read. As the &lt;code class=&quot;language-csharp&quot;&gt;EndRead()&lt;/code&gt; method is on another thread, there was no problem when deserialising/decompressing the received message. But that left the problem of sending. Normally it wouldn't be too bad, but sometimes you would get a message being sent out while the ball is moving, and compressing/serialising to JSON are pretty slow operations, so you're looking at minimum 1 dropped frame. Even sending the shoot message as you fired would introduce a tiny bit of lag on the client, which meant the experience wasn't great.&lt;/p&gt;
&lt;p&gt;A simple threading system is now in place which means that all that is pushed off the main thread, so there shouldn't be any impact because of it. Some of the logic in the game had to be rewritten to accomodate that - because the writes were synchronous before, we could reuse objects, which we can't do with async - but the performance gains are worth it. &lt;a href=&quot;http://www.albahari.com/threading/&quot; class=&quot;external&quot;&gt;Joseph Albahari has a great resource on threading in C#&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;webgl&quot;&gt;WebGL&lt;/h2&gt;
&lt;p&gt;If you're building your game for web, there are a few things to keep in mind:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Everything is single threaded, so any cool threading features (that you might have used to gain some speed) are out the window&lt;/li&gt;
	&lt;li&gt;In the same vein, all &lt;code class=&quot;language-csharp&quot;&gt;Jobs&lt;/code&gt; are now run on the main thread&lt;/li&gt;
	&lt;li&gt;Sockets don't exist, so if web players are playing on the same server as mobile players, you now have an extra layer of redirection, going from WebSocket -&amp;gt; Socket and back again&lt;/li&gt;
	&lt;li&gt;The memory that you assign when your project starts is all you get, so it's highly likely you'll get some Out-of-memory crashes in the beginning until you find your sweet spot (NOTE: it increments in steps of 16MB)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As memory is such a big issue, and even an empty project results in a pretty hefty download and an unskippable delay at the start while million-odd lines of JS are compiled, it's super important that you work on getting your project as slim as possible. To that end:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Make sure &lt;em&gt;Strip Engine Code&lt;/em&gt; is ticked in the Build Settings. If you're using something that you're not directly referencing (e.g. an AssetBundle is using a class that your main file isn't), then you'll need to use a &lt;a href=&quot;https://docs.unity3d.com/Manual/IL2CPP-BytecodeStripping.html&quot; class=&quot;external&quot;&gt;link.xml file&lt;/a&gt; to explicitly stop those files from being removed&lt;/li&gt;
	&lt;li&gt;Use &lt;em&gt;Crunch&lt;/em&gt; compression, as it's awesome. There can be some artifacts if you're using gradients, or issues with thin sprites (as it's a block-based compression), or sprites that you use for a clipping mask (e.g. round images can have block artifacts around the edges, and as clipping masks are either on or off, you lose your smooth edge), but it's worth it&lt;/li&gt;
	&lt;li&gt;Make use of AssetBundles, if you want to remove as many embedded assets as possible from your project&lt;/li&gt;
	&lt;li&gt;Enable &lt;em&gt;Brotli&lt;/em&gt; compression if you can. You'll need a web server that can support it, and have to serve your site over HTTPS, but you'll save a few MBs, easy. Modern browsers have brotli decompression built-in, and being HTTPS will also give you access to HTTP/2, so it's win/win&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A somewhat broken, but useful tool to use is &lt;a href=&quot;http://files.unity3d.com/build-report/&quot; class=&quot;external&quot;&gt;http://files.unity3d.com/build-report/&lt;/a&gt;, which you can use to see which libs are actually going into your build. It'll tell you if a lib is being added because of another library (e.g. &lt;em&gt;Particles&lt;/em&gt; might require &lt;em&gt;Wind&lt;/em&gt;), or scripts (though not which script).&lt;/p&gt;
&lt;p&gt;We found out that we had the 5MB 3D Physics lib being added, even though we're a 2D game. Why, you ask?&lt;/p&gt;
&lt;p&gt;Well, aside from UnityPurchasing for some reason (but UnityPurchasing doesn't work on the web, so we could just delete it), it was being brought in by TextMeshPro, which has a feature to let you make physics-enabled text in 3D, and thus had &lt;code class=&quot;language-csharp&quot;&gt;Collider&lt;/code&gt; references.&lt;/p&gt;
&lt;p&gt;As mentioned above, because we had the code for TextMeshPro, we could go in and comment out the offending classes, but if you're using the Unity built-in TextMeshPro, you'll have to wait until (or if) they do it.&lt;/p&gt;
&lt;h2 id=&quot;lrp&quot;&gt;Check out the Lightweight render pipeline&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blogs.unity3d.com/2018/02/21/the-lightweight-render-pipeline-optimizing-real-time-performance/&quot; class=&quot;external&quot;&gt;This is a new addition&lt;/a&gt; but it's pretty awesome. Basically, Unity have opened up their rendering, allowing devs to make their own custom pipeline.&lt;/p&gt;
&lt;p&gt;Honestly, looking at the code necessary to set it up gave me the shivers, but happily, Unity provide a Lightweight Pipeline and a HD Pipeline.&lt;/p&gt;
&lt;p&gt;It's &lt;em&gt;very&lt;/em&gt; easy to drop in and setup - if you're using the Unity shaders, you don't need to make any changes - and it simplifies rendering enough to give you a noticeable boost.&lt;/p&gt;
&lt;h2 id=&quot;ecs&quot;&gt;Check out the new ECS system&lt;/h2&gt;
&lt;p&gt;Another new one, and one we've not done yet. The new &lt;a href=&quot;https://unity3d.com/fr/learn/tutorials/topics/scripting/introduction-ecs&quot; class=&quot;external&quot;&gt;Entity Component System&lt;/a&gt; is definitely on the list for the next game though. Combined with the Job system, it's specifically built to take advantage of multithreaded systems, and lets you write high-performance code straight up.&lt;/p&gt;
&lt;p&gt;Entities are reduced to mere ints. Components are nothing more than containers for data. The real meat comes with the Systems. They're made to treat lists of components all at once, and just bang it out.&lt;/p&gt;
&lt;p&gt;If you've ever used something like &lt;a href=&quot;https://github.com/richardlord/Ash&quot; class=&quot;external&quot;&gt;Ash&lt;/a&gt; in AS3, you'll be right at home. One of the great advantages of this setup is that it becomes much easier to debug your code. You work on insular Systems, and only need to keep the code relative to that System in your head. You can modify freely without impacting the other Systems. It's a different way of thinking about things, but once you get into the swing of it, it's great.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;
&lt;p&gt;While going through all of this over a number of different weeks and months, I started saving a list of useful links. In no particular order:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The entire &lt;a href=&quot;https://unity3d.com/learn/tutorials/topics/performance-optimization&quot; class=&quot;external&quot;&gt;Performance Optimisation section&lt;/a&gt; - if you're having issues with framerate, the info in the graphics section can help you figure out if you're &lt;em&gt;CPU-bound&lt;/em&gt; or &lt;em&gt;GPU-bound&lt;/em&gt;&lt;/li&gt;
	&lt;li&gt;Pretty much the entire &lt;a href=&quot;https://unity3d.com/learn/tutorials/s/best-practices&quot; class=&quot;external&quot;&gt;Best Practices section&lt;/a&gt;, specifically Assets, UI, and Profiling&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=j4YAY36xjwE&quot; class=&quot;external&quot;&gt;Unite Europe 2016 - Optimizing Mobile Applications&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=6vmRwLYWNRo&quot; class=&quot;external&quot;&gt;Unite Europe 2016 - Overthrowing the MonoBehaviour Tyranny in a Glorious Scriptable Object Revolution&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=_wxitgdx-UI&quot; class=&quot;external&quot;&gt;Unite Europe 2017 - Squeezing Unity: Tipes for raising performance&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=OSlOwJP8Z14&quot; class=&quot;external&quot;&gt;Unite Europe 2017 -Practical guide to profiling tools in Unity&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=tGmnZdY5Y-E&quot; class=&quot;external&quot;&gt;Unite Austin 2017 - Writing High Performance C# Scripts&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=eH-PdFKgctE&quot; class=&quot;external&quot;&gt;Unite Seoul 2017 - Tips and Tricks for Optimising Unity UI&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://unity3d.com/how-to/unity-ui-optimization-tips&quot; class=&quot;external&quot;&gt;Unity UI optimization tips&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://unity3d.com/how-to/big-games-on-low-end-mobile&quot; class=&quot;external&quot;&gt;When it pays to be cheap: Tips for big games on low-end mobile&lt;/a&gt; - some great stuff in here, though a lot of it bypasses Unity&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity.html&quot; class=&quot;external&quot;&gt;Understanding optimization in Unity&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Optimisation is a pretty big subject, especially when so much of it is guesswork or slowed by the constant &lt;em&gt;change &gt; build &gt; test&lt;/em&gt; cycle, which on mobile isn't quick. I also find the profiler on Unity not as good compared to others, such as Scout. Your history extends as far as the window, you can't select multiple frames to get averages (even though they have this information), and &lt;strike&gt;you can't save the results&lt;/strike&gt; (update: you can now), so before and after comparisons either work through your memory, or screenshots.&lt;/p&gt;
&lt;p&gt;Still, it's what we have to work with, so if you have it, use it, as sometimes, what's causing your slowdown isn't what you think it is. You don't need to profile all the time, premature optimisation being the devil and all, but you should do it regularly.&lt;/p&gt;
&lt;p&gt;If there's anything I've missed, let me know! Happy hunting.&lt;/p&gt;</description>
        <pubDate>Thu, 21 Jan 2016 21:33:00 +0000</pubDate>
        <link>https://divillysausages.com/2016/01/21/performance-tips-for-unity-2d-mobile/</link>
        <guid isPermaLink="true">https://divillysausages.com/2016/01/21/performance-tips-for-unity-2d-mobile/</guid>
      </item>
    
      <item>
        <title>Hacking how Node.js launches in Visual Studio Code</title>
        <description>&lt;p&gt;I've been &lt;a href=&quot;https://divillysausages.com/2015/06/09/using-phaser-with-visual-studio-code/&quot;&gt;using Visual Studio Code&lt;/a&gt; lately, working on getting my &lt;a href=&quot;https://divillysausages.com/2015/07/12/an-intro-to-socket-io/&quot;&gt;socket sever&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/divillysausages/status/620252964360290304&quot; class=&quot;external&quot;&gt;Sir&lt;/a&gt;, off its feet. As any good dev will tell you, the first step for good debugging is good logging, so I was on the search for a small, fast &lt;a href=&quot;https://nodejs.org/&quot; class=&quot;external&quot;&gt;Node.js&lt;/a&gt; logging module.&lt;/p&gt;
&lt;p&gt;And thus I came across &lt;a href=&quot;https://github.com/trentm/node-bunyan&quot; class=&quot;external&quot;&gt;Bunyan&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Bunyan does a lot of things, but when you run it, a simple&lt;p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;log.&lt;span class=&quot;hljs-title function_&quot;&gt;info&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Hello world&amp;quot;&lt;/span&gt; );&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;becomes&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;{&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;:&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Sir&amp;quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;hostname&amp;quot;&lt;/span&gt;:&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;divillysausa-PC&amp;quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;pid&amp;quot;&lt;/span&gt;:2244,&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;level&amp;quot;&lt;/span&gt;:30,&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;msg&amp;quot;&lt;/span&gt;:&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Hello world!&amp;quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;time&amp;quot;&lt;/span&gt;:&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;2015-07-12T21:47:30.576Z&amp;quot;&lt;/span&gt;,&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;v&amp;quot;&lt;/span&gt;:0}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;in your console.&lt;/p&gt;
&lt;p&gt;While undoubtedly useful, it also makes actually &lt;em&gt;reading&lt;/em&gt; them a bit awkward. Happily, they've thought of this, and have included a CLI that takes that output and transforms it to:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;[2015-07-12T22:12:45.698Z]  INFO: Sir/4900 on divillysausa-PC: Hello world!&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;They've even colour coded it for your convenience!&lt;/p&gt;
&lt;h2 id=&quot;code&quot;&gt;Where Visual Studio Code comes in&lt;/h2&gt;
&lt;p&gt;All of this is relevant to the title, I promise.&lt;/p&gt;
&lt;p&gt;In order to get this wonderful output, you simply need to change your &lt;code class=&quot;language-bash&quot;&gt;node&lt;/code&gt; call from&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node app.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;to&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node app.js | &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;node_modules/.bin/bunyan&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In essence, what you're doing here is piping the output coming from &lt;code class=&quot;language-bash&quot;&gt;node&lt;/code&gt; and sending it to &lt;code class=&quot;language-bash&quot;&gt;bunyan&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So, how do you actually do that?&lt;/p&gt;
&lt;p&gt;The first time you hit &lt;em&gt;F5&lt;/em&gt; to launch your app, Visual Studio Code will create a &lt;em&gt;launch.json&lt;/em&gt; file for you that looks like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;version&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;0.1.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;hljs-comment&quot;&gt;// List of configurations. Add new configurations or edit existing ones.  &lt;/span&gt;
	&lt;span class=&quot;hljs-comment&quot;&gt;// ONLY &amp;quot;node&amp;quot; and &amp;quot;mono&amp;quot; are supported, change &amp;quot;type&amp;quot; to switch.&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;configurations&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;
		&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Name of configuration; appears in the launch configuration drop down menu.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Launch app.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Type of configuration. Possible values: &amp;quot;node&amp;quot;, &amp;quot;mono&amp;quot;.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;node&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Workspace relative or absolute path to the program.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;program&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;app.js&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Automatically stop program after launch.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;stopOnEntry&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Command line arguments passed to the program.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;args&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Workspace relative or absolute path to the working directory of the program being debugged. Default is the current workspace.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;cwd&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Workspace relative or absolute path to the runtime executable to be used. Default is the runtime executable on the PATH.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;runtimeExecutable&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;null&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Optional arguments passed to the runtime executable.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;runtimeArgs&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Environment variables passed to the program.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;env&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Use JavaScript source maps (if they exist).&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;sourceMaps&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;false&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// If JavaScript source maps are enabled, the generated code is expected in this directory.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;outDir&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;null&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt; 
		&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;name&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Attach&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;type&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;node&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// TCP/IP address. Default is &amp;quot;localhost&amp;quot;.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;address&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-comment&quot;&gt;// Port to attach to.&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;port&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;4000&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;sourceMaps&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
		&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;hljs-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I tried adding my &lt;code class=&quot;language-bash&quot;&gt;| &quot;node_modules/.bin/bunyan&quot;&lt;/code&gt; line first in &lt;code class=&quot;language-json&quot;&gt;args&lt;/code&gt;, then &lt;code class=&quot;language-json&quot;&gt;runtimeArgs&lt;/code&gt;, then &lt;code class=&quot;language-json&quot;&gt;env&lt;/code&gt;, before finally realising that these were being passed to your &lt;em&gt;app.js program&lt;/em&gt; rather than to &lt;code class=&quot;language-bash&quot;&gt;node&lt;/code&gt; itself. Then I tried modifying the &lt;code class=&quot;language-json&quot;&gt;program&lt;/code&gt; parameter to read &lt;code class=&quot;language-json&quot;&gt;&quot;app.js | \&quot;node_modules/.bin/bunyan\&quot;&quot;&lt;/code&gt;, but it complained that the file didn't exist :)&lt;/p&gt;
&lt;p&gt;The key to getting this hack working is with the &lt;code class=&quot;language-json&quot;&gt;runtimeExecutable&lt;/code&gt; property. By default it's &lt;code class=&quot;language-json&quot;&gt;null&lt;/code&gt;, which means that VSC will search in your &lt;code&gt;PATH&lt;/code&gt; directory for &lt;code class=&quot;language-bash&quot;&gt;node&lt;/code&gt;. If you specify a path, then VSC will run that instead.&lt;/p&gt;
&lt;p&gt;With that in mind, I created a small bat file (I'm on Windows), &lt;em&gt;nodeWithBunyan.bat&lt;/em&gt;:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;@&lt;span class=&quot;hljs-built_in&quot;&gt;echo&lt;/span&gt; off
node %* | &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;node_modules/.bin/bunyan&amp;quot;&lt;/span&gt; -o short&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Basically, what it's doing is calling &lt;code class=&quot;language-bash&quot;&gt;node&lt;/code&gt;, passing all the parameters that VSC is passing (&lt;code class=&quot;language-bash&quot;&gt;%*&lt;/code&gt;), before piping the output through &lt;code class=&quot;language-bash&quot;&gt;bunyan&lt;/code&gt; (the &lt;code class=&quot;language-bash&quot;&gt;-o short&lt;/code&gt; at the end is just telling &lt;code class=&quot;language-bash&quot;&gt;bunyan&lt;/code&gt; to use its &quot;short&quot; output mode, which is even more concise). As we're passing all the VSC parameters, we even get to keep debugging.&lt;/p&gt;
&lt;p&gt;If you're working in a bash environment, then I think the equivalent is&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;node &lt;span class=&quot;hljs-variable&quot;&gt;$@&lt;/span&gt; | ./node_modules/.bin/bunyan -o short&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Back in VSC, we replace the &lt;code class=&quot;language-json&quot;&gt;runtimeExecutable&lt;/code&gt; property so it reads&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;runtimeExecutable&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;./nodeWithBunyan.bat&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;hit &lt;em&gt;F5&lt;/em&gt;, and *jazzhands*!&lt;/p&gt;
&lt;img src=&quot;https://divillysausages.com/files/node_launch_vsc/bunyan.png&quot; alt=&quot;Final Bunyan output&quot; width=&quot;581&quot; height=&quot;131&quot;/&gt;
&lt;p&gt;If, for some reason, you want to switch between the default launch and the new, snazzy one, you can simply create another config, add it to the &lt;code class=&quot;language-json&quot;&gt;configurations&lt;/code&gt; array in your &lt;em&gt;launch.json&lt;/em&gt; file, then choose between them in dropdown menu on the Debug tab.&lt;/p&gt;
&lt;p&gt;You can also use this technique if, for example, you want to set the &lt;code class=&quot;language-bash&quot;&gt;DEBUG&lt;/code&gt; or &lt;code class=&quot;language-bash&quot;&gt;NODE_DEBUG&lt;/code&gt; environment variables when launching:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;NODE_DEBUG=http,fs,timers node %*&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;</description>
        <pubDate>Sun, 12 Jul 2015 23:39:00 +0000</pubDate>
        <link>https://divillysausages.com/2015/07/13/hacking-how-nodejs-launches-in-visual-studio-code/</link>
        <guid isPermaLink="true">https://divillysausages.com/2015/07/13/hacking-how-nodejs-launches-in-visual-studio-code/</guid>
      </item>
    
      <item>
        <title>You are the Monster - Ludum Dare #33</title>
        <description>&lt;p&gt;Another Ludum Dare, and hooo boy, what this a tough one. Because of other commitments through the weekend, I wasn't able to spend the full 48hrs on it - I'd say I managed around 18 all told - so to get solid blocks of time, I ended up doing an all-nighter on Saturday and right to the deadline on Sunday.&lt;/p&gt;
&lt;p&gt;The theme this time was &lt;em&gt;You are the Monster&lt;/em&gt;. As I wasn't going to have a lot of time, my weapon of choice was the tried and trusted workhorse, AS3.&lt;/p&gt;
&lt;img src=&quot;https://divillysausages.com/files/ludum_dare_33/ingame.png&quot; alt=&quot;You are the Monster&quot; width=&quot;800&quot; height=&quot;600&quot;/&gt;
&lt;h2 id=&quot;about&quot;&gt;About the game&lt;/h2&gt;
&lt;p&gt;You're happily passing your day in your underwear, when the classical damsel in distress comes screaming in, as there's a monster on the loose. This is the day you've been waiting for! Impress people! Win friends!&lt;/p&gt;
&lt;p&gt;Kit yourself out as best you can with the money you have, and essay forth to slay the foul beast.&lt;/p&gt;
&lt;p&gt;Be careful however, there are whispers of a curse abound...&lt;/p&gt;
&lt;h2 id=&quot;development&quot;&gt;Development&lt;/h2&gt;
&lt;p&gt;As I was used what I'm used to, development went pretty well. Because I didn't have the full weekend, I couldn't put in any sound effects or music, which is a shame, as I have a pretty good library full of swords and grunts and the like. So, if you could do me a favour, and provide your own &quot;huurgh&quot;, &quot;hiieagh&quot;, etc, that'd be &lt;em&gt;fantastic&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I was pretty pleased with how the art turned out - I'm not great at it, but the colours work, and the characters aren't too ugly. These are the benefits of pixel art.&lt;/p&gt;
&lt;p&gt;The actual art is draw to 1/4 of what you see on the screen, and scaled up, in order to get that nice chunky look. It makes it a bit awkward when working with objects with competing scales, but there you go.&lt;/p&gt;
&lt;p&gt;The idea itself owes a large part to &lt;a href=&quot;http://sdkgame.com/&quot; class=&quot;external&quot;&gt;Defect&lt;/a&gt;, which is a game about building a spaceship, to have the crew mutiny, then building another one to beat that first one, to have the crew mutiny, etc. I saw a video of it a few weeks/months ago, and I guess it stuck in my head. Marketing works, people!&lt;/p&gt;
&lt;h2 id=&quot;game&quot;&gt;The game&lt;/h2&gt;
&lt;p&gt;The game is available to play below and if you'd like to vote on it, you can at &lt;a href=&quot;http://ludumdare.com/compo/ludum-dare-33/?action=preview&amp;uid=12486&quot; class=&quot;external&quot;&gt;http://ludumdare.com/compo/ludum-dare-33/?action=preview&amp;uid=12486&lt;/a&gt; which would mean a lot. The full source is also available, if you dare to look at it.&lt;/p&gt;
&lt;h2 id=&quot;play&quot;&gt;Play&lt;/h2&gt;
&lt;img src=&quot;https://divillysausages.com/files/ludum_dare_33/Monster.png&quot; alt=&quot;You are the Monster&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;swfReplace&quot; /&gt;</description>
        <pubDate>Sun, 12 Jul 2015 23:39:00 +0000</pubDate>
        <link>https://divillysausages.com/ludumdare/33/</link>
        <guid isPermaLink="true">https://divillysausages.com/ludumdare/33/</guid>
      </item>
    
      <item>
        <title>An intro to Socket.io</title>
        <description>&lt;p&gt;Recently I've been playing around with &lt;a href=&quot;http://socket.io/&quot; class=&quot;external&quot;&gt;Socket.io&lt;/a&gt;, which, according to the site, &lt;em&gt;enables real-time bidirectional event-based communication&lt;/em&gt;. Damn, that's some sexy copy.&lt;/p&gt;
&lt;p&gt;Basically you can use it as a real-time server, and one of the things that makes it so interesting is that it works on pretty much every platform, browser, or device. I thought I'd write an intro on the subject as I found their documentation to be lacking in some cases and confusing in others; I frequently found myself with questions that I only answered through trial-and-error and reading the source code.&lt;/p&gt;
&lt;p&gt;And you should never have to read the source code.&lt;/p&gt;
&lt;h2 id=&quot;typescript&quot;&gt;TypeScript!&lt;/h2&gt;
&lt;p&gt;I'm using TypeScript, so one of the first things I did was try and hook myself up with some definition files so I could get some code completion. I found the excellent &lt;a href=&quot;https://github.com/borisyankov/DefinitelyTyped&quot; class=&quot;external&quot;&gt;DefinitelyTyped&lt;/a&gt; project and related &lt;a href=&quot;http://definitelytyped.org/tsd/&quot; class=&quot;external&quot;&gt;tsd&lt;/a&gt;, or TypeScript Definition manager.&lt;/p&gt;
&lt;p&gt;Unfortunately, after scratching my head for a day or two, I finally realised that the &lt;em&gt;d.ts&lt;/em&gt; files were for an old version. Sadface.&lt;/p&gt;
&lt;p&gt;So I learned how to create TypeScript definition files, and went through the code and created new definitions for the latest version (1.3.5 as of time of writing). If the &lt;a href=&quot;https://github.com/borisyankov/DefinitelyTyped/pull/4894&quot; class=&quot;external&quot;&gt;pull request&lt;/a&gt; hasn't been accepted yet, then you can find them at the bottom of this post.&lt;/p&gt;
&lt;p&gt;They're even commented to boot.&lt;/p&gt;
&lt;p&gt;AND I went out of my way to describe the options object, which nobody apparently does, but blow trawling through documentation and READMEs for a lark. If it's not code-completed, it doesn't exist.&lt;/p&gt;
&lt;h2 id=&quot;setup&quot;&gt;The setup&lt;/h2&gt;
&lt;p&gt;For the setup, I worked from the &lt;a href=&quot;http://socket.io/get-started/chat/&quot; class=&quot;external&quot;&gt;Chat example&lt;/a&gt; on the Socket.io website, with the exception that I didn't use Express (on a side node, Express is great and all, for beginners, one framework at a time, please). You should be able to follow that example in order to get setup. To install Socket.io (locally), just use:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install socket.io&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Like in my tutorial for &lt;a href=&quot;https://divillysausages.com/2015/06/09/using-phaser-with-visual-studio-code/&quot;&gt;Using Phaser with Visual Studio Code&lt;/a&gt; I'm not using Express, I'm using &lt;a href=&quot;https://github.com/cloudhead/node-static&quot; class=&quot;external&quot;&gt;node-static&lt;/a&gt; (as all it does is serve files from whatever directory you throw at it, so it's a perfect light-weight solution). If you want to use that, then install via:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install node-static&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then set up your &lt;em&gt;app.js&lt;/em&gt; file like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; nodeStatic 	= &lt;span class=&quot;hljs-built_in&quot;&gt;require&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;node-static&amp;#x27;&lt;/span&gt; );
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; http			= &lt;span class=&quot;hljs-built_in&quot;&gt;require&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;http&amp;#x27;&lt;/span&gt; );
&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; socketIO		= &lt;span class=&quot;hljs-built_in&quot;&gt;require&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;socket.io&amp;#x27;&lt;/span&gt; );

&lt;span class=&quot;hljs-comment&quot;&gt;// create our file server config&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; file = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; nodeStatic.&lt;span class=&quot;hljs-title class_&quot;&gt;Server&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;bin&amp;#x27;&lt;/span&gt;, { &lt;span class=&quot;hljs-comment&quot;&gt;// bin is the folder containing our html, etc&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;cache&lt;/span&gt;:&lt;span class=&quot;hljs-number&quot;&gt;0&lt;/span&gt;,	&lt;span class=&quot;hljs-comment&quot;&gt;// don&amp;#x27;t cache&lt;/span&gt;
	&lt;span class=&quot;hljs-attr&quot;&gt;gzip&lt;/span&gt;:&lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;	&lt;span class=&quot;hljs-comment&quot;&gt;// gzip our assets&lt;/span&gt;
});

&lt;span class=&quot;hljs-comment&quot;&gt;// create our server&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; httpServer = http.&lt;span class=&quot;hljs-title function_&quot;&gt;createServer&lt;/span&gt;( &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; request, response &lt;/span&gt;) {
	request.&lt;span class=&quot;hljs-title function_&quot;&gt;addListener&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;end&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;/span&gt;) {
		file.&lt;span class=&quot;hljs-title function_&quot;&gt;serve&lt;/span&gt;( request, response );
	});
	request.&lt;span class=&quot;hljs-title function_&quot;&gt;resume&lt;/span&gt;();
}).&lt;span class=&quot;hljs-title function_&quot;&gt;listen&lt;/span&gt;( &lt;span class=&quot;hljs-number&quot;&gt;4000&lt;/span&gt; );&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That's it. From this point out, it's all Socket.io. Note that when we call &lt;code class=&quot;language-javascript&quot;&gt;http.createServer()&lt;/code&gt;, we save the returned &lt;code class=&quot;language-javascript&quot;&gt;Server&lt;/code&gt; as our &lt;code class=&quot;language-javascript&quot;&gt;httpServer&lt;/code&gt; var; we'll need that later when binding Socket.io.&lt;/p&gt;
&lt;h2 id=&quot;basics&quot;&gt;The basics&lt;/h2&gt;
&lt;p&gt;First of all: connecting. On the server, setup Socket.io like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; sio = &lt;span class=&quot;hljs-title function_&quot;&gt;socketIO&lt;/span&gt;();
sio.&lt;span class=&quot;hljs-title function_&quot;&gt;serveClient&lt;/span&gt;( &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt; ); &lt;span class=&quot;hljs-comment&quot;&gt;// the server will serve the client js file&lt;/span&gt;
sio.&lt;span class=&quot;hljs-title function_&quot;&gt;attach&lt;/span&gt;( httpServer );

&lt;span class=&quot;hljs-comment&quot;&gt;// listen for a connection&lt;/span&gt;
sio.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;connection&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; socket &lt;/span&gt;){
	
	&lt;span class=&quot;hljs-variable language_&quot;&gt;console&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;log&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;User &amp;#x27;&lt;/span&gt; + socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; + &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27; connected&amp;#x27;&lt;/span&gt; );
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To explain a bit:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;code class=&quot;language-javascript&quot;&gt;var sio = socketIO()&lt;/code&gt; - here we're just creating our Socket.io server using the &lt;code class=&quot;langauge-javascript&quot;&gt;socketIO&lt;/code&gt; import above (NOTE: if you're not using TypeScript, then your import is in the form &lt;code class=&quot;language-javascript&quot;&gt;var socketIO = require('socket.io');&lt;/code&gt;)&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-javascript&quot;&gt;sio.serveClient( true )&lt;/code&gt; - here we're telling the server to serve any requests for the &lt;em&gt;socket.io-client.js&lt;/em&gt; file. You can serve it yourself if you wish, but this is pretty handy&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-javascript&quot;&gt;sio.attach( httpServer )&lt;/code&gt; - here we're attaching Socket.io to the &lt;code class=&quot;language-javascript&quot;&gt;httpServer&lt;/code&gt; instance created above via &lt;code class=&quot;language-javascript&quot;&gt;http.createServer()&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;&lt;code class=&quot;language-javascript&quot;&gt;sio.on( 'connection', function( socket ){...&lt;/code&gt; - this is called every time a client connects. In the callback function is where you'd add all your event listeners to the socket etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the client, you embed the client side code via:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;/socket.io/socket.io.js&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;script&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That particular path is if you're using &lt;code class=&quot;language-javascript&quot;&gt;sio.serveClient( true )&lt;/code&gt;. Otherwise, if you're serving it yourself, replace it with the right path. To connect to our server, it's simply:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; clientSocket = &lt;span class=&quot;hljs-title function_&quot;&gt;io&lt;/span&gt;();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Start your server (&lt;code class=&quot;language-javascript&quot;&gt;node app.js&lt;/code&gt;), go to http://localhost:4000 in your browser, and you should see something like&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;User 8fyIQvC4HUYEpZeDAAAA connected&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;in your console. Eassssssy.&lt;/p&gt;
&lt;h2 id=&quot;namespaces&quot;&gt;Namespaces and rooms&lt;/h2&gt;
&lt;p&gt;A quick note on namespaces and rooms and what the difference between the two is. By default, when you call &lt;code class=&quot;language-javascript&quot;&gt;io()&lt;/code&gt; on the client to connect, what you're actually calling is &lt;code class=&quot;language-javascript&quot;&gt;io( 'http://localhost:4000/' )&lt;/code&gt;, as by default, it will take the host and port of the current page, and connect to the default &lt;em&gt;namespace&lt;/em&gt;, which is &lt;code class=&quot;language-javascript&quot;&gt;/&lt;/code&gt;. If you wanted to connect to a namespace called &lt;code class=&quot;language-javascript&quot;&gt;'foo'&lt;/code&gt;, then you'd have to call &lt;code class=&quot;language-javascript&quot;&gt;io( 'http://localhost:4000/foo' )&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So what's a namespace?&lt;/p&gt;
&lt;p&gt;It's a way of separating your code. It's one socket to a namespace, so if you called:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; news = &lt;span class=&quot;hljs-title function_&quot;&gt;io&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;http://localhost:4000/news&amp;#x27;&lt;/span&gt; );
&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; chat = &lt;span class=&quot;hljs-title function_&quot;&gt;io&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;http://localhost:4000/chat&amp;#x27;&lt;/span&gt; );&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;while it might use the same physical TCP connection, they're two separate objects - &lt;code class=&quot;language-javascript&quot;&gt;news&lt;/code&gt; can't send messages to &lt;code class=&quot;language-javascript&quot;&gt;/chat&lt;/code&gt; and vice-versa.&lt;/p&gt;
&lt;p&gt;The main namespace, &lt;code class=&quot;language-javascript&quot;&gt;/&lt;/code&gt;, is created by default, and all clients join it, even if they connect to another namespace.&lt;/p&gt;
&lt;p&gt;On the server, when a namespace emits an event, all sockets connected to the namespace receive it.&lt;/p&gt;
&lt;p&gt;After that, we have &lt;em&gt;rooms&lt;/em&gt;. Rooms are essentially grouping of sockets, and let you send messages to a subgroup of a namespace. You only need to know the name of a room to send a message to it. Unlike with namespaces, sockets can join as many rooms as they want.&lt;/p&gt;
&lt;p&gt;Think of it like this: the Socket.io server is your game website (e.g. all-poker.com), namespaces are each game (e.g. /texas, /omaha), and rooms are private rooms within each game (e.g. table1, table2, etc).&lt;/p&gt;
&lt;p&gt;The differences in list form:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Namespaces are joined when you create the socket, rooms are joined as you want, but you need to build your own events to call &lt;code class=&quot;language-javascript&quot;&gt;join()&lt;/code&gt; and &lt;code&gt;leave()&lt;/code&gt;&lt;/li&gt;
	&lt;li&gt;One socket per namespace, but one socket can be in multiple rooms&lt;/li&gt;
	&lt;li&gt;Sockets can leave rooms as they please, but to leave the namespace, they need to disconnect&lt;/li&gt;
	&lt;li&gt;Communication can happen between rooms, but not between namespaces (there are work-arounds if you keep references on the server)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Or in picture form:&lt;/p&gt;
&lt;img src=&quot;https://divillysausages.com/files/socketio_intro/socket-io-nsp-room.png&quot; alt=&quot;Explaining namespaces and rooms&quot; width=&quot;495&quot; height=&quot;456&quot;/&gt;
&lt;h2 id=&quot;msg_to_client&quot;&gt;Sending a message to the client&lt;/h2&gt;
&lt;p&gt;To send a message to the client, we call &lt;code class=&quot;language-javascript&quot;&gt;emit()&lt;/code&gt; on the socket, passing the event name, and whatever data we want to send:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;greetings&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;Hello from the server!&amp;#x27;&lt;/span&gt;, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; );&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then on the client, we add a listener for the &lt;code class=&quot;language-javascript&quot;&gt;'greetings'&lt;/code&gt; event:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;clientSocket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;greetings&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; message, id &lt;/span&gt;) {
	&lt;span class=&quot;hljs-variable language_&quot;&gt;console&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;log&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;Got a message from the server: &amp;quot;&amp;#x27;&lt;/span&gt; + message + &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;&amp;#x27;, my ID is: &amp;quot;&lt;/span&gt; + id );
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that we're sending the &lt;code class=&quot;language-javascript&quot;&gt;socket.id&lt;/code&gt; to the client with the &lt;code class=&quot;language-javascript&quot;&gt;emit()&lt;/code&gt; call. We could have also called &lt;code class=&quot;language-javascript&quot;&gt;clientSocket.id&lt;/code&gt;, but we can use this technique to send the IDs of &lt;em&gt;other&lt;/em&gt; sockets, which will become useful later. The &lt;code class=&quot;language-javascript&quot;&gt;id&lt;/code&gt; is a auto-generated ID set when the socket connects. It'll be different everytime, so there's no point storing it.&lt;/p&gt;
&lt;p&gt;Also note that the only parameter that's actually required is the event name, in this case &lt;code class=&quot;language-javascript&quot;&gt;'greetings'&lt;/code&gt;. Anything after that is sent to the client as parameters for the callback - with one exception: if the last argument is a function, then it'll be used as an &lt;em&gt;ack&lt;/em&gt; - called when receipt of the message is acknowledged. This is a bit more advanced, so for the minute, we don't care.&lt;/p&gt;
&lt;h2 id=&quot;msg_to_server&quot;&gt;Sending a message to the server&lt;/h2&gt;
&lt;p&gt;Sending a message to the server is exactly the same. On the client:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;clientSocket.&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;message&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;Hello from the client&amp;#x27;&lt;/span&gt; );&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;While on the server, we need to add any listeners to the &lt;code class=&quot;language-javascript&quot;&gt;socket&lt;/code&gt; that we get during the &lt;code class=&quot;language-javascript&quot;&gt;'connection'&lt;/code&gt; event:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;// listen for a connection&lt;/span&gt;
sio.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;connection&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; socket &lt;/span&gt;){

	&lt;span class=&quot;hljs-variable language_&quot;&gt;console&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;log&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;User &amp;#x27;&lt;/span&gt; + socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; + &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27; connected&amp;#x27;&lt;/span&gt; );
	
	&lt;span class=&quot;hljs-comment&quot;&gt;// listen for the &amp;#x27;message&amp;#x27; event&lt;/span&gt;
	socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;message&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg &lt;/span&gt;){
		
		&lt;span class=&quot;hljs-variable language_&quot;&gt;console&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;log&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;User &amp;#x27;&lt;/span&gt; + &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; + &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27; sent message &amp;quot;&amp;#x27;&lt;/span&gt; + msg + &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;&amp;quot;&amp;#x27;&lt;/span&gt; );
	});
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that in the &lt;code class=&quot;language-javascript&quot;&gt;socket&lt;/code&gt; callback, you can use &lt;code class=&quot;language-javascript&quot;&gt;this&lt;/code&gt; to reference the socket (or &lt;code class=&quot;language-javascript&quot;&gt;socket&lt;/code&gt; as it'll be bound).&lt;/p&gt;
&lt;p&gt;This is about the height of what you need to do on the client. Everything after this (sending messages to different people etc) happens on the server.&lt;/p&gt;
&lt;h2 id=&quot;msg_to_everyone&quot;&gt;Sending a message to everyone in the namespace&lt;/h2&gt;
&lt;p&gt;To send a message to everyone, instead of calling &lt;code class=&quot;language-javascript&quot;&gt;socket.emit()&lt;/code&gt;, which only sends a message to that socket, we get the namespace itself to send the event.&lt;/p&gt;
&lt;p&gt;In this example, we're using the &lt;code class=&quot;language-javascript&quot;&gt;sio&lt;/code&gt; Server object, created when we created Socket.io on the server.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;tellEveryone&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg &lt;/span&gt;){

	sio.&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;heyEveryone&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; ); &lt;span class=&quot;hljs-comment&quot;&gt;// pass the socket.id so other client know who it&amp;#x27;s from&lt;/span&gt;
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-javascript&quot;&gt;sio.emit()&lt;/code&gt; method here is actually a helper function that gets the default namespace (&lt;code class=&quot;language-javascript&quot;&gt;/&lt;/code&gt;) and calls &lt;code class=&quot;language-javascript&quot;&gt;emit()&lt;/code&gt; on that. The following 3 calls all do the same thing:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;sio.&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;heyEveryone&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; );
sio.&lt;span class=&quot;hljs-property&quot;&gt;sockets&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;heyEveryone&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; ); &lt;span class=&quot;hljs-comment&quot;&gt;// sio.sockets is a reference to the main &amp;quot;/&amp;quot; namespace&lt;/span&gt;
sio.&lt;span class=&quot;hljs-title function_&quot;&gt;of&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;/&amp;#x27;&lt;/span&gt; ).&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;heyEveryone&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; ); &lt;span class=&quot;hljs-comment&quot;&gt;// of( &amp;#x27;/&amp;#x27; ) is how we reference a namespace, in this case the main &amp;quot;/&amp;quot; one&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice that last &lt;code class=&quot;language-javascript&quot;&gt;of( '/' )&lt;/code&gt; call? That's how we reference other namespaces. Remember how I said sockets in one namespace couldn't send messages to sockets in other namespaces? This is how you get around that:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;tellNamespace&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg, namespaceName &lt;/span&gt;){

	sio.&lt;span class=&quot;hljs-title function_&quot;&gt;of&lt;/span&gt;( namespaceName ).&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;heyEveryone&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; ); 	&lt;span class=&quot;hljs-comment&quot;&gt;// &lt;span class=&quot;hljs-doctag&quot;&gt;NOTE:&lt;/span&gt; namespace will be created if it doesn&amp;#x27;t exist&lt;/span&gt;
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;msg_to_room&quot;&gt;Sending a message to everyone in a room&lt;/h2&gt;
&lt;p&gt;To send a message to a room, you need to know it's name. You don't actually need to have joined the room to send a message to it, but you do if you want to receive messages from that room.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;tellRoom&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg, roomName &lt;/span&gt;) {

	socket.&lt;span class=&quot;hljs-title function_&quot;&gt;to&lt;/span&gt;( roomName ).&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;heyThere&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; ); &lt;span class=&quot;hljs-comment&quot;&gt;// &lt;span class=&quot;hljs-doctag&quot;&gt;NOTE:&lt;/span&gt; room will be created if it doesn&amp;#x27;t exist&lt;/span&gt;
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can also use &lt;code class=&quot;language-javascript&quot;&gt;in( roomName )&lt;/code&gt; instead of &lt;code class=&quot;language-javascript&quot;&gt;to()&lt;/code&gt; if you wish.&lt;/p&gt;
&lt;p&gt;You can also send to multiple rooms by chaining the &lt;code class=&quot;language-javascript&quot;&gt;to()&lt;/code&gt; calls:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;tellRooms&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg, room1Name, room2Name &lt;/span&gt;){
	
	socket.&lt;span class=&quot;hljs-title function_&quot;&gt;to&lt;/span&gt;( room1Name ).&lt;span class=&quot;hljs-title function_&quot;&gt;to&lt;/span&gt;( room2Name ).&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;heyThere&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; );
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that on the client, if they've joined multiple rooms, when they get a message, they won't actually know which room (if any) sent it, so it's generally a good idea to pass the room name as one of the parameters.&lt;/p&gt;
&lt;h2 id=&quot;msg_to_everyone_but_you&quot;&gt;Sending a message to everyone except you&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-javascript&quot;&gt;socket&lt;/code&gt; has a &lt;code&gt;broadcast&lt;/code&gt; flag, that when used will send the event to everyone in the namespace/room except for yourself. For a namespace:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;broadcastMsg&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg &lt;/span&gt;) {

	socket.&lt;span class=&quot;hljs-property&quot;&gt;broadcast&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;broadcastFrom&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; );
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or for a room:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;broadcastMsg&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg, roomName &lt;/span&gt;) {

	socket.&lt;span class=&quot;hljs-title function_&quot;&gt;to&lt;/span&gt;( roomName ).&lt;span class=&quot;hljs-property&quot;&gt;broadcast&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;broadcastFrom&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; );
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;msg_to_other_user&quot;&gt;Sending a message to another user&lt;/h2&gt;
&lt;p&gt;To send a message to another user, we can go about it two ways: we can get the socket of the other user from the &lt;code class=&quot;language-javascript&quot;&gt;connected&lt;/code&gt; dictionary on our namespace and call &lt;code class=&quot;language-javascript&quot;&gt;emit()&lt;/code&gt; on it:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;sendToUser&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg, userID &lt;/span&gt;){
	
	&lt;span class=&quot;hljs-comment&quot;&gt;// try and get the socket from our connected list&lt;/span&gt;
	&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; otherSocket = sio.&lt;span class=&quot;hljs-property&quot;&gt;sockets&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;connected&lt;/span&gt;[userID]; &lt;span class=&quot;hljs-comment&quot;&gt;// &lt;span class=&quot;hljs-doctag&quot;&gt;NOTE:&lt;/span&gt; sockets is the default &amp;quot;/&amp;quot; namespace&lt;/span&gt;
	&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt;( otherSocket )
		otherSocket.&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;messageFromUser&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; );
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But remember when I said it's a good idea for the client to know the IDs of other clients? This is because when a socket connects, one of the things that it does it join a room with the same name as its ID. So to send a message to a client, we can use the same logic as sending a message to a room:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;socket.&lt;span class=&quot;hljs-title function_&quot;&gt;on&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;sendToUser&amp;#x27;&lt;/span&gt;, &lt;span class=&quot;hljs-keyword&quot;&gt;function&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt; msg, userID &lt;/span&gt;){

	socket.&lt;span class=&quot;hljs-title function_&quot;&gt;to&lt;/span&gt;( userID ).&lt;span class=&quot;hljs-title function_&quot;&gt;emit&lt;/span&gt;( &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;messageFromUser&amp;#x27;&lt;/span&gt;, msg, socket.&lt;span class=&quot;hljs-property&quot;&gt;id&lt;/span&gt; );
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Technically this means that if we &lt;code class=&quot;language-javascript&quot;&gt;join()&lt;/code&gt; this room, we'd be privy to all the private messages sent to this user, which opens up all sorts of interesting possibilities.&lt;/p&gt;
&lt;h2 id=&quot;getting_ids&quot;&gt;Getting the IDs of connected users&lt;/h2&gt;
&lt;p&gt;If you want to send a list of all the socket IDs in a particular namespace, you can go through the &lt;code class=&quot;language-javascript&quot;&gt;connected&lt;/code&gt; dictionary on the namespace:
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; sids = &lt;span class=&quot;hljs-title function_&quot;&gt;keys&lt;/span&gt;( sio.&lt;span class=&quot;hljs-property&quot;&gt;sockets&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;connected&lt;/span&gt; ); &lt;span class=&quot;hljs-comment&quot;&gt;// sockets = default &amp;quot;/&amp;quot; namespace&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To send all the IDs for a particular room, you need to go through the &lt;code class=&quot;language-javascript&quot;&gt;adapter&lt;/code&gt; property on the namespace:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; socketsInRoom 	= sio.&lt;span class=&quot;hljs-property&quot;&gt;sockets&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;adapter&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;rooms&lt;/span&gt;[roomName]; &lt;span class=&quot;hljs-comment&quot;&gt;// sockets = default &amp;quot;/&amp;quot; namespace&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;var&lt;/span&gt; sids 			= ( socketsInRoom ) ? &lt;span class=&quot;hljs-title function_&quot;&gt;keys&lt;/span&gt;( socketsInRoom ) : []&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2 id=&quot;nickname&quot;&gt;Nicknames&lt;/h2&gt;
&lt;p&gt;To enable nicknames, you simply need to keep a dictionary of socket ids =&amp;gt; nicknames on the server. Then you can either do two things:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Instead of sending the ID to the client, you send the nickname instead. Although this means that you need a way to convert back to the socket ID if you want to send one player to send a message to another, and nicknames generally aren't unique&lt;/li&gt;
	&lt;li&gt;Send the client the socket ID/nickname dictionary, and it's up to them to keep it up-to-date and display the right name. A bit more awkward, but more flexible as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;fin&quot;&gt;Fin&lt;/h2&gt;
&lt;p&gt;So there you have it! You should be set up for firing messages left and right. If there's another scenario you want covered, leave a comment, and I'll add it in.&lt;/p&gt;
&lt;p&gt;In the meantime, check out &lt;a href=&quot;http://socket.io/&quot; class=&quot;external&quot;&gt;Socket.io&lt;/a&gt; and download the v1.3.5 TypeScript definition files below.&lt;/p&gt;</description>
        <pubDate>Sun, 12 Jul 2015 00:28:00 +0000</pubDate>
        <link>https://divillysausages.com/2015/07/12/an-intro-to-socket-io/</link>
        <guid isPermaLink="true">https://divillysausages.com/2015/07/12/an-intro-to-socket-io/</guid>
      </item>
    
  </channel>
</rss>