• # Developing an Isometric Plugin for Unity3D

Engines and Middleware

• Posted By MATov

It's a story on how to write a plugin for Unity Asset Store, take a crack at solving the well-known isometric problems in games, and make a little coffee money from that, and also to understand how expandable Unity editor is. Pictures, code, graphs and thoughts inside.

# Prologue

So, it was one night when I found out I had pretty much nothing to do. The coming year wasn't really promising in my professional life (unlike personal one, though, but that's a whole nother story). Anyway, I got this idea to write something fun for old times sake, that would be quite personal, something on my own, but still having a little commercial advantage (I just like that warm feeling when your project is interesting for somebody else, except for your employer). And all this went hand in hand with the fact that I have long awaited to check out the possibilities of Unity editor extension and to see if there's any good in its platform for selling the engine's own extensions.

I devoted one day to studying the Asset Store: models, scripts, integrations with various services. And first, it seemed like everything has already been written and integrated, having even a number of options of different quality and detail levels, just as much as prices and support. So right away I've narrowed it down to:

• code only (after all, I'm a programmer)
• 2D only (since I just love 2D and they've just made a decent out-of-the-box support for that in Unity)

And then I remembered just how many cactuses I've ate and how many mice've died when we were making an isometric game before. You won't believe how much time we've killed on searching viable solutions and how many copies we've broken in attempts to sort out this isometry and draw it. So, struggling to keep my hands still, I searched by different key and not-so-much-key words and couldn't find anything except a huge pile of isometric art, until I finally decided to make an isometric plugin from scratch.

## Setting the goals

The first I need was to describe in short what problems this plugin was supposed to solve and what use the isometric games developer would make of it. So, the isometry problems are as follows:

• sorting objects by remoteness in order to draw them properly
• extension for creation, positioning and displacement of isometric objects in the editor

Thus, with the main objectives for the first version formulated, I set myself  2-3 days deadline for the first draft version. Thus couldn't being deferred, you see, since enthusiasm is a fragile thing and if you don't have something ready in the first days, there's a great chance you ruin it. And New Year holidays are not so long as the might seem, even in Russia, and I wanted to release the first version within, like, ten days.

## Sorting

To put it short, isometry is an attempt made by 2D sprites to look like 3D models. That, of course, results in dozens of problems. The main one is that the sprites have to be sorted in the order in which they were to be drawn to avoid troubles with mutual overlapping.

On the screenshot you can see how it's the green sprite that is drawn first (2,1), and then the blue one goes (1,1)

The screenshot shows the incorrect sorting when the blue sprite's drawn first

In this simple case sorting won't be such a problem, and there are going to be  options, for example:

- sorting by position of Y on the screen, which is (isoX + isoY) * 0.5 + isoZ
- drawing from the remotest isometric grid cell from left to right, from top to down [(3,3),(2,3),(3,2),(1,3),(2,2),(3,1),...]
- and a whole bunch of other interesting and not really interesting ways

They all are pretty good, fast and working, but only in case of such single-celled objects or columns extended in isoZ direction After all, I was interested in more common solution that would work for the objects extended in one coordinate's direction, or even the "fences" which have absolutely no width, but are extended in the same direction as the necessary height.

The screenshot shows the right way of sorting extended objects 3x1 and 1x3 with "fences" measuring 3x0 and 0x3

And that's where our troubles begin and put us in place where we have to decide on the way forward:

• split "multi-celled" objects into "single-celled" ones, i.e. to cut it vertically and then sort the stripes emerged
• think about the new sorting method, more complicated and interesting

I chose the second option, having no particular desire to get into tricky processing of every object, into cutting (even automatic), and special approach to logic. For the record, they used the first way in few famous games like Fallout 1 and Fallout 2. You can actually see those strips if you get into the games' data.

So, the second option doesn't imply any sorting criteria. It means that there is no pre-calculated value by which you could sort objects. If you don't believe me (and I guess many people who never worked with isometry don't), take a piece of paper and draw small objects measuring like 2x8 and, for example, 2x2. If you somehow manage to figure out a value for calculation its depth and sorting - just add a 8x2 object and try to sort them in different positions relative to one another.

So, there's no such value, but we still can use dependencies between them (roughly speaking, which one's overlapping which) for topological sorting. We can calculate the objects' dependencies by using projections of isometric coordinates on isometric axis.

Screenshot shows the blue cube having dependency on the red one

Screenshot shows the green cube having dependency on the blue one

A pseudocode for dependency determination for two axis (same works with Z-axis):

bool IsIsoObjectsDepends(IsoObject obj_a, IsoObject obj_b) {
var obj_a_max_size = obj_a.position + obj_a.size;
return
obj_b.position.x < obj_a_max_size.x &&
obj_b.position.y < obj_a_max_size.y;
}

With such an approach we build dependencies between all the objects, passing among them recursively and marking the display Z coordinate. The method is quite universal, and, most importantly, it works. You can read detailed description of this algorithm, for example, here or here. Also they use this kind of approach in popular flash isometric library (as3isolib).

And everything was just great except that time complexity of this approach is O(N^2) since we've got to compare every object to every other one in order to create the dependencies. I've left optimization for later versions, having added only lazy re-sorting so that nothing would be sorted until something moves. So we're going to talk about optimization little bit later.

## Editor extension

From now on, I had the following goals:

• sorting of objects had to work in the editor (not only in a game)
• there had to be another kind of Gizmos-Arrow (arrows for moving objects)
• optionally, there would be an alignment with tiles when object's moved
• sizes of tiles would be applied and set in the isometric world inspector automatically
• AABB objects are drawn according to their isometric sizes
• output of isometric coordinates in the object inspector, by changing which we would change the object's position in the game world

And all of these goals have been achieved. Unity really does allow to expand its editor considerably. You can add new tabs, windows, buttons, new fields in object inspector. If you want, you can even create a customized inspector for a component of the exact type you need.  You can also output additional information in the editor's window (in my case, on AABB objects), and replace standard move gizmos of objects, too. The problem of sorting inside the editor was solved via this magic ExecuteInEditMode tag, which allows to run components of the object in editor mode, that is to do it the same way as in a game.

All of these were done, of course, not without difficulties and tricks of all kinds, but there was no single problem that I'd spent more than a couple of hours on (Google, forums and communities sure helped me to resolve all the issues arisen which were not mentioned in documentation).

Screenshot shows my gizmos for movement objects within isometric world

## Release

So, I got the first version ready, took the screenshot. I even drew an icon and wrote a description. It's time. So, I set a nominal price of \$5, upload the plugin in the store and wait for it to be approved by Unity. I didn't think over the price much, since I didn't really want to earn big money yet. My purpose was to find out if there is a general demand and if it was, I would like to estimate it. Also I wanted to help developers of isometric games who somehow ended up absolutely deprived of opportunities and additions.

In 5 rather painful days (I spent about the same time writing the first version, but I knew what I was doing, without further wondering and overthinking, that gave me the higher speed in comparison with people who'd just started working with isometry) I got a response from Unity saying that the plugin was approved and I could already see it in the store, just as well as its zero (so far) sales. It checked in on the local forum, built Google Analytics into the plugin's page in the store and prepared myself to wait the grass to grow.

It didn't take very long before first sales, just as feedbacks on the forum and the store came up. For the remaining days of January 12 copies of my plugin have been sold, which I considered as a sign of the public's interest and decided to continue.

## Optimization

So, I was unhappy with two things:

• Time complexity of sorting - O(N^2)
• Troubles with garbage collection and general performance

### Algorithm

Having 100 objects and O(N^2) I had 10,000 iterations to make just to find dependencies, and also I'd have to pass all of them and mark the display Z for sorting. There should've been some solution for that. So, I tried a huge number of options, could not sleep thinking about this problem. Anyway, I'm not going to tell you about all the methods I've tried, but I'll describe the one that I've found the best so far.

First thing first, of course, we sort only visible objects. What it means is that we constantly need to be know what's in our shot. If there is any new object, we got to add it in the sorting process, and if one of the old one's gone - ignore it. Now, Unity doesn't allow to determine the object's Bounding Box together with its  children in the scene tree. Pass over the children (every time, by the way, since they can be added and removed) wouldn't work - too slow. We also can't use OnBecameVisible and other events because these work only for parent objects. But we can get all Renderer components from the necessary object and its children. Of course, it doesn't sound like our best option, but I couldn't find another way, same universal and acceptable by performance.

List<Renderer> _tmpRenderers = new List<Renderer>();

bool IsIsoObjectVisible(IsoObject iso_object) {
iso_object.GetComponentsInChildren<Renderer>(_tmpRenderers);
for ( var i = 0; i < _tmpRenderers.Count; ++i ) {
if ( _tmpRenderers[i].isVisible ) {
return true;
}
}
return false;
}

There is a little trick of using GetComponentsInChildren function that allows to get components without allocations in the necessary buffer, unlike another one that returns new array of components

Secondly, I still had to do something about O(N^2). I've tried a number of space splitting techniques before I stopped at a simple two-dimensional grid in the display space where I project my isometric objects. Every such sector contains a list of isometric objects that are crossing it. So, the idea is simple: if projections of the objects are not crossed, then there's no point in building dependencies between the objects at all. Then we pass over all visible objects and build dependencies only in the sectors where it's necessary, thereby lowering time complexity of the algorithm and increasing performance. We calculate the size of each sector as an average between the sizes of all objects. I found the result more than satisfying.

### General performance

Of course, I could write a separate article on this... Okay, let's try to make this short. First, we're cashing the components (we use GetComponent to find them, which is not fast). I recommend everyone to be watch yourselves when working with anything that has to do with Update. You always have to bear in mind that it happens for every frame, so you've got to be really careful Also, remember about all interesting features like custom == operator. There are a lot to things to keep in mind, but in the end you get to know about every one of them in the built-in profiler. It makes it much easier to memorize and use them

Also you get to really understand the pain of garbage collector. Need higher performance? Then forget about anything that can allocate memory, which in C# (especially in old Mono compiler) can be done by anything, ranging from foreach(!) to emerging lambdas, let alone LINQ which is now prohibited for you even in the simplest cases. In the end instead of C# with its syntactic sugar you get a semblance of C with ridiculous capacities.

Here I'm gonna give some links on the topic you might find helpful: Part1, Part2, Part3.

### Results

I've never known anybody using this optimization technique before, so I was particularly glad to see the results. And if in the first versions it took literally 50 moving objects for the game to turn it into a slideshow, now it works pretty well even when there're 800 objects in a frame: everything's spinning at top speed and re-sorting for just for 3-6 ms which is very good for this number of objects in isometry. Moreover, after initialization it almost haven't allocate memory for a frame

## Further opportunities

After I read feedbacks and suggestions, there were a few features which I added in the past versions.

### 2D/3D Mixture

Mixing 2D and 3D in isometric games is an interesting opportunity allowing to minimize drawing of different movement and rotations options (for instance, 3D models of animated characters). It's not really hard thing to do, but requires integration within the sorting system. All you need is to get a Bounding Box of the model with all its children, and then to move the model along the display Z by the box's width.

Bounds IsoObject3DBounds(IsoObject iso_object) {
var bounds = new Bounds();
iso_object.GetComponentsInChildren<Renderer>(_tmpRenderers);
if ( _tmpRenderers.Count > 0 ) {
bounds = _tmpRenderers[0].bounds;
for ( var i = 1; i < _tmpRenderers.Count; ++i ) {
bounds.Encapsulate(_tmpRenderers[i].bounds);
}
}
return bounds;
}

that's an example of how you can get **Bounding Box** of the model with all its children

and that's what it looks like when it's done

### Custom isometric settings

That is relatively simple. I was asked to make it possible to set the isometric angle, aspect ratio, tile height. After suffering some pain involved in maths, you get something like this:

### Physics

And here it gets more interesting. Since isometry simulates 3D world, physics is supposed to be three-dimensional, too, with height and everything. I came up with this fascinating trick. I replicate all the components of physics, such as Rigidbody, Collider and so on, for isometric world. According to these descriptions and setups I make the copy of invisible physical three-dimensional world using the engine itself and built-in PhysX. After that I take the simulation data calculated and get those bacl in duplicating components for isometric world. Then I do the same to simulate bumping and trigger events.

The toolset physical demo GIF

## Epilogue and conclusions

After I implemented all the suggestions from the forum, I decided to raise the price up to 40 dollars, so it wouldn't look like just another cheap plugin with five lines of code I will be very much delighted to answer questions and listen to your advices. I welcome all kinds of criticism, thank you!

Unity Asset Store page link: Isometric 2.5D Toolset

Report Article

## User Feedback

There are no comments to display.

## Create an account

Register a new account

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 0
• 0
• 4
• 3
• 0

• 10
• 13
• 9
• 25
• 18
• ### Similar Content

• So this is the problem that I have :- https://youtu.be/kU8Dm5bDJXg
This is the code i am using:-
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace SA { public class AnimatorHook : MonoBehaviour { Animator anim; StateManager states; public void Init(StateManager st) { states = st; anim = st.anim; } void OnAnimatorMove() { if (!states.canMove) anim.ApplyBuiltinRootMotion(); states.rigid.drag = 0; float multiplier = 1; Vector3 delta = anim.deltaPosition; delta.y = 0; Vector3 v = (delta * multiplier) / states.delta; states.rigid.velocity = v; } } } For additional reference see the following code
using System.Collections; using System.Collections.Generic; using UnityEngine; namespace SA { public class StateManager : MonoBehaviour { [Header("Init")] public GameObject activeModel; [Header("Inputs")] public float vertical; public float horizontal; public float moveAmount; public Vector3 moveDir; public bool rt, rb, lt, lb; [Header("Stats")] public float moveSpeed = 5f; public float runSpeed = 8f; public float rotateSpeed = 20; public float toGround = 0.5f; [Header("States")] public bool onGround; public bool run; public bool lockOn; public bool inAction; public bool canMove; [Header("Other")] public EnemyTarget lockOnTarget; [HideInInspector] public Animator anim; [HideInInspector] public Rigidbody rigid; [HideInInspector] public AnimatorHook a_hook; [HideInInspector] public float delta; [HideInInspector] public LayerMask ignoreLayers; float _actionDelay; public void Init() { SetupAnimator(); rigid = GetComponent<Rigidbody>(); rigid.angularDrag = 999; rigid.drag = 4; rigid.constraints = RigidbodyConstraints.FreezeRotationX | RigidbodyConstraints.FreezeRotationZ; a_hook = activeModel.AddComponent<AnimatorHook>(); a_hook.Init(this); gameObject.layer = 8; ignoreLayers = ~(1 << 9); anim.SetBool("onGround",true); } void SetupAnimator() { if(activeModel == null) { anim = GetComponentInChildren<Animator>(); if(anim == null) { Debug.Log("no model found"); } else { activeModel = anim.gameObject; } } if(anim == null) { anim = activeModel.GetComponent<Animator>(); } //anim.applyRootMotion = false; } public void FixedTick(float d) { delta = d; rigid.drag = (moveAmount > 0 || !onGround) ? 0 : 4; DetectAction(); if (inAction) { // anim.applyRootMotion = true; _actionDelay += delta; if(_actionDelay > 0.3f) { inAction = false; _actionDelay = 0; } else { return; } } canMove = anim.GetBool("canMove"); if (!canMove) { return; } //anim.applyRootMotion = false; float targetSpeed = moveSpeed; if (run) targetSpeed = runSpeed; if(onGround) rigid.velocity = moveDir * (targetSpeed * moveAmount); /* if (run) lockOn = false; */ Vector3 targetDir = (lockOn == false) ? moveDir : lockOnTarget.transform.position - transform.position; targetDir.y = 0; if (targetDir == Vector3.zero) targetDir = transform.forward; Quaternion tr = Quaternion.LookRotation(targetDir); Quaternion targetRotation = Quaternion.Slerp(transform.rotation, tr, delta * moveAmount * rotateSpeed); transform.rotation = targetRotation; anim.SetBool("lockon", lockOn); if (lockOn == false) HandleMovementAnimations(); else HandleLockOnAnimations(moveDir); } public void DetectAction() { if (canMove == false) return; if (rb == false && rt == false && lt == false && lb == false) return; string targetAnim = null; if (rb) targetAnim = "Sword And Shield Attack"; if (rt) targetAnim = "Stable Sword Outward Slash"; if (lb) targetAnim = "Standing Melee Attack Horizontal"; if (lt) targetAnim = "Sword And Shield Slash (1)"; if (string.IsNullOrEmpty(targetAnim)) return; canMove = false; inAction = true; anim.CrossFade(targetAnim,0.2f); //rigid.velocity = Vector3.zero; } public void Tick(float d) { delta = d; onGround = OnGround(); anim.SetBool("onGround", onGround); } void HandleMovementAnimations() { anim.SetBool("run", run); anim.SetFloat("Vertical", moveAmount ,0.4f,delta); } void HandleLockOnAnimations(Vector3 moveDir) { Vector3 relativeDir = transform.InverseTransformDirection(moveDir); float h = relativeDir.x; float v = relativeDir.z; anim.SetFloat("Vertical", v, 0.2f, delta); anim.SetFloat("Horizontal", h, 0.2f, delta); } public bool OnGround() { bool r = false; Vector3 origin = transform.position + (Vector3.up * toGround); Vector3 dir = -Vector3.up; float dis = toGround + 0.3f; RaycastHit hit; if(Physics.Raycast(origin,dir,out hit,dis)) { r = true; Vector3 targetPosition = hit.point; transform.position = targetPosition; } return r; } } } I've been stuck on this for too long, any help will be highly appreciated
• By jb-dev
This is a short .gif showing off visuals effects for the parrying mechanic

Can programmers art? How far can creativity and programming take you?
I have summarized what I learned in several months into 7 key techniques to improve the visual quality of your game.

"Programmer art" is something of a running joke. For those unfamiliar with the term, it refers to the "placeholder" or "throw-together" art that programmers tend to use while developing games.
Some of us don't have the necessary artistic skills, however, sometimes we just can't be bothered to put in the effort. We're concerned about the technical side of things working - art can come later.
Here's what this usually means -

I worked on a game jam with some new people a few months ago. I just wanted to make sure that my gameplay and AI code was doing what it was supposed to do. This would have to interface with code from other teammates as well, so it was important to test and check for bugs. This was the result.
That's not what I'm going to talk about today though.

I'm going to take a different angle on "programmer art" - not the joke art that programmers often use, but the fact that there's a LOT that a programmer can do to improve the visual appeal of a game. I believe some of this falls under "technical art" as well.

My current job kind of forced me to think in this capacity.
I was tasked with visualizing some scientific data. Though this data was the result of years of hard work on the part of scientists, the result was unimpressive to the untrained eye - a heap of excel files with some words and numbers.
There are very few people in the world who can get excited by seeing a few excel files.
My job? To make this data exciting to everyone else.
My task was to visualize connectome data for a famous worm known as C. Elegans, made available by the wonderful people working on the OpenWorm project.
Part of the data parsing to read and display the data as a worm's body with neurons on it was done by my teammate. My main task was to improve the visuals and the overall graphical quality.

The first thing that comes to mind is using HD textures, PBR materials and high-poly models. Add in a 3D terrain using a height map, some post-processing and HDR lighting, and BOOM! Gorgeous 3D scene. I'm sure you've all seen loads of those by now.
Except, almost none of that would really help me.
The idea was very abstract - neurons and connections visible in a zoomed-in, x-ray-like view of a worm. I don't think rolling hills would have helped me much.
I had no 3D modelling skills or access to an artist - even if I did, I'm not sure what kind of 3D models would have helped.

As a result, what I've made isn't a gorgeous 3D environment with foliage and god-rays and lens flares. So it's not applicable in every case or the perfect example of how a programmer can make a gorgeous game.
But, it does provide a distinct viewpoint and result. The special sets of constraints in the problem I had to solve led to this.
So here's what I actually did:

The 7 things I did to improve the visuals of my Unity game
1. Conceptualizing the look
This could be considered a pre-production step for art or any visual project. Ideally, what should it look like? What's the goal? What are your references?
In this case, the viewer had a hologram-like feel to it (also there were plans to port it to a HoloLens eventually). I liked the idea of a futuristic hologram. And the metaphor of "AI bringing us towards a better future".
So what were my references? Sci-fi of course!
My first pick was one of my favourite franchises - Star Wars. I love how the holo-comms look in the movies.

Holograms became a key component of my design.
This is a HUD design from Prometheus that I found on Google -

In this case, the colours appealed to me more than the design itself. I ended up basing the UI design on this concept.

Key takeaway - Your imagination is the very first tool that helps you create impressive art. Use references! It's not cheating - it's inspiration. Your references will guide you as you create the look that you want.

I had some shader programming experience from University - D3D11 and HLSL. But that work had been about building a basic graphics engine with features like lighting, shadows, and some light post-processing. I had done some light Shader programming in Unity before as well.
What I really needed now was impressive visual effects, not basic lighting and shadows.
I was really lucky that this was about the time Unity made Shader Graph available, which made everything much easier. I can write Shader code, but being able to see in real time what each node (Which can be considered a line of code) does makes it so much easier to produce the effects you want.
I familiarized myself with all the samples Unity had included with this new tool. That wouldn't have been enough though. Thankfully due to my previous experience with Shaders, I was able to make some adjustments and improvements to make them suit my needs.
Some tweaking with speed, scaling, colours, and textures led to a nice hologram effect for the UI panels.

I wanted the viewer to feel good to interact with as well, and some work implementing a glow effect (alongside the dissolve effects) led to this -

Key takeaway - Shaders are an extremely powerful tool in a Game Programmer's repertoire. Tools like Unity's Shader Graph, the old Shader Forge asset, and Unreal's material editor make Shaders more accessible and easier to tune to get the exact look you want.
PS - Step 5 below is also really important for getting a nice glow effect.

3. Visual Effects and Animations using Shaders
I was able to extend the dissolve and hologram shaders to fake some animation-like visual effects.
And a combination of some timed Sine curves let me create an animation using the dissolve effect -

The work here was to move the animation smoothly across individual neuron objects. The animation makes it look like they're a single connected object, but they're actually individual Sphere meshes with the Shader applied to them. This is made possible by applying the dissolve texture in World Space instead of Object Space.
A single shader graph for the neurons had functionality for colour blending, glow, and dissolve animation.
All of this made the graphs really large and difficult to work with though. Unity was constantly updating the Shader Graph tools, and the new updates include sub-graphs which make it much easier to manage.
Key takeaway - There is more to shaders than meets the eye. As you gain familiarity with them, there are very few limits to the effects you can create. You can create animations and visual effects using Shaders too.

4. Particle systems - more than just trails and sparks
I have no idea why I put off working with the particle systems for so long!
The "neurons" in the viewer were just spheres, which was pretty boring.
Once I started to understand the basics of the particle system, I could see how powerful it was. I worked on some samples from great YouTube tutorials - I'm sharing a great one by Gabriel Aguiar in the comments below.
After that, I opened up Photoshop and experimented with different brushes to create Particle textures.
Once again, I referred to my sources of what neurons should look like. I wanted a similar look of "hair-like" connections coming out of the neurons, and the core being bright and dense.
This is what it looked like finished, and the particle system even let me create a nice pulsating effect.

Part of my work was also parsing a ton of "playback data" of neurons firing. I wanted this to look like bright beams of light, travelling from neuron to neuron. This involved some pathfinding and multi-threading work as well.

Lastly, I decided to add a sort of feedback effect of neurons firing. This way, you can see where a signal is originating and where it's ending.

Key takeaway - Particle systems can be used in many ways, not just for sparks and trails. Here, I used them to represent a rather abstract object, a neuron. They can be applied wherever a visual effect or a form of visual "feedback" seems relevant.

5. Post-processing to tie the graphics and art together
Post-processing makes a HUGE difference in the look of a game scene. It's not just about colours and tone, there's much more to it than that. You can easily adjust colours, brightness, contrast, and add effects such as bloom, motion blur, vignette, and screen-space reflections.
First of all, Linear colour space with HDR enabled makes a huge difference - make sure you try this out.
Next, Unity's new post-processing stack makes a lot of options available without impacting performance much.
The glow around the edges of the sphere only appears with an HDR colour selected for the shader, HDR enabled, and Linear colour space. Post-processing helps bump this up too - bloom is one of the most important settings for this.
Colour grading can be used to provide a warm or cool look to your entire scene. It's like applying a filter on top of the scene, as you would to an image in Photoshop. You can completely override the colours, desaturate to black and white, bump up the contrast, or apply a single colour to the whole scene.

There is a great tutorial from Unity for getting that HD look in your scenes - if you want a visible glow you normally associate with beautiful games, you need to check this out.

Key takeaway - Post processing ties everything together, and helps certain effects like glows stand out.

6. Timing and animation curves for better "feel"
This is a core concept of animation. I have some training in graphic design and animation, which is where I picked this up. I'm not sure about the proper term for it - timing, animation curves, tween, etc.
Basically, if you're animating something, it's rarely best to do it with linear timing. Instead, you want curves like this -

Or more crazy ones for more "bouncy" or cartoon-ish effects.
I applied this to the glow effects on the neurons, as I showed earlier.
And you can use this sparingly when working with particle systems as well - for speed, size, and similar effects. I used this for the effect of neurons firing, which is like a green "explosion" outwards. The particles move outwards fast and then slow down.
Unity has Animation Curve components you can attach to objects. You can set the curve using a GUI and then query it in your C# scripts. Definitely worth learning about.
Key takeaway - Curves or tweens are an animation concept that is easy to pick up and apply. It can be a key differentiator for whether your animations and overall game look polished or not.

7. Colour Palettes and Colour Theory - Often overlooked
Colour is something that I tend to experiment with and work with based on my instincts. I like being creative, however, I really underestimated the benefits of applying colour theory and using palettes.
Here's the before -

Here are some of the afters -

I implemented multiple themes because they all looked so good.
I basically messed around with different types of "Colour harmony" - Monochrome, triad, complementary, and more. I also borrowed some colours from my references and built around that.
Key takeaway - Don't underestimate the importance of colour and colour theory. Keep your initial concept and references in mind when choosing colours. This adds to that final, polished look you want.

Bonus - consider procedural art
Procedural Generation is just an amazing technique. I didn't apply it on this project, but I learned the basics of it such as generating Value and Perlin noise, generating and using Height maps for terrains, and generating mazes.

Procedural art is definitely something I want to explore more.
A couple of interesting things (Links in the "extra resources" section below) -
Google deepdream has been used to generate art. There's an open-source AI project that can colour lineart. Kate Compton has a lot of interesting projects and resources about PCG and generative art. I hope this leads to tools that can be directly applied to Game Development. To support the creation of art for games. I hope I get the opportunity to create something like that myself too.
Conclusion
These 7 techniques were at the core of what I did to improve the visual quality of my project.
This was mostly the result of the unique set of constraints that I had. But I'm pretty sure some famous person said: "true creativity is born of constraints". Or something along those lines. It basically means that constraints and problems help channel your creativity.
I'm sure there is more that I could have done, but I was happy with the stark difference between the "before" and "after" states of my project.
I've also realized that this project has made me more of an artist. If you work on visual quality even as a programmer, you practice and sharpen your artistic abilities, and end up becoming something of an artist yourself.

Did I miss something obvious? Let me know in the comments!

Extra Resources
OpenWorm project
Great tutorial by Gabriel Aguiar
Unity breaks down how to improve the look of a game using Post processing
Another resource on post-processing by Dilmer Valecillos
Brackey's tutorial on post-processing
Adobe Colour wheel, great for colour theory and palettes
An open-source AI project that can colour lineart
A demo of generative art by Kate Compton

• THE PROJECT

INT is a 3D Sci-fi RPG with a strong emphasis on story, role playing, and innovative RPG features such as randomized companions. The focus is on the journey through a war-torn world with fast-paced combat against hordes of enemies. The player must accomplish quests like a traditional RPG, complete objectives, and meet lively crew members who will aid in the player's survival. Throughout the game you can side and complete missions through criminal cartels, and the two major combatants, the UCE and ACP, of the Interstellar Civil War.
Please note that all of our current positions are remote work. You will not be required to travel.
INT Official website
IndieDB page
Also follow social media platforms for the latest news regarding our projects.

TALENTS NEEDED
3D Animator
3D Character Modeler
3D Environment Modeler
Unity Engine Programmer

REVENUE-SHARE
The project is marching increasingly closer to be ready for our crowd-funding campaign. Being an Indie team we do not have the creative restrictions often imposed by publishers or other third parties. We are extremely conscientious of our work and continuously uphold a high level of quality throughout our project.
We are unable to offer wages or per-item payments at this time. However revenue-sharing from crowd-funding is offered to team members who contribute 15-20 hours per week to company projects, as well as maintain constant communication and adhere to deadlines. Your understanding is dearly appreciated.

TO APPLY
Please send your Cover Letter, CV, Portfolio (if applicable), and other relevant documents/information to this email: JohnHR@int-game.net
Thank you for your time! Please feel free to contact me via the email provided should you have any questions or are interested to apply for this position. We look forward to hearing from you!
John Shen
Starboard Games LLC

• Hello, I'm currently searching for additional talented and passionate members for our team that's creating a small horror game.

About the game: The game would be a small sci-fi/post-apocalyptic survival horror 3D game with FPS (First person shooter) mechanics and an original setting and story based in a book (which I'm writing) scene, where a group of prisoners are left behind in an abandoned underground facility. It would play similar to Dead Space combined with Penumbra and SCP: Secret Laboratory, with the option of playing solo or multiplayer.

Engine that'd be used to create the game: Unity

About me: I'm a music composer with 4 years of experience and I'm fairly new in this game development world, and I'm currently leading the team that'd be creating this beautiful and horrifying game. I decided that making the book which I'm writing into a game would be really cool, and I got more motivated about doing so some time ago when I got a bunch of expensive Unity assets for a very low price. However, I researched about how to do things right in game development so I reduced the scope of it as much as I could so that's why this game is really based in a scene of the book and not the entire thing. Also I'm currently learning how to use Unity and learning how to program.

Our team right now consists of: Me (Game Designer, Creator, Music Composer, Writer), 3 3D Modelers, 3 Game Programmers, 1 Sound Effect Designer, 1 Concept Artist, 1 3D Animator and 1 Community Manager.

Who am I looking for: We are looking for a talented, passionate and experienced 3D Environment Modeler who is able to create high poly realistic tunnels, rooms, props, in other words, small closed environments and is able to also texture them with a sci-fi/horror vibe.
Right now the game is in mid-early development and you can see more information about it and follow our progress in our game jolt page here: https://gamejolt.com/games/devilspunishment/391190 . We expect to finish some sort of prototype in 2-3 months from now.

This is a contract rev-share position

If you are interested in joining, contributing or have questions about the project then let's talk. You can message me in Discord: world_creator#9524
×