MC2 – Technical – Sometimes I make silly mistakes…

I just started implementing stat tracking in MC2. I don’t mean user data tracking, or anything nefarious like that, I simply mean in game stats, like how many kills a player has scored, how many jumps, and so on. One of the stats is damage taken and damage caused. I named these floats damageAgainst and damageFor, respectively.

As I was testing the stat tracking I noticed something that made no sense. When two spheres collided, the damageFor was always substantially higher than the damageAgainst, say 7f vs 5f. This meant I had been calculating damage incorrectly all along! TRAGEDY!!!

First, let’s determine the design (this may change in future, but for now here it is): I want the faster sphere to damage the slower one. That’s it. The way I’ll find out who is faster is by subtracting my speed from my opponent’s. If the result is higher than 0f, then I will have lost that collision and I must therefore take damage. I will also include a small variation where I account for both of our original mass. The heavier sphere will get a small advantage. So, in words:

opponentSpeed (plus)
opponentMassModifier (all minus)
mySpeed (plus)
myMassModifier

Take a look at this snippet…

This is the first line in a function called 
CalculateDamage. This function is used to 
determine who won the 50-50 in a collision 
and how much damage was caused to whom.

damageRawValue = (
  opponentVelocityMagnitude +
  opponentVelocityMagnitude *
    ((opponentOriginalMass / playerAttributes.maxHP) *
    GameManager.singleton.massDifferetialDamageInfluence) -
  myVelocityMagnitude +
  myVelocityMagnitude *
    ((playerAttributes.maxHP / opponentOriginalMass) *
    GameManager.singleton.massDifferetialDamageInfluence)
);

OK, so let’s do some arithmetic…

First, values! Let’s give our variables the following values:

– opponentVelocityMagnitude = 6f;
– myVelocityMagnitude = 3f;
– opponentOriginalMass = 10f;
– playerAttributes.maxHP = 12f;
– GameManager.singleton.massDifferetialDamageInfluence = 0.1f;

Now let’s replace them as necessary:

myDamageRawValue =
6 + 6 * (10 / 12) * 0.1 – 3 + 3 * (12 / 10) * 0.1 =
6 + 0.5 – 3 + 0.36 =
6.86 – 3 =
3.86;

opponentDamageRawValue =
3 + 3 * (12 / 10) * 0.1 – 6 + 6 * (10 / 12) * 0.1 =
3 + 0.36 – 6 + 0.5 =
3.86 – 6 =
-2.14;

Aaaagh! Why are their absolute values different! Here’s why, silly (talking to myself!)

Here’s what I think:

myDamageRawValue =
6 + 6 * (10 / 12) * 0.13 + 3 * (12 / 10) * 0.1;
opponentDamageRawValue =
3 + 3 * (12 / 10) * 0.16 + 6 * (10 / 12) * 0.1;

Here’s what the computer thinks:

myDamageRawValue =
6 + 6 * (10 / 12) * 0.13 + 3 * (12 / 10) * 0.1;
opponentDamageRawValue =
3 + 3 * (12 / 10) * 0.16 + 6 * (10 / 12) * 0.1;

Nothing that a few well placed brackets couldn’t solve.

myDamageRawValue =
(6 + 6 * (10 / 12) * 0.1)(3 + 3 * (12 / 10) * 0.1) =
(6 + 0.5)(3 + 0.36) =
6.53.36 =
3.14;

opponentDamageRawValue =
(3 + 3 * (12 / 10) * 0.1)(6 + 6 * (10 / 12) * 0.1) =
(3 + 0.36)(6 + 0.5) =
3.366.5 =
-3.14;

That was a little stupid, wasn’t it, Mike? I hear you all think. And you would be entirely accurate. Sometimes, I am THIS silly…

– Mike

MC2 – The Story – Should I continue developing?

On March 31st, 2018 I went to Calgary, to the Video Games Live event. We had a booth there, showcasing MC2. Here’s how it looked like:

This was the first showing ever for the game, and the game was in pre-alpha at this stage. I primarily wanted to showcase Brawl mode (which is the one with the round ring, where players need to knock each other off the ring), and gauge whether people liked the game enough to want to play it more at home. In order to make it more official, I asked all of the adult players who stopped to play to tell me whether I should continue developing MC2 going forward. So here’s what my trusty assistant Zoë (8 year old) and I set up:

What you see on the right is a makeshift easel holding coloured paper that reads as follows: “SHOULD WE CONTINUE DEVELOPING MC^2? NAY (left, red), YAY (right, green)”.

