Portfolio

  1. Unlimited Scroller
    1. Technical Details
  2. Smart Reference
  3. Alio
    1. Design & Mechanics
    2. Art & Style
    3. Implementation
  4. Pragma Timeline
  5. Coin Dash
    1. Design
    2. Implementation
      1. Actor & Actor Property
      2. UI
      3. Game Data
    3. Current Progress

Unlimited Scroller

Unlimited Scroller is a Unity plugin that will improve performance for large scroll views in UGUI. Its core functionality is calculating scroll view padding and cell indices so that only cells visible in the viewport are spawned. Meanwhile, it also supports LRU cache for even faster performance. You can define padding, cell size, and spacing like in regular layout groups. It also supports setting the cache size, and explicitly setting the cell per row.

The setup is simple, just like using regular layout group

To use the plugin, you can follow the instruction here: https://brian-jiang.github.io/UnlimitedScrollUI/. If you are a programmer, you can find scripting documentation here: https://brian-jiang.github.io/UnlimitedScrollUI/api/UnlimitedScrollUI.GridUnlimitedScroller.html.

Technical Details

When you are using a large scroll view, for example, the backpack view that contains lots of items, you will only see a small portion of them at a time. Therefore, we can only spawn the part you will see as game objects and leave other parts blank so the total number of objects under scroll view should be equal to the number of cells visible. It can be achieved by adjusting the padding of the scroll view when the user is scrolling and spawning and destroying cells when they become visible and invisible respectively. The calculation of exact numbers is not difficult as shown in the diagram below.

The LRU cache is also supported so that invisible cells can be stored as a cache instead of destroyed immediately. An empty node will be created at runtime to store these cache cells and the algorithm is implemented using a doubly linked list and queue.

Besides the functions of Unlimited Scroller itself, I also used a build pipeline and docfx to quickly turn XML docs in code to a documentation website, the same pipeline Unity used to generate their package docs. The pipeline configuration was also used in my other projects below.


Smart Reference

Smart Reference is a Unity plugin that will help users reduce direct asset dependency when referencing assets. The key concept is to reference assets using paths instead of Unity’s default referencing. I have written a blog about it: https://xiaojiangbrian.com/2023/06/24/3-ways-to-reduce-load-time-in-runtime-for-unity/. Here’s an excerpt from the blog

The dependency on assets is usually a trap to assets loading if you don’t handle it carefully. Imagine that you use Scriptable Object to store data for your game. There is a class called Monster and it contains a name, description, icon sprite, maxHp, 3d model, attack, and defense. There are probably a total of 100 monsters and stores in 100 SO files respectively. Let’s say you want to get all names and descriptions of monsters, how long do you think it will take to load all 100 files? The result might be surprising if you test it. It will actually load 100 SO files, 100 sprite icons, 100 prefabs for models, 100 meshes, 100 or more materials, all shaders used for these materials, 100 textures used in the material, and more textures if have a normal map or other maps. These are the dependencies of monster SO files. Besides, if you have your own MonoBehaviours attached to the prefab and it references other assets, all of them and their dependencies will load as well. One more bad news is that when Unity loads assets, it loads the asset bundle which may contain other assets depending on how you organize. That’s a whole bunch of memory and time taken just to load names and descriptions of all monsters.

The solution to this problem is straightforward: do not directly reference sprite and prefab in monster SO files. So how can you indirectly reference a prefab? The best way is to store the path of the prefab and load the prefab using the path at runtime. With some editor code and runtime setup, it is possible to have the same workflow as direct referencing. The SmartReference package I mentioned earlier does exactly this. When you reference an asset, unity will serialize it like this and treat it as a dependency

Unity’s default reference

If you use SmartReference, it will instead serialize to this

SmartReference uses path instead of direct reference

The GUID, fileID, and type are all for editor use, the path property is what actually used in runtime. When you try to access the property, it will first load the asset at that path and then return it. The only thing you need to care about is setting up the load function when your game starts. Now if you want to load 100 names and descriptions, only 100 SO files would be loaded. If later you want the model as well, it will also load properly when you explicitly access them.


Alio

Alio is a rhythm game I developed during high school.

Design & Mechanics

The primary gameplay design came from similar game such as Deemo, Cytus, and Lanota, where you need to hit the note on the time matching the music rhythm. The major difference is that you will have a plate and the Settle Line turning. There are 3 kind of notes you need to handle differently.

You need to hit the black note when it reaches the Settle Line. There is a small area for scoring decision, and based on the timing of the hit you can get perfect, good, bad, or miss.

You need to hold down the gray note when it reaches the Settle Line. The scoring decision area is same but there are only two results: perfect or miss.

You need to hit the center when the center note reaches the center, it will have the similar scoring decision area and result as the black note.

Besides three different notes, the plate and Settle Line are also dynamic, creating more variation in levels. They can spinning, fading, and moving accords to the beat to increase difficulty and fun for players.

