Rocked like a hurricane

A few weeks ago, I got back from another trip to Japan, and no less than two typhoons ripped through the country during my wonderful vacation. Thankfully I had a friend who lives in Japan, so I could use his place to keep my stuff.

... Such as when the typhoon kept my luggage from getting to Japan when I did, and nobody was sure when it would arrive. I just had it sent to his place and made sure I went back that direction after the first week.

Though as a piece of advice, if you're getting shirts from the 100 yen store, I recommend getting the black shirts. Even the grey ones will be partially see-through after a few hours of walking around.

I'm going through the pics on my camera really slowly, but I'll write about it sometime. I mainly spent time west of Kyoto. I also went there right after AWA, so I'll have to go through my pics of that too.

Basic Dogfight Mode (4): Conclusion

Well, my imitation DFM didn't turn out the way I thought it would, but I was still able to figure out why my original idea didn't work and why. And once I did, all it took was a bit of a spin on the concept and I was mostly able to use it again.

Previous Post:
Basic Dogfight Mode (3): Yaw/Pitch/Throttle Spring Theory - Memories of Melon Pan

Many of you may have noticed that whenever I calculated yaw or pitch, I never calculated absolute yaw and pitch, only relative yaw and pitch from the ship's current heading. I actually don't keep track of absolute yaw, pitch, and roll when I update all the matrices on my ships. All I keep track of is current heading and up vector - each frame I look at the controller input (or DFM input), and adjust those two by the yaw, pitch, and roll of that frame. Then I make a new rotation matrix.

public void UpdateRotation(GameTime Time, float Yaw, float Pitch, float Roll) {
  Vector3 Right = Vector3.Cross(m_Dir, m_Up);


  // Local rotation around current direction.
  Matrix Rotation = Matrix.CreateFromAxisAngle(Right, Pitch)
            * Matrix.CreateFromAxisAngle(m_Up, Yaw)
            * Matrix.CreateFromAxisAngle(m_Dir, Roll);


  // Adjusting direction and up vector.
  m_Dir = Vector3.TransformNormal(m_Dir, Rotation);
  m_Up = Vector3.TransformNormal(m_Up, Rotation);
  Right = Vector3.Cross(m_Dir, m_Up);


  m_Dir.Normalize();
  m_Up.Normalize();
  Right.Normalize();


  // World rotation matrix.
  m_RotMatrix = Matrix.Identity;
  m_RotMatrix.Forward = m_Dir;
  m_RotMatrix.Up = m_Up;
  m_RotMatrix.Right = Right;
}

This is why for my final DFM run, I stopped as soon as I calculated axis velocity, since I add that to whatever is the current rotation every frame.

As always, the DFM used in Ace Combat: Assault Horizon is probably more complex than what I've done here, but I think I've done a decent imitation of it - enough that someone would get the idea about what DFM is all about and how they could do it too.

Basic Dogfight Mode (3): Yaw/Pitch/Throttle Spring Theory

My final stab at this all was taking the spring forces I used earlier, but using them differently. Before, I used them to tweak the position of the ship, then fudged its heading to make things look right... but this made my plane behave so very not like a plane.

Previous Post:
Basic Dogfight Mode (2): Pure Positional Spring Theory - Memories of Melon Pan

Planes can't just slide around in the air - they're controlled by yaw, pitch, roll, and throttle. So what would happen if I applied these spring forces to those instead of position?

Let's think about this. Normally, we were trying to move the plane to a point in 3d space, and figuring out acceleration and velocity based on that. Now all we want to do is adjust the speed of the plane, based on how far the plane is from its target. Having said that, we're no longer trying to place the plane at a point in space - we're trying to adjust its distance to the target.

So we can move our spring force back to one dimensional space, the length of this space being the total distance to the chase target.

f = m * A = (k * (D0 - De)) - (c * S0)


For the variables:

  • f, force
  • m, mass
  • A, acceleration
  • k, the spring constant
  • D0, most recent distance to the chase target
  • De, distance equilibrium
  • c, the damping coefficient
  • S0, most recent speed