At least 50+ people stopped to play (I was too busy to keep count, unfortunately) and I’m happy to say that most of them seemed to be very happy with the game, especially the younger audience. We had a few people even coming back to play more after they’d already tried it! Here’s a short sample of a highlight reel (I intend to make more!):

So, hopefully you’re as excited to see this as I am to find out… How did the whole impromptu easel survey go?

29 upvotes, 0 downvotes. Onwards then? Well, yes, but I’m still not sure MC2 is going to have customers… There is one thing in particular I intend to do differently next time. I will setup the same easel, however, I will sneakily add some negative doodles as well. I don’t want people to have to brave standing out in being the only one or the first one who dislikes the game. Hopefully, that will help me gauge whether MC2 is any good more accurately!

My deepest thanks go to all those who played MC2 on Saturday in Calgary. Thanks so much for making it a fun event and bolstering my morale in continuing development! ONWARDS! 😀

– Mike

MC2 – The Story – Characters

I’ve always wanted to make a game that had different characters. However, I had to keep in mind I had to keep things simple in the art department, because simple = cheap, both in terms of money and time. So I decided to build various different types of sphere “cages”, if you will. Here’s how these look in game at this time:

At this point I was faced with a dilemma: do I make the characters significantly different from one another, as they would be in a fighting game, for example, or do I make them very similar to one another to the point of them basically being equivalent to skins (at least in the eyes of most of the user base), like the cars in Rocket League, for example.

Going with the latter would be a no brainer, budget-wise, however, I didn’t think that the spheres had enough character, if you’ll pardon me the pun, to stand out as purely cosmetic. Going with the former, like for instance giving each sphere a special move, would significantly increase the scope of my work, as well as risk making it so that some may be significantly better than others, as is often the case with fighting games. So I decided to settle for a hybrid: no special qualities, but substantial variation in the spheres’ attributes. I decided to remain closer to the Rocket League model as well, by leaving these differences obscure to the player.

I’ll try to explain what I mean by “attribute”, without giving the secret numbers away! 🙂

All of the spheres have the following attributes (for now):

Max Health (which is also the mass)
Max Angular Velocity (the maximum speed at which a sphere can spin)
Torque Force (in simple terms, how quickly a sphere accelerates)
Air Force (in simple terms, how quickly a sphere accelerates in the air)
Jump Height (minimum height the sphere jumps when input is sent)
Jump Extension Strength (how much higher when the input is held)
Jump Extension Duration (how long will the held input apply additional force)
Slow Down Speed (how fast the sphere will come to a stand still)
Impact Force (how hard the sphere hits)

There are more attributes that I’m not willing to disclose, at least not for now, but suffice it to say, the spheres are different, very different. Mere tweaking of these properties, even by small amounts gives a completely different feel to the spheres.

Here’s a simple example: say hello to Orbital (left) and Full Metal (right):

Ignoring the colours, as those only indicate who is controlling the sphere, how would you say these two spheres differ? Which one do you think is lighter? Which one is faster? Which one flies well? Which one prefers to stay on the ground? These are the kinds of questions I would like the MC2 players to ask themselves. And, I’m not going to lie, I hope for them to form their own legends and speculations about all this. I hope the game will be successful enough that a small amount of dedicated players will run all sorts of empirical test to figure out the objective answer to these questions. One can dream, right?

And this is how I designed the “characters” in MC2. Hope you found this interesting.

Thanks for reading!

– Mike

MC2 – Technical – The Importance of Having Mass

When I began developing MC2 I decided that there had to be some kind of way for every match to get more exciting over time. While other games with similar round arenas make it so that the arena itself shrinks over time, or similar, this is not what I wanted to explore at the time. I wanted to explore the concept of damage instead. I wanted damage to have an effect on my player.

There are many games that have tried this before in various ways. For instance, in the Resident Evil series having sustained damage makes your character walk slowly and clumsily. I didn’t want the player’s mobility to be impacted by the lowered health. I wanted the player to be just as capable of moving when damaged. So I thought of a system similar to the Super Smash series, but I wanted it to be less abstract. So I decided that the spheres would have an armour, like a cage, and that this armour could fall to pieces.

The Art

1. I modeled a carved sphere in Blender, like this:

2. I applied the Cell Fracture plugin on it to split it:

3. Now pieces can fall off, like this:

The code

Note that the C# code below is extrapolated from my entire structure so you can understand it. It’s not exactly representative of what’s in there, but it should give you an idea of what I built. I’m also not explaining ABSOLUTELY everything. The following content presumes substantial knowledge of C# and Unity3D.

