Monday, January 2, 2012
Monday, July 5, 2010
Collisions and New Gameplay Goodness
Differences In the Videos
The last video demo just showed the character attacking, moving, and idling based on player input (or the lack thereof). What wasn't shown is that the player could move anywhere they wanted as well as through solid objects. In this video, you see that the player is actually bounded by walls and objects, can attack, and actually has something react due to the attacks.
Changes Since Last Post
The last post was mostly about player's character control and how it changed the character's state and animation. All of these things were within one class. Now, there is a class called CharacterAnimationController which handles all the animation calls. The PlayerController class has a variable which does the appropriate function calls to make animations occur. This allows for a clear separation of concerns and should allow for player control logic to be in PlayerController and animation control logic to be placed in CharacterAnimationController, to help alleviate any possible confusion. It also allows this to be very flexible, since the CharacterAnimationController class can be placed into any character model and an external controller can access it.
The New Stuff
The new stuff is mainly about collision detection. I will detail what the current logic is, but as for classes and variables, those will probably be left out until the next major post on this subject. The reason behind this is that there will probably be a major code organization change sometime soon based around this stuff and the character attributes of attack, blocking, etc. Also, a small but good change is that the camera has been updated to act like the old games in this genre. It moves only left and right with the character, but not up and down.
First Attempt and What Was Learned
The first attempt at collision detection was a lot of Capsules (a primitive object in which I later had their mesh renderer removed to use their Capsule Collider component) that closely fit to the body of the character. Each one was placed as child to the model's bone's GameObject. This allowed it to automatically rotate and move to the model's animation. This was great since we could have near perfect accuracy. However, it was decided that such accuracy could actually be bad for the type of gameplay we are aiming for since it could make some characters' attacks more effective than others. Though the Capsules were scrapped, we did confirm that we could place other things, such as particle effects, onto the bone of a model and have it act according to the mode's animations. This should allow us to easily have cool effects in certain areas.
The Thing We're Using Now… And What We Learned
Through other attempts and research, the CharacterController component was found. This component, when using it's own Move function, would allow the character to not walk through solid objects. This is now being used to prevent that, but to also be the basis of the collision region of the character (the region that will say when the player is hit). In front of the player is now an invisible BoxCollider, which is the attack region. If the player is attacking and something is in that region, it will be hit by the attack.
A problem that occurred during this is that the CharacterController and the BoxCollider would be colliding during attacks and triggering the colliding functions. This is an issue, since no matter when the player attacks, the trigger would go off, which is a waste and could cause other collision problems. To solve this, I luckily came across the function Physics.IgnoreCollision, which does exactly what it's name implies. It has to be called at runtime though; it will not carry over or save. This did solve the issue, and knowing about this function will most likely help in the future.
When a collision occurs, a message is sent upwards in the collided object with the SendMessageUpwards. Thus if the player hit's an enemy, the enemy has a message sent upwards to apply the damage. At this time, a preset number is sent from player to enemy on the amount of damage to be done. This will later be changed so the calculated maximum damage of the attack is sent, and then the enemy can apply any defenses and changes before actually applying the damage.
What's Next
Things coming up are refinement of this collision detection system, character attributes, and AI. Particle systems will be added somewhere along the way too.
Friday, July 2, 2010
Programming Animations and Attacks - Beginning of Gameplay
The first of many posts! Hopefully this will be detailed and useful to all that read.
One obstacle that had me going for awhile was how to determine when an attack animation was fully completed, without using the isPlaying member. Though this would tell me if an attack animation was done, it would not work well if you have more than 1 attack going in a row and wanted to do something special with each attack. Furthermore, it would always return true since another animation (usually walk or idle) would follow the set of attack animations. So this solution wouldn't work, and no other values were all that useful in knowing when a player was attacking, and when he was idling/walking.
On top of the goal of knowing when a player is attacking and when they are done, I wanted to be able to string attacks together in a combo. These go hand-in-hand, since we want combos, and we'll need to know when a combo is done so the state of the character can change.
So the problem is 2-fold: know when attacks are done, no matter how many occur in the set (though we are currently capped at 3 in a combo), and stringing the attacks together visually.
Attacking State Change Problem
I tossed around a random set of solutions, most of which I can't think of right now (if I remember any I'll post them in a comment). The solution I did settle on, and has been working well, is timestamps. Each AnimationState object has a member called length, a floating point value that is how long the animation is in seconds. So if the field returns 1.73, then the animation lasts for 1.73 seconds. When a combo occurs, the length of each attack animation is added to a single variable called attackTime. This is the total amount of time that the player will be in the attack state. So if combo1 last 0.5 seconds, combo2 last 0.25 seconds, and combo3 lasts for 0.75 seconds, the total amount of time the player will be attacking is 1.5 seconds.
When the player first performs an attack, a timestamp is taken and stored in a variable called timeOfAttack. This variable, in conjunction with the value returned by Time.time, is used to determine how much time, in seconds, has passed since the first attack. This is checked each update call. When the time that has passed is greater than or equal to attackTime, then the attack has completed, and various state related values are reset, including attackTime, the attacking state, and the comboNum (more on this variable below).
This has worked very well so far, with no noticeable issues. The states change when I expect them to, and there hasn't been any major performance hits for this approach that I've seen.
Stringing Attacks Together
Every time the player hits the attack button, the function Attack is called. This function updates the value comboNum each time it is called. This can be seen as the number of Attack function calls that has occurred so far. The function checks to see if the value is less than or equal to 3. If so, the value is passed into a switch statement. This switch statement will then add the length of that combo's animation to the attackTime value, and queue up it's animation for playback.
That's pretty much all there is to that one. Any additional logic for specific animations can go into the switch statement, but so far isn't needed. The code for the Attack function can be seen here.
Final Thoughts
I think this gives a pretty solid core for animations, attacks, and the state changes for moving, attacking, and idling. In the simple demo (embedded above) there's basic changes from idle to walking to attacking. A full combo has a smooth transition back to idle, but if you only do 1 or 2 attacks it pops back to idle. Currently thinking of ways to fix that.
One of the final things I'll probably do next for this is separate player input and movement into one class, and the animations all in another class. This way I can simply drag the animation class into any character type and use it seamlessly.