“The Poorly-lit Exploration Project” was an independent project set out to push my own limits and learn new knowledge in Unity.
My primary objective was to pick up at least 20 objects, and invoke a particle system whenever the player picks them up. I also wanted to test my skills as a narrative design, so my other goal is to tell a simple story through this experience.
It didn’t take me long to come up with a story, or at least a background setting. After all this was just a small game, or even just a demo, and I like to think of it as a teaser trailer, a playable demo of a much bigger game like the unfortunate P.T.. I have to admit that my recent mindset has been greatly influenced by the games just released: Horizon: Zero Dawn, The Last Guardian, and even Ghost Recon: Wildlands. All of these titles share some common keywords: ancient / mystery / aboriginal.
So I started out by searching for some graphics. Then this image flashed my mind:
It’s the light orb flashing in Sonic Adventure (Dreamcast / GameCube) (great game 10/10). The story behind this light fits all the keywords above as well.
So I thought to myself: let’s make 20 light orbs.
Also, I imagine that at the end of our adventure the player would be seeing an epic scenery like this:
Now that looks majestic.
As to our play style and details, “Witcher 3″ was the only game that came to my mind without any doubt. Aside from the fantastic graphics, I also like that it’s played in a 3rd person view.
To conclude, the following are a few items for my setting:
1. 3rd person camera
2. majestic moonlight view (low-lit)
3. Light orbs
4. aboriginal / mystic / ancient atmosphere
Given that I will be using different animations for different directions of movement, and that I have never used the standard third-person controller shipped with Unity, I decided that I should at least try scripting my own third person controller.
MixAmo provides some fantastic character models and animations, but there was only one character which its design “kinda” matched my keywords. Since this is just a small project and I don’t want to over-complicate it, I’m just going to use that instead of DIYing my character out.
Now, Unity has a great feature called Blendtrees. It basically blends your animation clips with each other depending on the parameters you give in. If you don’t know what I’m talking about, it looks something like this:
In the animator view it looks something like this:
As you can see above, I had two animations for each direction. The ones closer to red dot are the walking animations, and the ones further away are the running animations. This will slightly make your character look more natural when you want it to walk. Also notice that I’ve also assigned an idle animation right beneath the red dot.
After some easy scripting (I code in C# by the way), I can see my character working on the fly!
But here comes the problem: in my script the left joystick has been set to control my character’s movement. In convention the right joystick should be able to control the camera that cycles around the character.
At first I thought this should be easy to make: add a script on the camera that reads the inputs of the right joystick, and use the numbers to control the camera’s position, and make sure that the camera is always pointing at the character.
When I playtested it, I discovered something wrong with this mechanism.
In Witcher 3, for example (it’s so awesome that it’s the only example I can think of now), when the camera is facing at the back of the subject, then the controls are pretty trivial: when you stick left, your character walks to the left, and vice versa; but when your camera is facing at the front of the subject, then the controls are inverted, which means that you actually walk to the right when you steer to the left. More importantly when your camera is in such position, your character is supposed to walk in the opposite direction (or at least walk backwards) when you steer to the front, and to walk forwards when you steer downwards.
Now that’s a bummer: to fix this, certain adjustments regarding the rotation of the character must be made, which involves manipulating with Quaternions, and Quaternions are the creations of the devil.
My first attempt was to rotate my character whenever the player rotates the camera. Immediately this fixed the direction problem, but it also introduced a new problem: now the camera would always look at the back of our heroine. This is unacceptable because normally you would be able to see your character from every angle, not just from the back. It’s also disappointing because your character would spin on the ground when only the camera is rotating.
Looked something like this but totally unprofessional.
My second attempt was far more complicated. It involved calculating the dot product of the camera forward vector and the character’s forward vector. The dot product would then be applied back to the x and y parameters received from the joystick, thus modifying the parameters being passed into the blendtree.
I don’t know either.
Besides, the character does not rotate. This means that character would never move when the player steers fully right / left (dot product = 0).
So, time for round 3.
My strategy for round 3 was pretty much not modifying anything, except inverting the controls whenever the angle between the camera and the character exceeds a number. It still done in dot product between vectors, but it’s not Einstein math like what I did before.
The results – satisfactory, for now. I can control my character, though it sometimes felt a bit jerky due to the different walking speeds between normal running animations and strafing animations. It does require some improvements, like adding some backwards animation, and tweaking the dot product numbers so that it would never feel buggy.
So there you have it, a third person controller from scratch.
After finished writing all this, I realized that maybe combining round 1 and 3 would be a good idea – rotating the camera does not rotate the character, and the script would instead change the rotation of our character rather than modifying the inputs from our controller. Who knows?