WARNING, THIS GETS TECHNICAL!

When a collision with a Player is detected...

void OnCollisionEnter(Collision c) {
  if (c.transform.tag == "_Player") {
    // Store the player info
    PlayerBall p = c.transform.GetComponent<PlayerBall>();
    // Sending information from the opponent I just
    // collided with to the function dedicated
    // to calculate the damage
    CalculateDamage(
      p.myVelocityMagnitude,
      p.playerAttributes.maxHP
    );
  }
}
Now let's calculate the damage!
void CalculateDamage(
  float opponentVelocityMagnitude,
  float opponentOriginalMass
) {
  // Calculate the damage percentage
  // based on the maximum amount of health
  // this sphere has. Note that a sphere's
  // maxHP is also its mass. So a 40 HP
  // sphere is 40 Kg heavy, originally
  float damageRawValue = (
    opponentVelocityMagnitude +
    opponentVelocityMagnitude *
    ((opponentOriginalMass / playerAttributes.maxHP) *
    GameManager.singleton.massDifferetialDamageInfluence) -
    myVelocityMagnitude +
    myVelocityMagnitude *
    ((playerAttributes.maxHP / opponentOriginalMass) *
    GameManager.singleton.massDifferetialDamageInfluence)
  );
  // Notice that damageRawValue will
  // be >0f if this sphere's
  // opponent had a higher velocity
  // before impact (as well as a higher
  // original mass, multiplied by the
  // massDifferentialDamageInfluence
  // variable kept in my GameManager
  // If the damage is <0f, then
  // it means this sphere took no damage.
  // The other one must have
  damageRawValue = Mathf.Clamp(
    damageRawValue,
    0f,
    Mathf.Abs(damageRawValue)
  ) / playerAttributes.maxHP;
  // Now that we calculated the damage
  // percentage, time to apply it
  TakeDamage(damageRawValue);
}
Applying the damage and lowering the mass
void TakeDamage(float damagePercent) {
  // Lower the HP by the percent
  // calculated before
  playerAttributes.currentHP -=
    damagePercent * playerAttributes.maxHP;
  // Clamp the HP between 0f and maxHP
  playerAttributes.currentHP = Mathf.Clamp(
    playerAttributes.currentHP,
    0f,
    playerAttributes.maxHP
  );
  // Change the mass to be the same as
  // currentHP
  playerStructure.mainRigidBody.mass =
    playerAttributes.currentHP;
  // On to detach things!
  DetachParts();
}
Lastly, detaching the pieces.
void DetachParts() {
  // We need a counted for a loop
  // to detach parts, starting at 0
  int detachCount = 0;
  // We need to calculate how many pieces
  // we have to detach. Note that I need a
  // CeilToInt because HP is a float, not
  // an int. Also, note that I already have
  // two lists for the parts that I initialised
  // and populated earlier. list_rigidBodies
  // contains the rigidbodies of all of the
  // parts that belong to this player.
  // list_gameRigidBodies contains the
  // rigidbodies of all of the parts that are
  // still attached to the player
  int partsToDetach = Mathf.CeilToInt(
    (
      playerStructure.list_gameRigidBodies.Count /
      playerStructure.list_rigidBodies.Count -
      playerAttributes.currentHP /
      playerAttributes.maxHP
    ) * playerStructure.list_rigidBodies.Count
  );
  // I'm going to need an index randomiser
  int tempRand;
  // As long as there are parts to detach and
  // the detachCount hasn't reached its target...
  while (
    playerStructure.list_gameRigidBodies.Count > 0 &&
    detachCount < partsToDetach
  ) {
    // Assign the random ID for the part
    tempRand = Random.Range(0, playerStructure.list_gameRigidBodies.Count);
    // Activate rigidbody and collider
    playerStructure.list_gameRigidBodies[tempRand].isKinematic =
      false;
    playerStructure.list_gameBoxColliders[tempRand].enabled =
      true;
    // Change the layer so the detached parts
    // don't damage the surrounding players
    playerStructure.list_gameRigidBodies[tempRand].gameObject.layer =
      GameManager.singleton.debrisSpentLayer;
    // Unparent the chosen part
    playerStructure.list_gameRigidBodies[tempRand].transform.parent =
      null;
    // Make if fly away from the Player
    // using a force that originates at
    // the center of the player, with a
    // random magnitude between two constants
    // that I keep in my GameManager singleton
    // ignoring the mass of each part (VelocityChange)
    playerStructure.list_gameRigidBodies[tempRand].AddForce(
      (playerStructure.list_gameRigidBodies[tempRand].transform.position - transform.position).normalized *
      Random.Range(GameManager.singleton.debrisForces[0], GameManager.singleton.debrisForces[1]),
      ForceMode.VelocityChange
    );
    // Remove the chosen part from the game
    // lists
    playerStructure.list_gameRigidBodies.RemoveAt(tempRand);
    playerStructure.list_gameBoxColliders.RemoveAt(tempRand);
    // Increment the counter
    detachCount++;
  }
}

