Tuesday, May 03, 2016

I Tried To Make Line Rider

It worked marginally well, but the rotational velocity was very, very difficult to figure out: it was never fully figured out. Firstly, SFML's Shape classes (2D) rely entirely on Euler angles to set the rotation of an object. No telling objects to look forward with a nice little forward vector, oh no. I figured I could just convert the forward vector into an angle... and I did. Using this function I added to my Vector Math class:















Seems like a reasonable approach, right? It was. Those functions determine the angle of any vector just fine as far as I could tell. Let's pop over to the Sled class (the sled rides the lines) and grab the Euler angle of the line (do you feel that chill in your spine?):





















Okay we're getting a little less reasonable now, but this was my solution to get the angle of the colliding line segment at least mostly correct.






























First, I have a bit of boolean functionality to test whether it is the first frame of collision (I wanted to do this all without using contact points, as time was running out and finishing making this was becoming less and less possible). To find the rotational velocity of an object that is revolving around a point, given the linear velocity, is a simple enough task. It's simply the linear velocity divided by the distance of the object to the axis of rotation. Since I was not using contact points, and was just rotating about the center of the object, that distance was zero. So I just took that part out, which is valid.

After all this, it appeared that my angle math just didn't align with SFML rotations. Which makes sense, I am pretty arbitrarily defining the point of rotation. I tried simply adding and subtracting 90, 180, 270 degrees to and from the draw angle, but things just didn't work.

So getting the rotational velocity didn't happen, but the rest of it works pretty well! I used a distance check to detect whether lines were worth trying the SAT (Separating Axis Theorem) test with the sled. I got to write a pretty special version of it to handle collision not with another convex polygon, but with a line. I also had to be able to collide with the bottom of the line, so I implemented a special, yet very close to normal, version of SAT.


Phase 1:


























Phase 2:






























I also did some tricky stuff with line drawing to make the lines. Each connected series of lines was its own std::vector of sf::VertexArrays contained within one big 2d vector of these lines. I made it so the user can, after drawing any amount of lines, continue drawing existing lines. I also added a few constraints on how the user can draw lines to avoid ridiculous angles and make smooth lines.

Even though this didn't work out as I had hoped, feel free to pull the repository and play around with it. It's a Mercurial repository, go ahead and

 hg clone https://nicholaspedalino@bitbucket.org/nicholaspedalino/line-rider

to play around with it. Here's a video link to see the final demo:


Cloth Demo

Springs are an integral part of any game programmer's arsenal. They are used everywhere: in cameras, ragdoll physics, water, soft-body physics, and the focus of this post: dynamic cloth. If you take quantum field theory to be the fabric of the universe, then energy springs are basically the fabric of the universe. (resist tangent)

Anyways, I made a "cloth," which could be better described as a 2d lattice of springs.

Looking at the interactions of the system as a whole can be overwhelming, so let's break it down. First let's work with the most basic structure: a point mass. Each vertex of the cloth is simply a point mass (also known as a Particle). Define a struct with the following members, and any others you'd like to add: a mass, a position, a velocity, a force, and a boolean indicating whether the point mass is fixed or not. Use any integrator you'd like: Euler works, but Verlet works better as it takes inertia into account. They are very similar, and there are many explanations of each method.

In addition to an integrator, also include a Post-Update method: an update function that runs immediately after your normal update. This Post-Update should perform the actual integration on the total force, while the update method should additively calculate force each frame. The usefulness of this will become apparent later. Simply put: since there are 4 distinct connections for all non-edge case PointMasses, we will need to find the cumulative force for all of them.

Next, we'll need to create a Spring class. This class will include the following members: two PointMasses, a spring constant, and a scalar equilibrium distance. You could add a breaking distance to define the max distance a spring can expand before it breaks, but I won't go over that in this post.

Next, we need to create a 2D array of PointMasses. While doing so, we want to create spring joints connecting each PointMass to the one above and to the left of it. The general algorithm for doing so is as follows:

 PointMass[][] pointMasses;
 Spring[] springs;
