• entries
    27
  • comments
    62
  • views
    24985

Simple Brilliance, Crunch Time

Sign in to follow this  

1686 views

Simple Brilliance


The thing I was working on tonight was getting all of my enemy variants into the game. I've got the color variants done, with the red versions being more difficult to kill, and I'm working on getting the limb variants in.

This took a little bit of thinking as to how would be best to handle it.

I knew I would need an animation state for each permutation of limb loss (no head, no head no left arm, no head no right arm, etc.), so I set up each animation from the sprite sheets that I already made. Then I had to figure out how to determine which animation state to play based on what limbs were missing from the enemy (and think about how to lose limbs in the future).

My first thought was just to use bools and a relatively complicated if/else structure, something like:[code=:0]if(headLost){ if(leftArmLost){ if(rightArmLost){ animator.SetBool("NoLimbs", true); } else { animator.SetBool("NoHeadNoLeftArm", true); } //etc., ... }}
This would work, but it would just be really ugly.

My next thought was to still use bools, but in combinations--one for each possible state (of which there are 8 in total). I would use a specific combination of them to set any given animation state, one bool for each limb lost to form the specific combination. This solution seemed finicky, and potentially confusing to set up, so I thought maybe I could set up some kind of string concatenation when a limb was lost to use as the trigger name value. Again, this seemed finicky, and would really be a hacky version of the second bool solution.

The solution that I've settled on, though have yet to fully implement, I think is fairly clever, reduces the amount of code I have to write, and will make it easier to remove limbs during play while also easily managing the animation state.

First off, I decided that all of my zombies would be instantiated will their full set of limbs. In their Start function, I will randomly determine how many, and which limbs to remove, if any, to create an enemy variant. Since they always start with all limbs, and because they can never gain limbs, I set my animation states up in a one-directional manner, under the assumption that only one limb could be lost at a time. It wound up being a bit of a thing of beauty:

zxSMltm.png


At first glance, it might not be perfectly clear, but here is the progression if you trace the far right track (notice that the transition from "SkinbieNoLeftArm" to "SkinbieNoArms" runs BEHIND "SkinbieRightArmOnly").

So yeah, the progression tracing the right track:

[font='courier new']-----------------------------------------------------------------------[/font]
[font='courier new']|SkinbieWalk -> SkinbieNoLeftArm -> SkinbieNoArms -> SkinbieNoLimbs | [/font]
[font='courier new']-----------------------------------------------------------------------[/font]
[font='courier new']| All Limbs -> Lose Left Arm ->Lose Right Arm -> Lose Head |[/font]
[font='courier new']-----------------------------------------------------------------------[/font]

It functions similarly for all other tracks as well.

Then, I needed to figure out how to explain to the animator what state the enemies' limbs were in to determine the correct animation.

I figured, I would assign a unique integer value to each limb, and the sum of the existing limbs would be used as an integer parameter in the Animator to set the state. The only caveat with this solution, is that all permutations of the possible sums of the integers must be unique as well.

I started by assigning the integers 1, 2 and 3 to the right arm, head and left arm, respectively:

89g1WiU.png


Then I quickly figured out all possible sums:

vtTeO3A.png

Here, we have 7 values, one for each state (and 0, for the 8th state of no limbs)
those of the limbs themselves: 1, 2 and 3
the sums of each couple: 3, 5 and 4
and the total sum: 7

However, all values/sums aren't unique as 3 is present twice.

So I realized that using values of 1, 2 and 4 would give all unique values, from 1 - 7:
FqIvR4h.png

This is a pretty simple solution, but I think it's a pretty clever one. Luckily I only have 3 limbs that can be lost, so figuring out all unique values was super easy, but this method could be used on a much greater set of items than just 3 in a similar situation (where removing a single item from a set is required, especially where the removal is order independent).

So I went from that ugly if/else structure:if(headLost){ if(leftArmLost){ if(rightArmLost){ animator.SetBool("NoLimbs", true); } else { animator.SetBool("NoHeadNoLeftArm", true); }//etc.,... }}
To something much simpler:[code=:0]private int limbSum = 7;enum Limb{ Left, Right, Head};public void RemoveLimb(Limb limb){ switch(limb){ case Limb.Left: limbSum -= 1; //Instantiate left limb here when implementing dismemberable limbs. break; case Limb.Right: limbSum -= 4; break; case Limb.Head: limbSum -= 2; break; } animator.SetInteger("LimbSum", limbSum);}
Again, not super complicated, but I think it's a pretty elegant solution.


Crunch Time

Alright. I'm down to the home stretch. I've got just over a week until the deadline I've set for myself, and of course, life has gotten in the way.

A new project suddenly became a huge priority at work, so I've been devoting extra time to getting that done. The good news is that I'm developing my portion of the project in Unity (which I don't normally get to do), so at least I'm having fun at work. Also, I've been a bit sick. Blah blah blah. Excuses excuses.

However, I am not deviating from my deadline of midnight, Saturday, July 16th to have all of the core functionality implemented and have the game mostly play-tuned (beta tester feedback will help determine the final play-tuning) for release to those individuals who will be play testing.

I still have quite a bit I want to do before the initial beta-testing, and little time to do it in, so I'm making it a point to put in at least some time every single night. I'm also going to try to take a vacation day next Friday to work ALL DAY on the project (obligations permitting).

Looking over the list of things I want to do, I'm actually closer to having the core functionality complete than I thought. Here is the list of what I NEED done by the 16th (and this only contains items that will directly affect game play):


  1. Implement Headshots
  2. More enemy variants
  3. Having my 'Blood Bird' (a crow variant) affect the player's move speed and jump height.
  4. Fixing an accuracy bug
  5. Game Ending (The player riding away on the bike when it's repaired).
  6. Playtuning:

  • Rate of currency accumulation
  • Cost of weapons, ammo, upgrades and medkits,
  • Upgrade values (weapon damage, accuracy, capacity, fire rate, etc.)
  • Rate of experience accumulation and leveling curve
  • Ability values (how much damage does the Iron Skin ability negate, how fast should the Fast Feet ability make the player move?, etc.)
  • Enemy Health and Damage
  • Wave Tuning (Number of enemies in each wave, types of enemies in each wave, number of predefined waves, infinite wave generator)
  • Bike repair speed

    This really isn't that bad of a list, so I shouldn't have a problem getting this all done by next week.

    Then there are the nice-to-have's which will all be implemented by the time of actual release, and as many as I can get by the 16th:


    • Add pickup sounds
    • Blood Particles
    • Exploding/dismemberable enemies
    • Enemy Variants V2 (Spawning with limbs missing)
    • Have crows fall from the sky and hit the ground when they die
    • Intro
    • Achievements (Including icons)
    • Wave Counter
    • Add a level up animation.
    • Mute Button
    • Sponsor placeholder
    • Shellcasings
    • Trailer
    • Facebook/Twitter links and rewards
    • Optimization Refactor

      Again, even this list isn't that bad. Hopefully late July/early August I'll be done everything? So I'm getting excited to have my first game complete!


      Also, if you are at all interested in play testing, let me know. I have a decent number of people willing to test, but I can always use more. I won't be sharing the link to this publicly, so if you want to test, you HAVE to let me know.

      Anyway, that's it for now. I'll probably be silent for the next week. Maybe an update some time on Friday (and I suspect I'll STREAM ON TWITCH all day that day as well)

      Wish me luck!

Sign in to follow this  


5 Comments


Recommended Comments

Hi JEJoll,

Lot of progress with each consequtive journal entry! Great job ;).
The list looks a bit steep, but I wish you the best of luck!!!
Don't crunch too much, not good for your health, just a little, for the extra mile ;):).

+
Little info and tricks for your limb separation logic:
What you have there is a bit-field, or "flags". Each value is actually a power of 2, which is represented by only 1 bit set on the position of the power.
So Nth bit set where N is the power:

 1 == 2^0 (0th bit set for one,     binary: 00001)
 2 == 2^1 (1st bit set for two,     binary: 00010)
 4 == 2^2 (2nd bit set for four,    binary: 00100)
 8 == 2^3 (3rd bit set for eight,   binary: 01000)
16 == 2^4 (4th bit set for sixteen, binary: 10000)
...

With a 32 bit unsigned integer you could handle 32 limbs :). That could allow a really cool looking zombie with some brutal gory limb separation :D:D:D.

C# is not particularly a system level language :), but has nice support and syntax for working with flags. Here is a code sample:
 

[Flags]
enum Limb {
	None = 0,
	
	// here you shift 1 by the power, so essentially you multiply with the Nth power of 2:
	Head     = 1 << 0,
	LeftArm  = 1 << 1,
	RightArm = 1 << 2,
	LeftLeg  = 1 << 3,
	RightLeg = 1 << 4,
	
	// for having a value containing all flags, you can use the binary | (or) operator
	// to set each bits/flags
	All = Head | LeftArm | RightArm | LeftLeg | RightLeg,
	
	// you can also include special values for checks
	Arms = LeftArm | RightArm,
	Legs = LeftLeg | RightLeg
}

private Limb limbSum = Limb.All;

// the methods are implemented with binary operations.
// if you not yet know them, ultra short description:
// they do the same as logical operations (|| or, && and, ! not ...)
// but not on logical values but on the bits of the operands.
public void RemoveLimb(Limb limb) {
	// here comes the "trick"
	// with binary ~ (negation) you reverse the bits set
	// than with a binary & (and) and the "All" value you limit
	// the negated bits to the bits within "All".
	// again with a & on your contained values, you "subtract"
	// the bits set in "limb" from the bits of "limbSum"
	limbSum &= (~limb & Limb.All);
	animator.SetInteger("LimbSum", (int)limbSum);
}

public void AddLimb(Limb limb) {
	// with the binary | (or) operation you can set back the flags,
	// and you could create limb-regenerating super zombies :D...
	limbSum |= (limb & Limb.All); // & Limb.All is added so no invalid flags are set 
	animator.SetInteger("LimbSum", (int)limbSum);
}

// example check:
public bool HasLegs() {
	return (limbSum & Limb.Legs) != Limb.None;
}

// Sample for removal:
limbSum = Limb.Head | Limb.LeftLeg | Limb.RightLeg | Limb.LeftArm;
RemoveLimb(Limb.LeftArm | Limb.RightLeg);
// here: limbSum == Head | LeftLeg !!!

Indeed, it may seem like a bit too "hack-ish" solution, but much much shorter than a switch case and if you add more flags, there is no need for modifying the Add/Remove switch with adding more cases!
+ It is pretty common to use it for solving similar problems.

Share this comment


Link to comment

What you have there is a bit-field, or "flags". Each value is actually a power of 2, which is represented by only 1 bit set on the position of the power.

 

Thanks for the input Spidi. Looking at this, it occurs to me that I've never even touched bitwise operations, which is surprising, especially given that I actually have a diploma in programming. They were mentioned in college, but I really don't think I've ever touched them. In fact, the basic explanation you've given contains more than what I learned about them in college altogether.

 

I'm definitely going to have to look into them some more, especially since I plan on allowing my 'Slenderbie' monster to lose all of his limbs as well, and he has 15 all together, so your solution might be a better variation. However, I think I remember reading somewhere that you can't use bitwise operations in a WebGL build, though I could be wrong about that. 

 

Seems like some experimentation is in order. Thanks again!

Share this comment


Link to comment

You beat me to it Spidi. I was all ready to impart some bitfield wisdom. :)  Still JEJoll, it's neat that you came up with this "on your own". 

Share this comment


Link to comment

What you have there is a bit-field, or "flags". 

 

Indeed, it may seem like a bit too "hack-ish" solution, but much much shorter than a switch case and if you add more flags, there is no need for modifying the Add/Remove switch with adding more cases!
+ It is pretty common to use it for solving similar problems.

 

Really cool and detailed example about bit-field, specially the combination of the shift inside the enum, was not aware you could do it like that.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now