Alio only has 4 levels but each have several difficulty. Unlike most rhythm games, Alio has different difficulty levels for each song so that each level will have their own arrange.

Art & Style

All 4 levels of Alio use classical music, a deliberate choice not only to circumvent copyright concerns but also to create an aesthetic that resonates with the elegance and sophistication. It should be obvious that black, gray, and white are the theme colors used in Alio, enhancing the game’s connection to its classical music roots, and evoking a sense of nostalgia.

The game’s commitment to this color scheme is not just a stylistic choice but also a narrative device. Each level in Alio has a background with more vibrant color that reflecting the style of music. Colorful effects are also used when making a perfect or good hit that separate from black and white. My purpose was to save the cost of making GUI sprites and to let players consider colorful as a positive feedback.

Implementation

Given that this game only has four levels and my limited programming skills at high school, there is not so much low-level system development, such as asset loading and data saving. However, some techniques are used in the development. I used Scriptable Objects to store all game data and only need to define the note timing and distance from the center, the actual position will be calculated at runtime. Plugins such as iTween Path are also used to make the plate move in a curved path with different ease types to select.

The setup of one level

Besides basic gameplay development, I also learned how to integrate Game Center, achievements, and leaderboards. Building the game and publishing it to the App Store and Google Play is also an intriguing step I managed to finish.


Pragma Timeline

Pragma Timeline is a Unity plugin that capable of handling all bindings easily so that it can play with flexibility at runtime. There are two different bindings in Unity timeline: generic binding and exposed reference. You can add bind objects to bind info, give them names, and update the binding configuration.

Then when you run your game, you can use dictionary to reference dynamic objects with the name you gave.

var map = new Dictionary<string, object> {
    {
        "Logo", new Dictionary<string, object> {
            {"LogoRenderer", go.GetComponentInChildren<SpriteRenderer>()},
            {"LogoAnimator", go.GetComponent<Animator>()}
        }
    },
}

Internally, it uses the playable API and serialized fields to save and restore bindings. You can check my blog for more details: https://xiaojiangbrian.com/2023/06/22/how-does-the-binding-of-unitys-timeline-work/.


Coin Dash

Coin Dash is a game project I’m currently working on.

Design

The main purpose of this game is to let players have experiences that mimic the one they will have when playing a coin pusher. The core gameplay is designed to be shooting a coin forward in an infinite level where new paths and props will be spawned as you move. Each time you shoot the coin, energy will be consumed and you need to recover energy through different ways such as collecting energy points, breaking obstacles, and so on. If the energy drains, the game is over. One mechanic that’s crucial to the game is being able to possess another coin by colliding with it. Coins will be spawned randomly along the way and by hitting on them you can possess that coin and give up your current one. Each coin has different abilities, for example, some coins are faster than others and some coins can grant you several strong shots when you possess them. To preserve the continuity, the game will automatically save after each shot and all states will be restored when you restart the game.

The key to attracting players to this game is the variation of the level and chain reaction after each shoot. With the above mechanics, the game experience would only be like billiards instead of coin pushers. Their difference is that the hangover in the coin pusher is a dynamic object that increases uncertainty and randomness in the game. Therefore, other mechanics that increase dynamic will also add to the game. Several candidates include moving walls, rails, portals, pickups, and so on.

Implementation

I used the Unity engine to make this game and separate the functionalities into several modules. Here are a few core modules.

Actor & Actor Property

The Actor class is defined as a base class that represents any dynamic object involved in game mechanics, so coins and walls are actors where particle effects are not. The ActorProperty is also a base class that contains all data involved in gameplay, like position, rotation, and size. An actor itself is not a game object but it contains a variable called actor proxy, a component that can attach to a game object. The purpose of actor proxy is to separate the actor from the game object and listen to events on the game object, such as OnTriggerEnter. When the game is saved, all actor properties on actors will be serialized alone with their actor type using Memory Pack. Then the binary data will be saved locally or remotely. When the game state needs to be restored, actors will be created with their properties.

UI

A UI manager package is under development along with the game. The core concept of it is to use MVVM that binds C# properties to properties on UI and auto-generate the required C# script. Currently, I’m adding Unlimited Scroller to this UI package to make it more powerful.

Above screenshots shows the binding of a UI panel.

Game Data

Game data refers to all data set by the designer, which are separated from player data. The game data in this game uses Scriptable Object. The data are divided into several categories where most of which have a table-like structure for indexing. With the help of Odin, a flexible game data editor is made.

The game data editor
Current Progress

The low-level system development has mostly finished and the gameplay development is in progress. Meanwhile, more playtesting is planned and changes could be made to the design. With that said, the game with very basic mechanics is now live on itch.io and more iterations on it are expected in the future.


Check all my projects and blogs:

Blog at WordPress.com.