The result

There, the sphere’s armour is falling apart because some of its parts are flying off. I quite like this effect myself.

Wait a second champ!

You may now ask: so what’s the deal with mass? Well, I actually don’t need to do anything about it really. Each of the collisions in the game is controlled by the Unity physics engine, which already accounts for mass when two objects collide. Since I’ve lowered the mass of the green sphere via code, it will be lighter next time it’s hit by the red sphere, which will make the green one fly further when hit. Neat, eh?

Till next time!

– Mike

MC2 – The Story – Humble Beginnings

After the release of my first commercial indie game on Steam, OVO Smash! (October 10th 2017), I’ve been prototyping concepts rapidly to see if I could come up with something interesting enough to develop as a new full fledged project. So on February 16th, 2018 I came up with this idea:

I wanted to try and make a multiplayer game that would be:
Possible for me to complete with my minimal resources
Immediately fun to play
Incredibly easy to pick up
Somewhat harder to learn to master

So I began thinking: what are the multiplayer games I’ve had the most fun playing recently? The games in question would have be designed in such a way that there would be minimal need for numerous and complex (and expensive) assets.

Rocket League immediately came to mind. Amazingly intelligent gameplay with variations of the same car, in variations of the same stadium, with very few mechanics (drive, jump and boost). These few ingredients create an incredibly deep experience. I would love to recreate something similar, even if smaller in scale and scope. – Simple but deep

I’m also a big fan of the Street Fighter franchise, and while Street Fighter is a very complex game now, in its first iterations it was somewhat simpler. It had charge characters and quarter circle characters, who all had standard moves and some special moves. Usually the special moves were either horizontal (like the Hadouken or the Sonic Boom) or vertical (like the Shoryuken or the Flash Kick). – Spacial awareness

Hadouken – Horizontal
Shoryuken – Vertical

Lastly, I also recalled that when a was a much younger fella, I used to play an Xbox game called Fuzion Frenzy with my buddies in couch multiplayer. One of the minigames in this collection was called “Sumo”, and it was about balls knocking each other off of an ever-shrinking ring. – Spheres, simplicity

Special mentions also go to Dive Kick, Nidhogg and Towerfall Ascension. All of these games involve two or more opponents studying one another and trying to psyche each other out. I wanted to recreate something similar. – Mind games

So I got to work and within about a day I got to a pretty decent result. I quickly had some spheres that were capable of colliding with one another. Unfortunately, I wasn’t mindful enough to collect footage at different stages of development, so the only thing I can show you is a much more advanced build. Apologies.

Here were my basic ideas:
1. As the spheres collide with one another, the one with more momentum wins the collision confrontation and damages the other by an amount that depends on speed at moment of impact (collision.relativeVelocity) and weight of the sphere (rigidbody.mass).
2. The more damage the sphere takes, the lighter it becomes. Lighter spheres are easier to knock around.
3. Whoever is left alive last wins the match.
4. The spheres had to be able to jump, to dodge opponent spheres.
5. All of the locomotion in the game had to be largely based on semi-realistic physics (meaning using the Unity physics engine to a substantial degree).

Pretty soon, in a few days, I was able to test the game with my kids. While I expected success, I certainly hadn’t anticipated the degree of success. The kids were very excited and enthusiastic. They wanted to keep playing. And as we played a few emergent properties of gameplay became apparent:
1. Jumping near the edge of the arena was dangerous, because while in the air you’re like a sitting duck, very easy to hit.
2. It’s possible to jump onto your opponents, stomping them. This would also then allow you to jump again from a yet higher position and stomp them again.
3. It’s possible to jump into your opponents from underneath, executing an uppercut if you will. If done at the right time this can be a very effective way of damaging opponents.

And this is how MC2 started. At the time, I had no idea how to call it, so I dubbed it “project Bouncers”.

Stay tuned for more. 🙂

– Mike