for (i < cloth_height):
  PointMass[] row;
 
  for (j < cloth_width):
    PointMass newPM;
    newPM.position = (initialPos.x + j * spawnDistance, 
                      initialPos.y + i * spawnDistance);
    pointMasses.add(newPM);
      

    //if there is a PM above this one
    if(i > 0):
      Spring newSpring;
      newSpring.PointMassA = newPM;
      newSpring.PointMassB =  pointMasses[i - 1][j];
      springs.add(newSpring);

    //make the top row fixed
    if(i == 0):
      newPM.isFixed = true;

    //if there is a PM to the left of this one
    if(j > 0):
      Spring newSpring;
      newSpring.PointMassA = newPM;
      newSpring.PointMassB =  pointMasses[i][j - 1];
      springs.add(newSpring);

Of course this is just pseudo-code, but you get the idea. Something to note: you can set any of the PointMasses to be fixed, which can lead to some interesting results.

I won't go into the spring integration method here, as it is also well documented online. One important note: make sure your force directions are correct!

And that's pretty much it, except one caveat. Since we update in-order, and since we are dealing with an interconnected lattice of springs, PointMasses indirectly effect all of the other PointMasses. So, if we move the bottom-right point on our cloth, the top left one may be effected. Say we update our PointMasses left-to-right, top-to-bottom. The new position of the bottom-right PointMass will update after we've already calculated the top right one.

We need to account for this, and the simplest (yet particularly inefficient) method I've found is to integrate multiple times in each frame: each time getting closer and closer to the actual position the PointMasses would be in ideally. If you have any better solution, or ideas for resolving this, email me or leave a comment! I'd love to find a better way to do this, but for now integrating multiple times is the best way I know.

Predicting Physics

Say you're playing a game of football with Sandra Bullock. Sandra is running in some arbitrary direction, so you will want to throw the ball somewhere in front of where she is at the time of your throw. In addition to this, since gravity exists, you will also want to throw the ball upwards a bit. We intuitively understand these concepts, and carry out basic, inexact predictions like this all the time.

Similarly, if you wanted to beam yourself from a planet onto a starship moving at warp speeds, I would imagine there would be a lot of physics predictions to take into account. Luckily, only rarely in games do we need to take relativity into account when predicting physics.

Predicting an object's physics is as easy as running your object's integrator (update) functions a bunch of times, without applying those new positions. If you have a fixed framerate, passing that fixed framerate into your special prediction integrator as its delta time will return a highly accurate prediction.

