Saturday, December 12, 2015

HT: Adding Dash movement

I was thinking if the dash feature should be a new script or just add it to the PlayerMover script. I thought, "dash is simply a speed boost for a limited time". There's no need to create a new move script, all I need to do is change the speed while dash is in effect.

There were two ways to specify this. Either I express it in terms of distance and duration, or I express it in terms of speed and duration. I almost always start things this way, thinking of what variables I need to implement the functionality.

Both methods should give the same end result, but my way of thinking differs depending on which I start with, and that tends to take the development in different directions.

I went with distance and duration (speed is derived by distance divided by duration).

My first attempts were crude. I simply created a boolean to signify if the character is dashing or not. It's set to true when the player presses the dash button (A on the Xbox controller). When I do, current speed is set to an incredibly high value. I record the time the dash should end (current time plus dash duration). This "end time" I check for continually, and if we've passed that time, the dash is set to false and speed is back to normal.

Pretty simple, but I noticed a lot of things that go wrong:

  1. The camera follows the character smoothly (it kind of lags behind the player when you start moving). This is normally fine, but since dash has high speed, it makes the character go off-screen for a few moments, enough to make me feel like it was an annoyance. There needed to be different cam follow behaviour while the character is in dash.
  2. The stopping of the dash was too abrupt. Imagine how The Road Runner (Looney Tunes) stops. Nothing bad there but the thing is, the camera is following the player, so the whole world feels like it's suddenly stopping. This makes it feel rather motion-sickness inducing. There needs to be some sort of easing out to make it smooth.
  3. The character's cape was flailing wildly when subject to the high speed of the dash.
  4. The character's turn speed is too slow for this high speed dash. The dash had already finished, and only after a second or so does the character finish facing the direction where it went to. I needed a different turn speed while in dash.
  5. I haven't made the character play the dash animation yet, so it looks kinda weird.
First thing I did was change my cam follow script to stop the smoothening behaviour when following the character. This is only done during a dash (my PlayerMover script now has a public bool IsInDash property). Later on I'll realize this was the wrong way to go. Just as bad as when the dash stops abruptly, gluing the camera to the player was making the whole thing feel abrupt. It made it look like the dash was moving in stutters. I thought my framerate was just bad. I will not realize my stupidity until at the end when I was polishing the dash movement.

So I moved on. To fix the abrupt stopping, at first I thought I'd be clever and create an AnimationCurve and use that for the easing out. Then I remembered why I hate using Unity's curve editor controls.

In Photoshop, the curve controls allow you to move the handles as far away as you want. This is important. The distance of the handles can influence the shape of the curve. And the farther the handles are, the finer control you have at rotating them.


In Unity, you can't move the handles farther or nearer. You can only rotate them. This was frustrating for me:


In Photoshop, it's so easy to make this kind of easing out curve:


You just needed to move the handles on both ends.

With Unity on the other hand, you can't move the handles away. So you need to compensate by adding more handles to achieve the same curve more or less, and it's not easy to get it right:


I highly doubt the things I like about Photoshop's curve controls is proprietary stuff. Blender (an open-source program) does it too.

So I thought to myself, "why am I being a masochist?". So I stopped using Unity's curve editor and just moved on. I'll polish that later.

Adjusting the turn speed was easy. I simply have a new turn speed variable meant to be used during dash, and use that instead whenever the dash boolean was true.

What took up the most time was making some appropriate dash animations. I had one prepared, but it was just a single frame pose. I thought that would be enough. I was wrong. It looked horribly amateurish.

I thought maybe if I went ahead and added that dash recovery animation that I wanted to make, it'll look better! You see, the dash animation I had in mind was inspired by Alice's dash in Alice: Madness Returns. When she dashes, she zips by fast, and we only get to see her already in a sort of semi-crouched position, slowly going back to her idle pose. It's like she was throwing her body momentum downwards to stop the dash. And it actually made sense visually. In my sheer poetic genius, I ended up calling it, simply, dash recovery.

So I created something to that effect in Blender, and added it in the Mecanim state machine.