Where all variables are scalar values.

And from here, you can figure out the new speed at the current frame just like the other spring force equations.

A = ((k * (D0 - De)) - (c * S0)) / m
S = A * t = ((k * (D0 - De)) - (c * S0)) * t / m


For the variables:

  • A, acceleration
  • k, the spring constant
  • D0, most recent distance to the chase target
  • De, distance equilibrium
  • c, the damping coefficient
  • S0, most recent speed
  • m, mass
  • S, final calculated speed
  • t, time elapsed between updates, in seconds

Some of you may have noticed that I'm no longer negating the spring constant for this formula. This is just how the numbers work out - if we're farther than our equilibrium distance, then we need to speed up to catch up to our target. This will mean that we want positive acceleration. We know (D0 - De) will be positive in that case, and we have to multiply by a positive number afterwards, so we can just get rid of that negation in front of the spring constant.

This gives us the speed we need to be traveling at, but now we gotta figure out what direction we're going in. Like the first iteration, we first have to tell if the angle to our target exceeds some threshold, Tn. Finding that angle doesn't change at all.

dw = normalize(Xt - Xs)
d = dw * transpose(Rs)


α = acos(dot(f, d))


For the variables:

  • dw, direction vector in world space
  • Xt, position of the target
  • Xs, position of your ship
  • d, direction vector relative to your ship in object space
  • Rs, rotation matrix of your ship
  • α, the angle between your heading and the direction to target
  • f, the forward vector (0, 0, -1)



Where normalize(v) is the vector v in normalized form, and
where transpose(m) is the transpose of the matrix m.

This also gives us the direction to the target from the point of view of the chasing ship. Just like the first iteration of this, if we're outside an angular threshold, Tn, we have to yaw and pitch the ship to face the target.

This is where we do things a little differently, but if you've read my canvas renderer posts, the math shouldn't be too hard. See, when we calculated α, we only got one angle between the vectors across some arbitrary axis. We're also going to need an absolute horizontal and vertical angle to the target. If we had those, we would know how far to the left the target was, or how steep we'd need to pitch to face directly towards it.

We can calculate these from our new direction vector d, though. If we project this vector onto the xz-plane, we'll end up with a direction vector that's purely horizontal. The angle between between this new vector and the forward vector is our horizontal angle. We can do the same thing for the vertical angle if we use the yz-plane.

The math is similar to the planar projections I did in my imitation canvas renderer, but I'll go over it here too. We'll need to define a plane first, and planes can be defined with two vectors. For what we're doing, we're going to use a planar forward vector and a planar normal vector, which is orthogonal to the plane.

The first part of the calculation only uses the normal vector. If we're trying to find the horizontal angle, we can project the direction vector onto the xz-plane to get the pure horizontal component of the direction. The planar projection of a vector onto a plane is the rejection of the vector from the plane's normal.

We start by projecting the direction vector onto the plane's normal. Once we have that, we subtract that vector from the original direction vector.

Pn = dot(d, n) * n
Pp = d - Pn


For the variables:

  • d, the direction vector
  • n, the plane's normal vector
  • Pn, the projection of the direction vector onto the normal vector
  • Pp, the planar projection vector



Where dot(v1, v2) is the dot product between two vectors v1 and v2.

Now that we have the purely horizontal component of the direction vector, all we have to do is find the angle between that and our forward vector.

α' = acos(dot(f, Pp))


For the variables:

  • α', the axis angle
  • f, the planar forward vector
  • Pp, the planar projection vector



Where dot(v1, v2) is the dot product between two vectors v1 and v2, and
where acos(x) is the inverse cosine of x.

The only thing is that this angle will always be positive, whether or not the vector runs to the left or to the right. Since we want to know which side the angle runs to, we're going to have to negate that angle if it's off in one of those directions. Go ahead and roll your eyes, but this means we're going to have to prepare ourselves to find a third vector we can compare this to. For this, we'll be taking a vector that runs to the right, and negating the axis angle if it runs to the left.

