I like others here have been organizing my scripts as we went.
I started with the file structure that was from the completed project in the resources, so I do have several empty scripts that we have not completed, these my change or be deleted (Player Health) as I go through the lecture.
Part of my refactoring was to help make it more modular/reusable, I still have things to do to achieve this, like removing the dependency of the Player class from my sword and moving the activating of the weapon collider to the animation. I want to be able to have an enemy that has a sword without having to code a sword for the enemy and the player.
My Class Hierarchy
I did things like made a Damageable Class for anything that can receive damage, now I do not need code functionality for the enemy, for the player, or for a destructible. I just use the damageable component.
using System.Collections;
using System.Linq;
using Effects;
using UnityEngine;
namespace Damageables
{
/// <summary>
/// A damageable Game Object.
/// </summary>
public class Damageable : MonoBehaviour
{
[Header("Destroy Effects")]
[SerializeField] private GameObject destroyVFX;
[Header("Hit points")]
[SerializeField] private int startingHitPoints = 1;
private int _currentHitPoints;
private bool _isDying;
[Header("Hit Effects")]
[SerializeField] private float forceAmount = 15f;
private bool _hasForceEffects;
private IForceEffect[] _forceEffects;
private bool _hasDamageEffects;
private IDamageEffect[] _damageEffects;
#region Unity Methods
private void Awake()
{
_currentHitPoints = startingHitPoints;
_forceEffects = GetComponents<IForceEffect>();
_hasForceEffects = _forceEffects is { Length: > 0 };
_damageEffects = GetComponents<IDamageEffect>();
_hasDamageEffects = _damageEffects is { Length: > 0 };
}
#endregion
private void DetectDeath()
{
if (!(_currentHitPoints <= 0)) return;
StartCoroutine(DieRoutine());
}
private void Die()
{
Instantiate(destroyVFX, transform.position, Quaternion.identity);
Destroy(gameObject);
}
private IEnumerator GotHitRoutine(Transform other)
{
switch (_hasDamageEffects)
{
case false when !_hasForceEffects:
yield break;
case true:
{
foreach (var damageEffect in _damageEffects)
{
damageEffect.StartEffect();
while (damageEffect.WaitForCompletion && damageEffect.IsEffectRunning)
{
yield return null;
}
}
break;
}
}
if (!_hasForceEffects) yield break;
foreach (var forceEffect in _forceEffects)
{
forceEffect.StartEffect(other, forceAmount);
while (forceEffect.WaitForCompletion && forceEffect.IsEffectRunning)
{
yield return null;
}
}
}
private IEnumerator DieRoutine()
{
if (_isDying) yield break;
_isDying = true;
switch (_hasForceEffects)
{
case false when !_hasDamageEffects:
Die();
yield break;
case true:
{
var effectRunning = true;
while (effectRunning)
{
effectRunning = _forceEffects.Any(forceEffect =>
forceEffect.WaitForCompletionBeforeDeath && forceEffect.IsEffectRunning);
yield return null;
}
break;
}
}
if (_hasDamageEffects)
{
var effectRunning = true;
while (effectRunning)
{
effectRunning = _damageEffects.Any(damageEffect =>
damageEffect.WaitForCompletionBeforeDeath && damageEffect.IsEffectRunning);
yield return null;
}
}
yield return null;
Die();
}
/// <summary>
/// Do Damage.
///
/// ToDo: take into account stats and damage type.
/// </summary>
/// <param name="amount">The amount of damage to do</param>
/// <param name="other">The Transform of the object that is doing the damage. Used for Force Effects i.e. knock-back.</param>
public void Damage(int amount, Transform other)
{
if (_isDying) return;
_currentHitPoints -= amount;
StartCoroutine(GotHitRoutine(other));
DetectDeath();
}
}
}
And here is the damage source
using Damageables;
using UnityEngine;
namespace Weapons
{
/// <summary>
/// Attach to A Game Object to deal damage to a Damageable.
/// The amount of damage that this source does has to be set through script.
/// This is usually set by a weapon when it is enabled/spawned.
/// </summary>
public class DamageSource : MonoBehaviour
{
private int _damageAmount;
#region Unity Methods
private void OnTriggerEnter2D(Collider2D col)
{
var damageable = col.GetComponent<Damageable>();
if (damageable) damageable.Damage(_damageAmount, transform);
}
#endregion
public void SetDamage(int amount)
{
_damageAmount = amount;
}
}
}
I use the Physics Layers to determine what weapons can collide with what objects, This is why there are no checks in the damage source.
I do know that I want to be able to deflect a projectile back at an enemy with a shield or sword hit (Like in the Zelda games.) So I may have to change this slightly at the end.
Here is My Slime
And my Player, and Sword.
My Game so far.
I Just noticed that at some point my weapons quite following the mouse so I am going to have to figure that out. It was working earlier after I refactored the Projectile, and weapon classes. Edit Update: I found the problem It now follows the mouse, the issue was at some point I changed from using Camera.main to Camera.current in my Mouse Follow script, I am not sure if this was working before like this or not, but changing back to using camera.main fixed the problem.
But notice the the enemies do not damage each other or the bushes, they do damage the Player. I have to fix the knock back for the Player, I will do that when we get to that point in the lecture.