I figured that, the player isn't allowed to move while in the dash recovery period, but he can still do another dash (provided he still has enough energy to do so, once I implement energy usage).

At first I had a separate state for the dash loop, and for the dash recovery animation. The more I tested it, the more I saw that the dash animation and dash movement were out of sync. Sometimes the animation played just in time, sometimes there was this weird delay, and the animation would play well after the dash movement finished. Turns out it's because the dash animation isn't getting played if the state machine is in the middle of a transition. So if the character was transitioning from idle to move, dash doesn't activate yet.


I Googled and checked the docs, and the way to fix it is to set the "Interruption Source" to something other than "None" (the default value). It helped, but it still didn't work quite well. I thought, maybe I should start using that new StateMachineBehaviour script I read about. And as I was skimming through the template file Unity gave me, I thought, no, I think I'm just making things harder than it should be.

Then I thought of what exactly I want Mecanim to do. So I Googled "mecanim state transition to itself and replay animation at beginning". Then I saw this page in Unity Answers: Restart same animation in Mecanim. As I was looking through it, I thought it looked familiar. I actually answered this question with "use Animator.ForceStateNormalizedTime" That doesn't exist in Unity 5 anymore though. Someone answered you could just use "Animator.Play". So this actually means we can bypass the transition rules in the state machine and force it to jump to a state, all of a sudden.

For a scant few moments I pondered on the meaninglessness of creating lots of nice state machine features, when you are provided with the means to undermine it.

Anyway, Animator.Play worked perfectly and fixed the problem.

I also simplified the state machine by removing the dash loop state and instead use the dash recovery animation itself as the dash animation. I don't have a dash recovery state anymore.

I also removed the Move.Forward state and just kept the Move.Forward.Loop state. The Move.Forward was actually an animation to play to transition from idle to move, but there was little need for it as Unity already handles fading of one animation to another.