We can get the right vector by taking the cross product of the forward and the normal vectors. That will get us a vector that's orthogonal to both, and that runs to the right of the forward vector since we use right-hand rule in XNA. If we remember the unit circle, we'll know that we don't have to actually find the angle - all we need to do is take the dot product and see if it's positive or negative, since that will tell us if the angle it makes is greater or less than ninety degrees.


r = cross(f, n)


if (dot(r, Pp) < 0.0) {
  α' = -α'
}


For the variables:

  • r, the right vector
  • f, the planar forward vector
  • n, the planar normal vector
  • Pp, the planar projection vector
  • α', the axis angle



Where cross(v1, v2) is the cross product between two vectors v1 and v2, and
where dot(v1, v2) is the dot product between two vectors v1 and v2.

We calculate these planar angles for both the xz-plane to find the horizontal angle, and the yz-plane for the vertical angle.

  • xz-plane: Forward = (0, 0, -1), Normal = (0, 1, 0)
  • yz-plane: Forward = (0, 0, -1), Normal = (1, 0, 0)

The thing we use these horizontal and vertical angles for is to adjust how much we're going to yaw or pitch to keep up with our moving target. I originally intended to use a spring system for these numbers like I did for throttle... but then, it dawned on me I didn't really need a damping coefficient. The DFM railroad pilot shuts off when you're pointed in the general direction of the target, so the spring would never go back to straight center, which would have been its equilibrium point.

With the damping part out of the picture, what's left?

m * A' = k * α'
A' = (k / m) * α'


V' = A' * t
V' = ((k / m) * α') * t


For the variables:

  • m, mass
  • A', axis acceleration
  • k, spring constant
  • α', axis angle
  • V', axis velocity
  • t, time

When you realize that k and m are just arbitrary values that you decided, it's the same as multiplying α' by some constant, then by time. Compare this to what you do when you're taking input from the controller.

V' = I' * t


For the variables:

  • V', axis velocity
  • I', input axis intensity
  • t, time

The only difference is what's multiplied by t. In one case, we multiply t by the yaw or pitch angle to the chase target times some arbitrary weight. In the other, we multiply t by however much you're tilting the analog stick. Not too complex when you think about it.

But the rest of this is pretty much like it was when we first tried out DFM. If you're within the near threshold Tn, then your ship does whatever the controller says. If it's outside that threshold, then push it back on track so that your ship heads back towards your target. If it's outside the far threshold or if it gets too far from the target, then disengage DFM.

Next Post:
Basic Dogfight Mode (4): Conclusion - Memories of Melon Pan

Basic Dogfight Mode (2): Pure Positional Spring Theory

The next thing I tried to do to make one plane follow another was to base its movement off the same sort of movement a chase camera does. It didn't turn out as expected, though.

Previous Post:
Basic Dogfight Mode (1): Basic Yaw/Pitch Adjustments - Memories of Melon Pan

A basic chase camera can behave like a spring system. It's a classic mechanical system - just imagine a regular spring hanging from a ceiling with a dense wooden cube at the end to act as a weight. You pull down on the weight, let go, and what happens? Yup, the spring goes up and down, up and down, mesmerizing the minds of children everywhere until it finally stops where it started.

The position where the spring naturally rests we call the equilibrium position. The system also has a host of other variables as well - the spring moves so it has speed and position, the weight has mass, the spring has springiness or stiffness (yes, this can be reduced to a number), and so on. How are these all related?

By the power of MATH!

f = m * A = (-k * (X0 - Xz)) - (c * V0)


For the variables:

  • f, force, a three dimensional vector
  • m, mass, a scalar value
  • A, acceleration, an unknown three dimensional vector
  • k, the spring constant, a scalar value
  • X0, most recent position, a three dimensional vector
  • Xz, the target position, a three dimensional vector
  • c, the damping coefficient, a scalar value
  • V0, most recent velocity, a three dimensional vector