In my asteroid physics prediction demo, the first thing that had to be done was to calculate a time value. How far in the future should we predict? I derived that time value based off of the distance of the mouse cursor to the missile silo. This asked and answered the question of: "How much time will have passed given the missile's initial position at the silo (P1), the missile's final position (the mouse position, P2), and the linear speed of the missile?" A simple calculation:
t = distance between P1 and P2 / missile speed

 Given this time variable, solving for the final position of the asteroid was now possible. While the missile moved at a constant rate, the asteroid moved not only at a variable rate (acceleration), but a variable acceleration (jerk). Quite a complex calculation to do all in one go (I'm not sure if this is solvable using one equation). Luckily, computers are great at repetitive tasks!

So, in a for loop: 
for (float i = 0.0f; i < TOTAL_TIME; i += FIXED_TIME_STEP)

I integrated i times, and saved off the final position after simulating real-time physics in one frame.
Pretty simple! This was so interesting to work with, however, because the asteroids moved with variable vector accelerations defined by a gravitational force. The asteroid relied on only two variables to move: an initial speed and the force of gravity. Below is the derivation I used to go from this gravitational force to a manageable vector acceleration (which was then simply added to the velocity).


And that was it! A cool thing about predicting physics is it has nothing to do with the accuracy of your game's physics: no matter what kind of integrator function (real or imaginary) is implemented, predicting this physics is as easy as running a special version of the integrator for any given time.

Saturday, March 19, 2016

Production Post 10 : End Game

We're about two weeks away from release and it's coming down to the wire. The project is dripping with my blood, sweat, and tears, and I have to just keep working harder and harder in order to deliver that release build in a state I will be satisfied with. I'll have so much time to sleep when I'm dead, so why bother sleeping now?

I would put the main menu at about 60% functionally complete, and 30% polished. The pause menu still breaks minor stuff and doesn't look good. The dragon movement is only mostly finished, but not in a state we can release with. The levels are looking a lot better, but the ghosts need new, better AI, the ocean level needs a lot of visual polish, the splash and boulder crush particle effects are severely lacking, and the wind obstacle particle effect needs some minor tweaking.

One huge area of our game we are missing entirely is prompts to tell the player how to play. We need to make this as soon as humanly possible, one shot, polished, ready for release. We don't have time to iterate on this.

The game needs a LOT more feedback to inform the player what exactly is going on. We need to polish the credits menu. It's too late to make that difficulty curve tool for Zac, so we're going to need to sit down and figure out a decent way to work around that limitation.

Will and I are still pushing heavily for the infinite play mode, which the systems are almost entirely already made for, but I'm going to put that at 50% probability of materializing.

We need to make it so the split orbs get destroyed (with feedback) when they get hit by an obstacle. The Overworld gate animations still aren't finished. We still need to add chapters, A LOT of chapters if we don't have time to make the infinite play mode.

We have a document on how we might optimize the game, but we need to sit down and optimize it. The framerate on mobile is still very not great. Load times are verging on atrocious, with no indications that loading is occurring.

There's still so much to do: these next two weeks are going to be critical. Compounding this with other classes, which are also coming down to the wire, and you get eight very stressed out, very overworked developers. But that's half the fun! We've all worked too hard on this to not come out with a polished, bug-free, beautiful game, and I know we will be able to do it. But wow are these two weeks ever going to push us to our limits.

Production Post 9 : Inspiration Outside of Games

I have a snake. Her name is Mona. She is a sweet babe and I love her. Here is a picture of her on my head.


As it turns out, she moves a lot like how our dragon moves! Watching her move actually inspired a lot of how the dragon moves.


This isn't her, I don't have a gif of her slithering around, so I found this other snake that looks like her on the internet! But notice how a turn will start with her head, and propagate down through the tail. In code, this ended up being the node system of motion I implemented. A turn starts with the head, and the nodes follow, but not directly, the motion of the head. It kind of smooths out down the tail:


In the past, the nodes would follow the path of the head exactly. It didn't feel right, it felt more like controlling a train than a dragon. It's hard to put into words the tiny details of how a snake moves, but as the turn propagates down the tail it tends to sort of straighten out.

When making games, I think most of the best inspirations come from anything that isn't a game. Observing the world around, and other forms of media, you makes great, original material for systems, functionalities, and games in general.

I haven't ever seen a game entirely made out of shadows, which means at the very least it's a relatively unique style of game. It also gave way to a series of really interesting effects and mechanics that work well with a game made of shadows. We got the idea to do this through a series of thoughts, the major breakthrough being shadow puppetry.

Self-sacrifice and protection are themes rarely seen in games: to arrive at these we thought a lot about motherhood. Given that none of us are mothers, this was a difficult thing to do accurately. But we keep this idea in mind as we develop the game, and I think it shows through.

But I think what we've done here has been important in shaping how I look at games: it's easy to think that all the good game ideas are already taken, but to create an original, interesting game I think you have to step outside of the gaming world and draw inspiration from anything else.

Production Post 8 : Particle Effect Vacations

I love implementing particle effects. So much. It relaxes me: after working on core systems, tearing my hair out over complex algorithms, and handling the responsibilities that come with being the lead programmer: making particle effects is practically a vacation. I just kind of zone out and mess around with sliders, values, and curves until things look pretty. I like making things in our game look pretty.

So I'm going to post a few gifs of some particle effects I've made, and briefly talk about them!

This was a pretty quick one to make, I wish I could have made the actual line thinner as it expanded, but that isn't how basic scaling works. The time it would take to make that work is more time than I'm willing to spend on something like that. Maybe when we get to pure polishing, but there are more pressing things to attend to at the moment.


There are two particle effects here: one when a boulder crumbles and another, more subtle fog/mist effect under the mountain. The boulder smashing was the first particle effect in the game, and I'm not particlary (heh) happy with it. The mist I made earlier today: a lot of stuff is happening with that but my favorite part is that as it rises, the second or third phase of transparency brings it slowly to zero alpha, which gives the illusion that it dissipates as it rises into the atmosphere.


I set the background dandelion effect to render as shadows on the same layer as the background, which I think looks really nice. If you notice, the dandelion tumbleweed thing actually effects the background dandelions! I attached a wind zone to effect the background particles, and a sphere trigger collider to impart a force onto the orbs. The dandelion ball obstacles aren't finished yet, but they look alright. To get them to fly around in a circle, I had to give it an x force over time as a sine wave, and a y force over time as a cosine wave. Circles! Everything is circles!

Ah Unity default particles. Maybe I'll change the texture of these to one of the smoke particles Shannon made, but then again... meh. I think this one looks pretty good. I do need to resize this: the ghosts used to be tiny, shitty hands until Amanda made them ghosts. So the effect would execute, and I destroyed the hand a couple split seconds after the effect started, which made it look like a smoke bomb kind of magic poof. You can see the ghosts disappear while the effect plays, and yeah me no like.

I'm looking through the game and, uh, I'm not seeing any more. That's really disappointing for me. I need to make more particle effects. Luckily, most of my tasks right now are particle effects now that the Overworld is done. Next week I'm thinking I'm going to spend a lot of time on these. It's going to be a good week :)