For the dash easing curve, I settled for using the easeOutQuint function (see http://easings.net/) to make a nice, perfect quintuple curve. For the camera follow script, instead of gluing the camera to the character, I simply gave the camera a speed boost when dash is occurring so it can catch up easily.

I ended up expressing the variables in terms of speed and duration.


Sunday, December 6, 2015

HT: Adding lean left/right to the mecanim animation



It took a while to figure this out. To do this, I needed to create new animations, a steer left, and steer right animation, and play them when the player is turning left or right.

Technically, they weren't animations. They were just single frame poses.
Preparing the Mecanim state machine for this is easy: the move animation state just needs to be converted to a blend tree, then I'd add the steer left and right animations to it:


I made a new parameter called "Steer". It's a float. The "moving straight forward" animation is set to use 0, the "lean left" is in -1, and the "lean right" is in +1.

The preview shows how it will end up looking like:


Now, how to properly play those in runtime? What I want to do is detect when the player is rotating the thumbsticks, and use that to play the lean left and lean right animations correspondingly.


This same thumbstick is the one used for movement. So it's when I'm rotating the thumbstick I should transition from lean left to right and vice-versa.

Turns out it's not so easy.

I'm using CharacterController which doesn't calcualte angular velocity. I could've just used that to detect when the character is rotating (equivalent to when the player is rotating the thumbstick).

My first thought was to use dot product on the character's current forward direction, and the direction where he needs to go to. You see, I make the character smoothly rotate towards where the thumbsticks are pointing. When you want to do a smooth rotation, you'd need to store where they currently are facing, and where they need to face to, and slowly interpolate to where they need to face to. So I thought of using those existing variables already.

The red dot here is where the thumbstick is pointing to, and the yellow dot is where the character is currently facing.

The dot product tells me how far apart the two direction values are. But it doesn't tell me if the direction where the character should go to is to their left or to their right. That's important since I need to know if I should play the lean left or lean right animation.

This is the video that taught me dot products:



So I needed an additional way to determine as to what direction the thumbsticks are rotating to. I thought of deriving the angles (in degrees) that the direction values are creating. Then subtract the two to tell if we're rotating to the right or to the left.

It almost worked well.

I noticed "bumps" when I rotate my character to the left.



I realized it was because angle values I calculate always get clamped to 360 degrees. When the target angle is over the 360 degree mark but the current angle isn't over that yet, the resulting value is too big, causing the "bump".

I recorded the angle values to a graph, so I could see what exact values I'm getting.


I figured the only way I could get around this was to get the proper angle value when it crosses over the 360 degrees mark. With a little bit of trial and error, I found the code that best suited the situation. It looks like this:

if (_angleDelta > 270) _angleDelta = -(360 -  _angleDelta);
if (_angleDelta < -270) _angleDelta = 360 + _angleDelta;


The only thing left was that I still noticed spikes in the graph of my "Steer" getting function. Depending on how bad your framerate is, it made the whole animation look like it was stuttering. I guess there was noise in the input data.

This graph looked a lot worse before.


So I just added a sort of average filter on the last 4 values recorded in the graph and use that instead:



It all seems rather straightforward now that I've put all this in a blog post, but before this, I forgot what "dot product" is and what values it returns, I didn't know what code to use to clamp angle values, and I didn't know what an "average filter" was. There was lots of looking through the Unity manual, and I wasn't even sure if this "Steer" factor idea was the right way to go about it.

It's all exploratory when solving a problem like this and trying out clever solutions.

I was a little against posting a blog post about this because it did take up the entire day, making all those screenshots and videos, and it's making me feel that people would think I'm obsessing over the little insignificant details. At least the time it took to make and fix the whole steer feature took only about a day of work.

Wednesday, February 18, 2015

Visa Application

I gave myself a mental note to not get distracted while a self-satisfied woman looked like she was letting her boobs bounce on purpose as she walked eagerly to the exit.

Guess someone's visa got approved.

I looked up again. The dot-matrix screen reminded us whose stub number was ready for interview. Above the shuffling of feet and the confused whispers, the usherette kept on reminding us that the stub numbers come up in random order.

"Ah, raandoom!" I heard someone say beside me.

What that really meant was, simply that some applicants finish their talk with the consul faster than others.

...

Honestly, in my head I thought the interview consisted of sitting down in some posh office. Some nice plants, a portrait, certificates hanging on the wall, maybe a golf club leaning somewhere.

And then I'd come in, nervously stammering and showing my papers to a stern old man who'd look like he never has enough time.

Of course, as with most of what I imagine, it was nothing like that.

The embassy probably gets a hundred or so applicants per day, judging from the long lines and the amount of seats.

So it all worked like a production line. Apart from the guards and the usherettes, all the personnel you talk to are behind windows, including the consuls. There's a chute at the bottom of the window where you pass down your passport.

The whole thing reminded me of computers and how they pass data around.

...

It was a long wait before my number lit up, so I had a lot of time on my hands.

Rummaged through my old passports. I went to Hong Kong when I was a kid? Oh wait, I did. I remember riding that World's Longest Escalator ride.

I didn't think much of it back then. But it turned out it really was the World's Longest Escalator.

Someone, at some point in time, thought it was a good idea to put SeaWorld Hong Kong on top of a mountain.

I looked at all those ink stamps on the pages. Departure. Arrival. They had different shapes, bright colors. I heard myself make a wistful laugh. They made my old passports look like stamp collections.

I saw my DS-160 form had the words CAPTURED stamped on it.

For a moment, the nervousness was replaced with disgust. Middle-aged guys ogling X-ray body scans of passengers behind the security doors of an airport. Videos of police brutality on Facebook. Why would anyone want to live there?

It makes me think getting into the US isn't as rosy and important as these hopefuls are making it out to be. Wearing smug faces, trying to display they know more than the guy next to them. Maybe it's just to cope with the nervousness? Validating all the hardships they went through?

In a place like this, you could tell who were the people who had actual character.

...

The guy beside me started saying out loud the subtitles on the instructional video playing in front of us.

Jeez... Practicing English just now huh?

More time passed.

I couldn't help yawning over and over. Well, I did take the 7:15 AM. I needed the earliest slot I could find. Russ had been bugging me already, and I've put this off for a long time.

Man... I found myself slouching in my seat as I thought, I just want to get back in front of a computer.

The instructional video even managed to be funny at one point:

Myth: Only cute people get their visa applications approved.

Fact: Non-models get their visas approved every day.

Hah.

The next slide came up:

Myth: Single people get their visa applications denied.

Fact: Your honesty is more important than your marital status.

Ok, not laughing so much now.

...

When my number came up, I kept looking at it to make sure it really was mine. 2126. Probably took too long, cause the guy beside me caught my attention and told me my turn is up.

Looking back, the interview ended faster than I thought.

I just ended up talking about being chief technology officer, us having a booth in Game Developer's Conference, the game we're marketing.

"Games?", he asked.

"Yeah, video games.", I nodded back.

"Oh, cool.", he said.

"Our game looks like this...", I held up a printout of one of the screenshots of our game.

"So are you guys gonna put this out on... games..."

"Sorry?" I asked.

"I mean, are you guys like, will you put this out on the Xbox?"

"Oh," I shook my head, "Getting into consoles is a lot more difficult, so we're targeting... only Windows for now."

"Ah", he made a face as if he'd scratched a card that said 'Try again next time'.

"So how long do you plan to stay?"

"Oh about... 2 weeks maybe. My partner has a relative in Las Vegas so that's probably where we're staying--"

"Ok, visa approved. Thank you."

I saw him place my passport on a pile. Presumably, the pile of passports that would get visas.

"Oh, thank you.", I managed to reply, before leaving. I haven't even showed any of the documents yet, I thought as I left.

I didn't look into his eyes the whole time. I was told that it gave the impression of shifty-eyed liars to Americans.

I wonder if he didn't like that.

...

As I walked out, there was only one thing that crossed my mind.

I took out the cheap black ballpen I stuck in my left pocket a few hours ago, and stared at it.

Fuck, I got duped into buying this stupid ballpen! The old lady in front of the embassy was hawking ballpens, making them sound important. Turns out I didn't even need to write anything the whole time.

Wednesday, September 24, 2014

Ingress Day 4

I decided to explore the southern part of my nearby vicinity. There were two portals there: the Ateneo Blue Eagle Gym, and the now erased graffiti on some doors of an abandoned building by the roadside.


Blue Eagle Gym

This portal looked like no one was reclaiming it. So I went here to try to get it.


The gym is inside the Ateneo University campus. Since I'm not a student there, I could not get in. However, I thought I could reach it by standing outside the school grounds, by the fence. I can almost reach it, but couldn't. So I had to abandon this one.



Marked Doorway Murals

This graffiti portal, which I remember being there before but had been removed some time ago, seemed like no one was touching it, so I investigated it.

Turns out it was nearby a lot of telephone wires, and some power line posts with transformers. Seems like that disrupted my 3G connection to the game, so I couldn't interact with this portal at all. I logged out to try to "refresh" the game, but I couldn't even log-in again while I was in that spot.





So all in all, I couldn't expand my options here.

Meanwhile there's a place nearer my "safehouse" that I thought would work as a new portal and I was thinking of submitting it. I'll try it one time.

Ingress Day 3

Finally reached level 3!



And got something pretty powerful:



An Enlightened captured that portal I was using. I still haven't reclaimed it. I realize I should have used Power Cubes when my XM reserves run out when attacking, so I could keep at it.



Monday, September 22, 2014

Ingress Day 2


I accidentally broke my toenail while running. Had to limp my way back to the apartment.



Not to worry, it's all patched up.





My portals got attacked. Looks like this won't be boring too soon.

That green one used to be mine.

Somewhere out there, some frog's fucking up my portals.



Ingress Day 1

So I just found out I have hypertension. Part of it means I need to do cardio exercise.

I thought, "Well, I could use one of those fitness tracking apps, or I could start playing that Ingress game I saw long ago."

So I started playing Ingress.

Seems like some people nearby already started putting up some portals, but looks like they were abandoned. So I took them.

I'm only starting out


Looks like someone's been busy. Those aren't mine, obviously.