We're kinda taking the one dimensional spring and applying it in three dimensions when we make our chase camera here - we don't want to go just up and down, but also left, right, towards, and backwards. The intuitive thing to do is to use vectors for a lot of the variables here, and that'll do us. We know position has to be a three dimensional value, but we can have things like velocity and acceleration as three dimensional vectors as well, each with a component for the x, y, and z axes.

You probably noticed I talked about the equilibrium distance (which in three dimensional space is an equilibrium vector) but I only mentioned target position in the formula. Every time we use a position in the equation, we're talking about world space, so we have to find out at what point in world space the spring naturally rests at. If the spring comes to equilibrium (25, 25, 0) from its base, and its base is at (200, 200, 1000), then we can add those together and see that the spring wants to stand still at (225, 225, 1000) in world space. And for a chase camera, the base of the spring is whatever the camera's chasing.

Xz = Xt + Xe


For the variables:

  • Xz, the target position
  • Xt, the position of the chase target
  • Xe, the equilibrium vector

So back to our force formula, you can just muck with values for mass, spring constant, equilibrium vector, and damping coefficient to make the spring behave the way you want it to.

Ultimately, we want to update the position of the chase camera to some new position, and we have one unknown: acceleration. Just plug in the last position and velocity of the ship into X0 and V0, and divide the right side of the equation by mass. You'll end up with the acceleration of the ship at this frame.

And what do we know about acceleration?

V = A * t
X = V * t


For the variables:

  • X, position
  • V, velocity
  • A, acceleration
  • t, time