Production Post 7 : A Brave New Overworld

A couple days ago I rewrote how the Overworld functions! It extensively changes how the Overworld interacts with DataManager, and how a player navigates. Here's a clip:


All the tutorials are on a big wheel now! With zooming in and zooming out, proper level progression (persistent progression saving included with DataManager), and.... drumroll.... chapter progression! This is a pretty large step forward in development, and puts us significantly closer to a complete build ready for release. Also, it allows Zac to design and implement new chapters seamlessly.

In this post, I'll talk about how it works currently, and how it can be improved.

First off, there are six states the Overworld exists in:


This object-state design pattern is proving to be invaluable. There are different inputs that need to be processed and followed unique events for ZOOMED_IN and ZOOMED_OUT.

ZOOMED_IN recognizes, for now, a single tap to begin zooming out. If the tap was on a node; however, this zoom out event is overridden by the level entering.

ZOOMED_OUT recognizes a single tap which switches over to the ZOOMING_IN state. It also recognizes swipes left and right, and tells the chapter wheel to begin rotating in the correct direction. This instantiates either the next, or the previous (with exception handling for if we are on the first or last chapters and whatnot) chapter, rotated 90 degrees, and positioned either left-ward or right-ward from the wheel axis.

That instantiation makes the new chapter a child of the wheel, allowing me to simply rotate the wheel with the chapters following, without having to worry about moving the chapters at all.

As far as I know, this all works really well! No bugs have come up and everything seems fine from the player's point of view. But, I think the system is flawed and a bit hard to follow for a few reasons. I made this switch about 3/4ths of the way through writing this: I went from reading everything from DataManager to storing localized instances of data. The chapters are destroyed and then instantiated, which means the indices of the List of chapters does not line up with DM's list of chapters (the List of Lists of level IDs I talked about a couple posts ago). If I can change it to sync up perfectly, and hide inactive chapters on the wheel rather than destroy them, it would make saving level progressions a bit more sensical. I have a feeling that this convolution will lead to some minor bugs in the next week, so when those pop up (inevitably when Zac starts adding chapters), I'll redo a bit of how this class works. Until then, who really cares?