Adding a critical strike marker for enemies when they're facing away from the player. Needs some fixing still because of a small detour during the day.
First I simply created a world canvas with a radial fill for the timer and a simple cross graphic. The idea was to first run the timer and then activate the graphic and notify the character scripts that this enemy can be critically striked. Everything looked pretty good but I just got a tingle that it could look amazing actually on the back of the enemy.
Well I "somehow" got it working and looking great, but when actually playing the mark was way too small in my opinion (could be a sick shirt graphic though). Since there's no good way of making it noticeably larger, I scrapped the material for now. It really wouldn't even make a big difference, because of the viewing angle always being favourable for the canvas.
"Execute" probably going to get renamed to critical strike
I got a little excited when I got the combat actually playable and worked on it for 13 hours on Wednesday. So yeah, today I'm just gonna write this and take it easy. On Wednesday though, the combat started to look and feel pretty good already. I'll still have to mess around with the camera, figure out how to visualize the enemy's health and add a lot of things, but that's gonna be after getting the stealth mechanic working and seeing how it all plays out.
I had trouble keeping the player at a hittable range. The problem wasn't only that the enemy wouldn't hit but more that the enemy got "pushed" when trying to move back to the hittable range. It probably could've worked with some precise movement, but that means a lot of time for polishing. I thought a more interesting way to keep the player at a distance would be a kick.
This seemed like a great idea but still required some coding for the push. In the end I decided that this wouldn't be an obstacle for adding it because the pushing code would probably get used a lot in the future. Currently the actual push is just roughly timed and the strength over time is controlled with an animation curve and I don't think that that's going to be changing because it really might not even matter.
The kick ended up being a great and time efficient way of preventing the player from drilling the enemy's shins to dust.
I also got some great sounds in, thanks to Eponn on FreeSound.org. The WeaponInfo ScriptableObject has a list for swinging sounds and impact sounds (per weapon of course). Then when swinging I just get a random one different from the previous and play it with the help of an AudioSource pool.
I thought AudioSource pooling can be useful in any project, so if you wanna save a couple minutes on writing one, download this. AudioSourcePool (Isn't optimized or anything)
I decided to disable behaviour tree evaluating totally while the enemy is dead or disabled. After this I got almost all of the functionality out of the EnemyBehaviour and into the behaviour tree nodes.
Adding basic functionality for enemy combat. I will probably be refining and adding effects to this for the rest of the week.
I implemented the combat state management system pretty weird. I have some functionality in the behaviour tree nodes and some in the EnemyBehaviour script (driven by the behaviour tree though). I have some concerns but first, here's roughly how chasing and attacking works at the moment.
Pseudocode:
if (PlayerInAttackRange && IsFacingPlayer)
enemy.state = EnemyBehaviour.EnemyState.Attacking;
else
{
enemy.state = EnemyBehaviour.EnemyState.Chasing;
// If target in range but not facing it turn it
if (PlayerInAttackRange)
RotateTowardsPlayer();
navMeshAgent.SetDestination(playerPosition);
}
Pseudocode:
Update()
{
switch (state)
{
case EnemyState.Patrolling:
Patrolling();
case EnemyState.Chasing:
Chasing();
case EnemyState.Inspecting:
Inspecting();
case EnemyState.Attacking:
Attacking();
case EnemyState.Dead:
break;
}
}
I did this partly because I found it easy to implement the attacking loop fully in the EnemyBehaviour. I'm worried that this might cause issues when implementing the stealth and such, but I'll have to look into it tomorrow and decide how I'm going to approach this.
Adding an animated enemy around which I will be building the enemy functionality hopefully within this week. As much as possible is yoinked from the player systems. Today I got the enemy's basic patroling movement and hit reaction working.
I'll implemented enemy animations with a single Animator Controller and just change the animations within it using Animator Override Controllers.
I use a blend tree for changing the reaction direction. In the code I have an Enum that has the type of reactions and I use this when setting reactions, making the code a lot more readable.
Today's task was to add combo attacks and do minor fixes all around the combat system. Now I will probably be switching focus to enemy combat. I think If I were to continue adding to the combat system, I would find a lot to be useless after adding the enemy combat system. After getting the enemy combat system up and running I will probably see the bigger picture clearer.
On the code side I'm just checking for the attack input when 60% of the attack's cooldown has passed. If input is given a "Combo" boolean parameter on the animator is set to true which I use to disable the "Any State" transitions.
Today was almost all about implementing an actual platformer camera into the project. Other than that I just added a trail to the sword.
I started to notice that I had to fully control the camera which isn't what platformers are about and thought I could fix this before going deeper into combat. I played a couple of old platformers and realized that I was essentially using a 3rd person shooter camera. I knew all along it wasn't the right kind of camera but didn't realise the right one would make such a big difference.
Before this I started with fiddling around with the Cinemachine Virtual Camera but didn't seem to find the right one for the job. So I looked into it a bit more and found out that Cinemachine has the right camera but just not within the virtual camera (has like a thousand settings itself), which I quess is made for 3rd and 1st person shooters. The right camera was the "free look" camera with the orbit mode "simple follow with world up", which wants to stay in place(world space) while looking at the player at a specified distance.
I added a sword trail with the help of a useful tutorial I found on Youtube. For turning it off and on again I use the emission module's RateOverTimeMultiplier, setting it to 0 and back to original when attacking. In need of some polishing effort though.
Tuesday stretched a little longer than I would've wanted to. A lot got done on the detection and damage system though. Wednesday I spent on making minor fixes all around and getting the attacks to feel more responsive.
Detection system got a lot of new stuff including:
Sphere Overlap is used to find clean hits and raycasts are used find hits where the blade has gone through the player in one frame (collision detection therefore missing it).
I had a couple of ideas for the damage system, one being calculating the damage for every detection point and the other being velocity based attacks. The first one felt nice but is a total pain to implement thanks to the complexity. The velocity on the other hand made the hits feel random.
Finally I ended up with a per point calculations with position of the points scaling the damage linearly (Closer to the tip deals more damage). The point closest to the handle dealing 1/10 (amount of points) the damage and the very tip dealing full damage.
This damage also scales the effects which are up next.
For every detection point that has hit there is a ParticleSystem instantiated or placed from pool. I created an empty object and a "MonoBehaviour" to hold and manage these, called "ParticleSystemPool", which does the following:
Switched from using root motion to just locking the direction and adding some speed. Can't see a difference and feels a 100 times better. Before even the smoothed camera got weird because the root motion was so jittery.
WeaponManager Editor
SwordEditor
WeaponType ScirptableObject
Attack Animation Events
Starting on this week's focus combat with melee hit detection. Creating a sword with a line acting as the damage dealing section. Raycast based hit detection script for it.
I created a script that distributes given amount of points on a line constructed of empties. In the video the "detection points" are drawn as spheres on the sword.
Spheres in the picture visualize the line's points.
The position of the "detection points" are saved for the next frame. Then a raycast is shot towards the last frames position, detecting a possible hit. In the video the red rays mean no hit and green ones mean hit.
Continuing yesterday's animating and animator work. Adding a ledge grab animation, trying new things with the animator layers and minor fixing overall.
Trying different things with the animator, but still having the problem of the character floating on the wall because of the root bone being a deform one. I will be looking into this over the weekend, the solution hopefully not being to manually animate a new non-deform root bone to every animation.
Sharing states among more animator layers for clarity. Minor flaw being the lack of blending between jumping and grabbing the wall.
After yesterday's coding I took this day a little more chill and did some animating and unity animator work.
Looking pretty janky at the moment, but almost everything is going to get polished.
I didn't find good animations from mixamo so I had to make them. So I added inverse kinematics to my rig to do them in a timely manner and it's also beneficial for any probable animating in the future. IK turned out to be a problem when exporting the animations. I thought Blender would add keyframes to the constrained bones automatically when exporting, but this wasn't the case, I only got the old Unity rig to animate the root bone.
After a "little bit" of googling I found out about "baking actions" which saved the day. This feature creates a new action and basically creates keyframes for the constrained bones every "Frame Step".