Using the amount of time elapsed between each frame (ideally 16.66 ms, if you're running at 60 frames per second) as t, we can calculate the new velocity, and then the new position of our chase camera.

This is the basics of a chase camera, and the last step is just pointing it either directly at your chase target, or at some offset from it. It's pretty simple, so I'm going to skip ahead and ask: instead of having a chase camera and target ship, what if we have a chase ship and a target ship?

That was what I was thinking as soon as I programmed a chase camera myself, but let's just say that if your ships have infinite maneuverability, it'll look okay... sometimes.

When I did my test run, I changed the target position so that the chasing ship would try to maintain some distance between it and the target.

Xz = Xt + (De * normalize(Xs - Xt))


For the variables:

  • Xz, the target position
  • Xt, the position of the target ship
  • De, the equilibrium distance, a scalar value
  • Xs, the position of the chasing ship

Where normalize(v) converts a vector v to its normalized form.

Then I just pointed the ship in the direction of the chase target, and for fun I gave it the same up vector so that the two ships would roll together.

But put it all together, and what did I find?

... Well, dang. If the chasing ship gets too close to its target, it will start to move backwards. This means if the two are heading right towards each other, eventually the chasing ship will moonwalk backwards in order to bring itself back to its equilibrium distance. It should be obvious, but normal planes can't do this.

Thing is, when we're moving the ship around like this, we're not moving it around like a ship. It can have negative acceleration, or even acceleration that lets it slide left and right. This means that we eventually figure out some position in space the plane should be in and place it there, ignoring things like flight physics and stuff. We're also pointing it towards the its target so it can yaw and pitch as fast as it wants.

So if we want to use spring forces to yank the chasing ship around, we have to make sure that none of these get out of hand. We could make sure the new acceleration doesn't change the ship's heading too much, and keep it from pulling those slidey stunts. What I did in the end, though, was change what these spring forces were acting on, though I when I did that, it turned out to be a mix between this and simple braindead DFM.

Next Post:
Basic Dogfight Mode (3): Yaw/Pitch/Throttle Spring Theory - Memories of Melon Pan

Basic Dogfight Mode (1): Basic Yaw/Pitch Adjustments

The simplest way you can do DFM is... honestly not that difficult, as long as you know some basic vector math.

Previous Post:
Basic Dogfight Mode (0): Overview - Memories of Melon Pan

In Assault Horizon, there was that circle in your HUD, and if your chase target got outside that circle, your ship would yaw and pitch to put your target back in that circle. Conceptually, you could extend a cylinder from your ship and check if your target is inside that cylinder... but I went for something a little simpler - just a cone.

We've got two thresholds here, the first of which is the near threshold Tn. If the angle to your target is greater than this threshold, then you can slightly nudge your ship around so that it turns towards your chase target.

However, if that angle becomes too large, then your ship will lose the chase target and disengage DFM. This is the far threshold, Tf, and if the angle to your target exceeds this threshold, instead of bumping your flight path, you can just clear the chase target variable and disengage DFM that way.

So now, we have to figure out the direction to the chase target relative to our chasing ship, but it's not enough to subtract the positions of each ship from each other. You'll just get the direction from one to the other in world space, or you'll learn that if your ship moved X units horizontally and Y units vertically, you'd be where the chase target is.

That's not what we want - if the ship we're after is dead ahead, we want to know to move (0, 0) from our current heading, and not (25, 25, 25) from our position in world space. We can start with finding the direction in world space, but to convert it to object space, we'll need a transformation.

dw = normalize(Xt - Xs)
d = dw * transpose(Rs)


α = acos(dot(f, d))


For the variables:

  • dw, direction vector in world space
  • Xt, position of the target
  • Xs, position of your ship
  • d, direction vector relative to your ship in object space
  • Rs, rotation matrix of your ship
  • α, the angle between your heading and the direction to target
  • f, the forward vector (0, 0, -1)



Where normalize(v) is the vector v in normalized form, and
where transpose(m) is the transpose of the matrix m.

You're dealing with a directional vector dw here, which is the direction from your ship to your target... but the catch is that your ship can be rotated to point in some weird direction. But if we multiply all these things by the transpose of your ship's rotation matrix, what happens to the direction to your target in world space or your ship's current heading? The former is swiveled around to truly be relative to your ship's point of view. The latter is swiveled around to point straight forward in world space, which is why we can use the forward vector to find α.

Trust me when I say this is as complex as it gets. Don't worry, I'll get to more interesting stuff when I get to the other things I tried, but for now this is Hello World for DFM.

From here, things can go the way you think they'd go. If the angle to your target is between the two thresholds, you need to take over the player's input with some automatic yaw and pitch inputs to face your ship towards your target. If your target is off to the left, then yaw left. If it's below you, then pitch down to meet it.

If α < Tn, then let player input control ship.
If Tn <= α < Tf, then yaw and pitch ship towards the chase target.
If α >= Tf, then disengage DFM.


For the variables:

  • α, the angle between your heading and the direction to target
  • Tn, the near angular threshold
  • Tf, the far angular threshold

... Which means we're now we're ready to yaw and pitch our ship to keep it on our target.

If d.x < 0, then yaw left some arbitrary amount N.
If d.x > 0, then yaw right some arbitrary amount N.
If d.y < 0, then pitch down some arbitrary amount N.
If d.y > 0, then pitch up some arbitrary amount N.


For the variables:

  • d, the direction to your target as a unit vector

Well how's that for disappointing? At it's most basic, you can just choose some number to yaw and pitch by, and that should get you by. In practice however, it might look a little jerky, since you either yaw/pitch by N or nothing at all. There's a few refinements you can make, but I'll save that for a later post.

You might think I'm done explaining this very basic way of doing DFM, but I haven't mentioned speed control yet. You have to maintain a certain distance from your target when you're in DFM, right? Well...

Let's keep things basic for now. Sorry, but that means they aren't getting any more exciting. Let's define a distance equilibrium De - this is the ideal distance we want to stay at. We can define a function that calculates what our speed should be based on how far we are from that equilibrium distance. If we want to maintain a distance of 100, speed up if we're further away than that distance, and slow down if we're closer. Your speed function can be simple or complex, but just to illustrate...

S = Sc + ((D - De) / w)


For the variables:

  • S, final calculated speed
  • Sc, cruising speed
  • D, distance to the chase target
  • De, distance equilibrium
  • w, an arbitrary weight

If you really want to keep things simple, this is how your speed function could look - speed gain or loss is linearly proportional to the distance between you and your target.

Choose whatever you want for Sc, De and w, just get numbers that make things look... not too bad. And when you're done calculating, you should probably limit your speed to some range, not letting it dip below some minimum speed, or fly over some maximum speed.

And the final thing to do - if your target gets too far from you, disengage DFM since you've technically lost your target. I'm mentioning it here just so that I say everything that's involved, but I really don't think I need to elaborate more on that.

Not a very exciting way to tail enemy planes, now is it? When I first thought about this, I totally agreed with how plain this was and tried doing something different for my first attempt. I ended up with something pretty wacky and it was an overall bust, but I was able to take what I learned from that ill-fated attempt and adapt it to what I did here, making it a little more intellectually stimulating.

Next Post:
Basic Dogfight Mode (2): Pure Positional Spring Theory - Memories of Melon Pan

Basic Dogfight Mode (0): Overview

If any of you have played Ace Combat: Assault Horizon, you'll know that you're forced into this thing called Dogfight Mode (DFM) every so often to take out enemy aces and such. I'm not gonna get into whether or not it was good for the series, but at the very least, it was different. It was something that I'd never seen before, and what's more, I was wondering how it could be programmed.

What I observed in the game was that there's this circle at the center of your HUD. When you're in DFM and your target is outside of that circle, your plane will yaw or pitch (never roll) towards your target until they're back in the circle, and that your speed will automatically adjust itself to maintain some distance between you and the plane you're chasing. Of course, if they get far enough out of the circle or far enough away from you, DFM will break automatically.

Knowing that, you could do the simple but very boring thing: compare your current heading to the direction to your chase target, and if the angle is past a certain threshold, then start yawing and pitching the plane towards the target. Speed up if they get too far, slow down if they get too close. Yeah, there's not too much ingenuity involved in that.

But there was another way which... probably wasn't the one that they went for. I first thought about programming DFM after going through a tutorial that taught me how to program chase cameras - that's the same sort of camera that follows your plane around in Ace Combat when you're in third-person view. When I finished that tutorial, the similarities between the two just came to me - the camera's got a position, a heading, and an up direction just like your plane after all. The theory goes that if you replaced the chase target with another plane (and not your plane), then replaced the camera with your plane, you'd have Dogfight Mode. Your plane would follow another just like the third-person chase camera followed your plane.

Thing is, I tried it this way, and uh... it doesn't really go over that well. A chase camera can do all sorts of zany repositioning - you don't actually see the camera, you just look out of it, and as long as the ride's smooth, there's nothing to fear. But when you see an actual object wigging out trying to get into position... well, toss that idea out the window.

I'll go over both of these starting with the simple adjusting-yaw-and-pitch way of doing things. I'll go over the chase camera method more from the viewpoint of making a chase camera, with a side of the funny things that happen when you slap that movement onto another object. I've gone over the more complicated math with the canvas rendering tutorial, to be honest.

Next Post:
Basic Dogfight Mode (1): Basic Yaw/Pitch Adjustments - Memories of Melon Pan

Why you do this to me, AWA?

So, it looks like the MIQ concert at AWA is happening at the same time as the Idolm@ster panel. Dang it, now I gotta choose, but... well, sorry guys, I'm leaning towards the concert.

On the other hand, I have three or four costumes to take to the con. And, unless I decide to go to the Im@s panel after all, this will be my cosplay schedule.

Friday: Sadao Maou, from Hataraku Maou-sama!
Saturday morning: Little Mac, from Punch-Out!
Saturday night: Hirotaka Wakamatsu, from Gekkan Shoujo Nozaki-kun
Sunday: Little Mac, from Punch-Out!

The Wakamatsu costume I'm still finishing up the embroidery, and it looks like I'll have to spend an all-nighter doing it since this is new to me and I am thus pretty terrible at it.

Update, 29 September 2014: With what I actually went with. Going as Little Mac was pretty fun, so I wore it again on Sunday. Good thing I brought an extra tank top, though maybe I should have done Ryou on Friday night or something.