Programming My Life - Unity
  1. AstroVenture and University of Mars

    In this post, I'd like to provide the context for the project I'm currently writing about on this blog. I'm also using this as an opportunity to start a series of blog posts breaking down my recent talk from DjangoConUS 2022 about what I learned related to Django while building this project.

    I've realized since giving the talk there are a few things I'd like to change in/add to it. So instead of a blog post summarizing the talk, I'm opting to revisit the talk and discuss those changes/additions (as well as highlighting the advice I feel holds up). The beginning of the talk covers some of the content of this post. The other sections of the talk will be covered in dedicated posts in the coming weeks.

    In 2018, I founded AstroVenture with my former colleagues from Penn State University. When I worked there from 2010-2014, we built a videogame that teaches introductory astronomy to undergraduate students that we later named 'University of Mars'. We founded the company in order to sell that game to anyone who wants to learn astronomy. If you'd like to check it out, the first quarter of the game is free as a demo at the link above. If you'd like to play more than that, contact me! There is also a video at the site if you just want to see some gameplay to get a better idea of how it works.

    In the nearly 5 years since we founded the company, this has been a side project that I've worked on actively. It is the project that I really dove into and learned Django with. I'd previously had some professional experience with Django, but I had a lot of other responsibilities on that project, so I didn't have time to dig into Django itself as much as I would have liked. The game was built with Unity3D, which I also learned as I built this project.

    The game uses a story to engage students and set up discussions for each topic. The gameplay loop for each lesson is for the game characters to start discussing the topic of the lesson (ranging from gravity to dark energy), then the student explores some type of interactive minigame on the topic, followed by a quiz. Then we usually lead into the next section's discussion and repeat.

    The servers that I manage for the company handle the website, which has the functionality you would expect from a Django website: login, download the game, view content related to the game (mostly in the form of our static 'encyclopedia' pages that accompany the game). Additionally, those servers also track student progress as they play through the game in order for instructors to give credit to students for finishing the different sections of the game. Currently, instructors can download a CSV file with player progress and quiz scores, but I'm working on integrating with Canvas and Blackboard to make grade syncing more direct.

    The backend code for this player tracking was the first API that I ever wrote and was originally written in PHP hosted in a VPS. When I founded the company, I rewrote the API in Django and moved the hosting to AWS. Since my time was limited, I mostly ported the previous API to Django, faults and all. Fortunately, I had some time to dedicate to this recently and I've just finished updating it to be more efficient and use more of Django Rest Framework's built in features. As for the infrastructure, I chose AWS because I had some experience with it, and I had access to free credits there for a limited time. I'll cover more about the infrastructure in a future post.

    We use Sentry for error handling and Stripe for payment processing, and I highly recommend both.

    The front end of the website is in a separate repo written in Typescript[1] with a bit of jQuery sprinkled in. It also uses Bootstrap for CSS. I chose to not use Django for the front end because I thought I might have someone else working on it who wouldn't have familiarity with Django. If I were to start again today, I might do it differently and have everything in one repo. I'm not exactly sure what I'd settle on, though I'd investigate HTMX, Alpine.js, and Tailwind for CSS as I've heard good things about those.

    If any of these technology choices are interesting to you, I'm happy to discuss them more. See the contact links on the about page, or at the bottom of this page.

    In the upcoming posts, I'll talk more about the other sections of the talk: infrastructure, testing, and Django vs DRF.

    1 - This is almost worth a blog post in itself. When I chose to use Typescript, it was because I was frustrated with JavaScript's lack of typing leading to errors (usually with JSON objects) and not being able to easily debug them in larger codebases. I think Typescript is good, but it was also difficult to find good resources (responses on StackOverflow etc.) dealing with Typescript outside of React or Angular, which made getting started a lot more difficult for me.

  2. Signing and Notarizing a Unity App for MacOS

    Edit: A lot of the information in this post is still great, but Apple have recently recommended moving from altool (referenced in the linked scripts below) to notarytool, so I have added an update to this post here.

    For the past few years, Apple's Gatekeeper has made it difficult to run apps downloaded from the internet. Since most of the users of the game that I distribute (more on this soon) aren't technical, we get a significant number of folks needing help even with detailed instructions we supply. I finally had the time to look into code-signing our Unity game for MacOS that would be distributed outside the app store (downloaded from the internet).

    The following two links have almost all of the instructions necessary, but I want to highlight two issues I ran into in case others have a similar problem. And I think I can explain the root issue a little better than I found elsewhere.

    Links: a good example of how to script the process and a walkthrough with much more thorough explanations

    Note that my Macbook is managed by the IT department at my current employer, so this may be an issue only for managed machines.

    First, it isn't specified in either link, but I installed my certificate (the one I created and downloaded from developer.apple.com, see the second link above for more about that) into my 'login' keychain after running into the issue below and reading through some apple developer forum and stack overflow discussions. I'm not sure if that matters, but it seems that is the recommended way.

    I kept getting Warning: unable to build chain to self-signed root for signer when trying to run codesign, and the answer here about the WWDR Intermediate Certification worked for me initially. I installed the linked cert into my System keychain. But I was using the wrong cerification (Distribution, which is for submitting to the store, I think) so notarization failed.

    After getting the correct certification (again, from the instructions in the second link above, and installing into my 'login' keychain), I had to download the intermediate certification Developer ID - G2 (Expiring 09/17/2031 00:00:00 UTC) from here, and I installed that into my System keychain. This got notarization to work!

    I know it was this one because after I added that, my certification (the one I created in my developer account, downloaded and installed to login) changed to 'trusted'. I tried a different one first that didn't change the status of my certification.

    In short, if codesigning is giving you the error above, you are probably missing the intermediate certification from Apple (this was another answer from the developer forum link above, but I didn't understand it when I read it). What this means is that you should determine which type of certification you requested from Apple (Apple Distribution, etc.) and find the matching certification from Apple. It wasn't immediately clear to me which was the matching certification in my case, but I downloaded two that seemed like they might be correct and only one of them changed my cert to trusted in Keychain Access. Good luck!

  3. Unity3D Scriptable Objects

    This week at our local Unity user meetup group, I presented (along with a co-organizer of the group) about Scriptable Objects in Unity. You can find that talk here.

    This is that same content in text form.

    Scriptable objects are a powerful tool in designing and developing games in Unity3D. It took me longer than I’d like to admit to get around to using them, but I’d like to introduce them in such a way that makes it easier for you to just get started using them.

    What is a Scriptable Object (SO)?

    It is a script that derives from ScriptableObject, instead of MonoBehaviour. This script allows the user to create objects either in memory or as .asset files in Unity, which are also referred to as Scriptable Objects.. A simple example:

    using UnityEngine;
    
    [CreateAssetMenu(menuName = "SOs/FloatVar")]
    public class FloatVariable : ScriptableObject
    {
        public float Value;
    
        void MethodName()
        {
            //Do stuff
        }
    }
    

    The line with ‘CreateAssetMenu’ adds a new line to the ‘Create’ menu in the Project window in Unity. When you click that menu item, it will create a new .asset file that has access to the variables and methods defined in your file.

    It does not have access to the standard Update(), Start(), Awake()* methods because those are part of MonoBehaviour. It does derive from Unity’s Object class, so it has access to classes like GameObject, Transform, etc.

    *use OnEnable for initialization instead of Start or Awake

    SOs can contain data and functions just like a MB, but it can’t be attached to a GameObject in the hierarchy as a component. A SO can be referenced by a MB, though.

    Two things to differentiate:

    A script that creates the SO (same as a MB in Project) The SO, which is a .asset file. (lives in Project folder, analogous to an instance of a MB in the hierarchy).

    SOs aren’t meant to replace MBs everywhere in your project. But there are places where they are a better fit for storing data/functions.

    Why use SOs?

    No need for JSON, XML, Text, which means no need to parse data. Can save large amounts of data and optimize data loading when you need it. They don’t get reset when exiting playmode! Since SOs aren’t tied to the scene (not in the hierarchy), you can commit changes to source control without impacting a scene another team member may be working on. This allows you to more easily reference data/functions across scenes when using multiple scenes in development, which I highly recommend you do (this could be a whole other blog post). No need to depend on manager classes to hold all of your references.

    3 and 4 combine to allow us to store the data and functions for a type of enemy and tweak that inside of play mode, have the changes saved, then share that with a teammate without having to worry about impacting the scene file. We also don’t have to re-instantiate prefabs or change every instance of the enemy in a scene (or multiple scenes).

    You may already be doing something similar with prefabs (holding/referencing data and never instantiating that particular prefab). If so, look at SOs! Using prefabs for this purpose is confusing and accident prone (accidentally throw a prefab into a scene, get confused between what is a prefab and what is a data holder).

    If you are a less experienced Unity developer and this seems like a lot to consider, don’t worry about digesting all of it. Just think about some piece of your game design and try to make it as a SO instead of a Monobehaviour such as an enemies stats, or some inventory items.

    If you are more experienced, but some of this doesn’t entirely make sense, please try out SOs for some small use cases to see how they differ from MBs. It took me some time to get used to thinking in terms of using SOs, but they are a great tool for a lot of use cases.

    How to use SOs

    A few Unity Learn examples that demonstrate different use cases for SOs:
    Text Adventure
    Ability System
    Character Select
    Customizing UI

    Two talks about SOs that really helped me understand how and where to use them:
    Richard Fine - Overthrowing the MonoBehaviour Tyranny in a Glorious Scriptable Object Revolution
    Link to the project from the talk

    Game Architecture with Scriptable Objects
    Blog post for the previous talk

    Serialization

    This is how Unity reads out your data attached in scripts. This gets talked about alongside Scriptable Objects sometimes because the serialization that Unity does sometimes messes up scriptable objects. Unity serializes data when it enters/exits play mode and some data types don’t play nice (polymorphic classes for example). If you are having issues with data resetting/corrupting under those circumstances, check these out:
    Forum post on Serialization and Scriptable Objects
    Blog post from Lucas Meijer
    Blog Post from Tim Cooper
    Talk by Richard Fine

  4. Unity 2D Tools for Level Building

    This week for our local Unity meetup group, I presented an intro to some of the new 2D Tools in Unity (There is an intro about more general Unity topics, so for the 2D stuff skip to 17 minutes in): https://www.youtube.com/watch?v=xopzxmzFJUs

    Here are the links to things I mentioned were outside of the scope of that talk but might be interesting to learn:

    Sprite masks: https://docs.unity3d.com/Manual/class-SpriteMask.html

    2D side scrolling brawler style camera (focus on 9-slicing sprites and new features for sorting): https://unity3d.com/learn/tutorials/topics/2d-game-creation/introduction-and-goals

    Platformer character controller: https://unity3d.com/learn/tutorials/topics/2d-game-creation/intro-and-session-goals

    https://github.com/MelvynMay/UnityPhysics2D - a lot of interesting scenes demoing 2D physics.

    Pretty cool topdown game from Unity to show off tilemap and other 2d features (from a talk at Unite Austin, >https://www.youtube.com/watch?v=RkaEh--qUAY>): https://github.com/Unity-Technologies/2d-gamedemo-robodash

    2D Game Kit - This is a 2D Game that Unity built to show off 2D features, and what a complete project looks like including tools for designers so that they don’t need to dive into the code to create new puzzles, levels, etc. https://blogs.unity3d.com/2018/02/13/introducing-2d-game-kit-learn-unity-with-drag-and-drop/, https://unity3d.com/learn/tutorials/s/2d-game-kit. Unity also recorded a live training for this recently that I’m assuming they will publish soon, but I can’t find a link to it yet.

    Edit: Unity posted the live training for 2D Game Kit here: https://unity3d.com/learn/tutorials/projects/2d-game-kit/overview-and-goals?playlist=49633

    What I covered in the video is using the new Tilemaps and associated features for designing levels in 2D. This was also covered by this Unity Learn tutorial: https://unity3d.com/learn/tutorials/topics/2d-game-creation/intro-2d-world-building-w-tilemap, and this blog post: https://blogs.unity3d.com/2018/01/25/2d-tilemap-asset-workflow-from-image-to-level/. This are very thorough and a great reference for these features. I found that there were a couple of things I could talk about not covered in those videos, specifically how to create your own rule and random tiles, and how to create tiles and tilemaps from art that you generate or find yourself.

    Finally, here is the collection of brushes and tiles that Unity has coded that cover a huge range of use cases: https://github.com/Unity-Technologies/2d-extras

    The ground sprites I used came from here: https://bitcan.itch.io/tileset-simples

    And the flower sprites I used came from here: https://onimaru.itch.io/green-platform