started building NPC combat
This commit is contained in:
@@ -14,6 +14,9 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
[Tooltip("Your main camera's transform - used to know where you're looking")]
|
||||
[SerializeField] private Transform _cameraTransform;
|
||||
|
||||
[Tooltip("The player's stats script for stamina usage")]
|
||||
[SerializeField] private PlayerStats _playerStats;
|
||||
|
||||
[Header("Weapon Management")]
|
||||
[Tooltip("The active weapon used for damage and stats.")]
|
||||
[SerializeField] private Weapon _currentWeapon;
|
||||
@@ -54,10 +57,12 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
private float _equipWeight;
|
||||
private int _meleeLayerIndex;
|
||||
|
||||
private Transform _spineBone;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_meleeLayerIndex = _animator.GetLayerIndex(_meleeLayerName);
|
||||
_spineBone = _animator.GetBoneTransform(HumanBodyBones.Spine);
|
||||
|
||||
// If the referenced weapon is a prefab asset (not in the scene), instantiate it
|
||||
if (_currentWeapon != null && !_currentWeapon.gameObject.scene.IsValid())
|
||||
@@ -97,10 +102,55 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
_blockAction.Disable();
|
||||
}
|
||||
|
||||
private bool _isAttacking;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Blend the melee layer weight based on equip state
|
||||
float equipTarget = _isEquipped ? 1f : 0f;
|
||||
// We only want this layer to be active if we are actively attacking, blocking, or equipping.
|
||||
bool isLayerActive = false;
|
||||
_isAttacking = false;
|
||||
|
||||
if (_animator != null)
|
||||
{
|
||||
// Check all layers for the Attack state
|
||||
for (int i = 0; i < _animator.layerCount; i++)
|
||||
{
|
||||
AnimatorStateInfo stateInfo = _animator.GetCurrentAnimatorStateInfo(i);
|
||||
if (stateInfo.IsTag("Attack") ||
|
||||
stateInfo.IsName("Attack") ||
|
||||
stateInfo.IsName("Attack1") ||
|
||||
stateInfo.IsName("Attack2") ||
|
||||
stateInfo.IsName("Attack3") ||
|
||||
stateInfo.IsName("A_MOD_SWD_Attack_03"))
|
||||
{
|
||||
_isAttacking = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (_meleeLayerIndex != -1)
|
||||
{
|
||||
AnimatorStateInfo stateInfo = _animator.GetCurrentAnimatorStateInfo(_meleeLayerIndex);
|
||||
|
||||
bool isPlayingCombatAnim = stateInfo.IsName("Equip") ||
|
||||
stateInfo.IsName("Unequip");
|
||||
|
||||
// If attacking on ANY layer, or blocking/equipping, keep the combat state active
|
||||
if (_isPoweringUp || _isBlocking || isPlayingCombatAnim)
|
||||
{
|
||||
isLayerActive = true;
|
||||
}
|
||||
|
||||
// If the attack is actually on the melee layer (old setup), keep weight 1
|
||||
if (stateInfo.IsName("Attack"))
|
||||
{
|
||||
isLayerActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Blend the melee layer weight based on if it's currently active
|
||||
float equipTarget = isLayerActive ? 1f : 0f;
|
||||
_equipWeight = Mathf.MoveTowards(_equipWeight, equipTarget, _equipBlendSpeed * Time.deltaTime);
|
||||
|
||||
if (_meleeLayerIndex != -1)
|
||||
@@ -121,6 +171,12 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
{
|
||||
if (_isEquipped) yield break;
|
||||
|
||||
if (_currentWeapon == null)
|
||||
{
|
||||
Debug.LogWarning("Cannot equip! No melee weapon is assigned.");
|
||||
yield break;
|
||||
}
|
||||
|
||||
_isEquipped = true;
|
||||
|
||||
if (_meleeLayerIndex != -1)
|
||||
@@ -221,6 +277,11 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
if (!_isEquipped) return;
|
||||
_isBlocking = true;
|
||||
|
||||
if (_playerStats != null)
|
||||
{
|
||||
_playerStats.staminaRegenMultiplier = 0.5f; // Half regen while blocking
|
||||
}
|
||||
|
||||
// Slow down to walk speed while blocking
|
||||
_inputReader.onWalkToggled?.Invoke();
|
||||
}
|
||||
@@ -231,6 +292,11 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
{
|
||||
_isBlocking = false;
|
||||
|
||||
if (_playerStats != null)
|
||||
{
|
||||
_playerStats.staminaRegenMultiplier = 1f; // Restore regen rate
|
||||
}
|
||||
|
||||
// Return to normal speed
|
||||
_inputReader.onWalkToggled?.Invoke();
|
||||
}
|
||||
@@ -244,6 +310,14 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
{
|
||||
if (_animator != null)
|
||||
{
|
||||
// Optional: You can check if there's enough stamina to attack here
|
||||
// e.g. if (_playerStats != null && !_playerStats.HasStaminaPercentage(0.1f)) return;
|
||||
|
||||
if (_playerStats != null)
|
||||
{
|
||||
_playerStats.UseStamina(15f); // Use 15 stamina per swing
|
||||
}
|
||||
|
||||
_animator.SetTrigger(_attackHash);
|
||||
}
|
||||
}
|
||||
@@ -256,11 +330,13 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
/// </summary>
|
||||
public void Hit()
|
||||
{
|
||||
Debug.Log("Animation Event 'Hit' triggered!");
|
||||
DealDamage();
|
||||
}
|
||||
|
||||
public void DealDamage()
|
||||
{
|
||||
Debug.Log("DealDamage called!");
|
||||
if (_currentWeapon == null)
|
||||
{
|
||||
Debug.LogWarning("No weapon assigned to deal damage!");
|
||||
@@ -271,17 +347,19 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
|
||||
if (_currentWeapon.hitShape == Weapon.HitShape.Sphere)
|
||||
{
|
||||
hitColliders = Physics.OverlapSphere(_currentWeapon.GetHitCenter(), _currentWeapon.attackRadius, _targetLayers);
|
||||
hitColliders = Physics.OverlapSphere(_currentWeapon.GetHitCenter(), _currentWeapon.attackRadius, _targetLayers, QueryTriggerInteraction.Ignore);
|
||||
}
|
||||
else
|
||||
{
|
||||
hitColliders = Physics.OverlapBox(_currentWeapon.GetHitCenter(), _currentWeapon.hitBoxHalfExtents, _currentWeapon.GetHitRotation(), _targetLayers);
|
||||
hitColliders = Physics.OverlapBox(_currentWeapon.GetHitCenter(), _currentWeapon.hitBoxHalfExtents, _currentWeapon.GetHitRotation(), _targetLayers, QueryTriggerInteraction.Ignore);
|
||||
}
|
||||
|
||||
foreach (Collider hit in hitColliders)
|
||||
{
|
||||
Debug.Log($"Melee hit {hit.name} for {_currentWeapon.damage} damage!");
|
||||
hit.GetComponent<WorldObject>()?.TakeDamage(_currentWeapon.damage);
|
||||
hit.GetComponent<NPCController>()?.TakeDamage(_currentWeapon.damage);
|
||||
hit.GetComponent<PlayerStats>()?.TakeDamage(_currentWeapon.damage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,11 +367,40 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
{
|
||||
if (_isBlocking)
|
||||
{
|
||||
Debug.Log($"Blocked {amount} damage!");
|
||||
return;
|
||||
if (_playerStats != null)
|
||||
{
|
||||
if (_playerStats.currentStamina >= amount)
|
||||
{
|
||||
Debug.Log($"Blocked {amount} damage! Cost {amount} stamina.");
|
||||
_playerStats.UseStamina(amount);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Block broken! Took {amount} damage.");
|
||||
_isBlocking = false;
|
||||
_playerStats.staminaRegenMultiplier = 1f;
|
||||
_playerStats.UseStamina(_playerStats.currentStamina); // Drain rest of stamina
|
||||
_playerStats.PauseStaminaRegen(3f); // Pause regen for 3 seconds
|
||||
_inputReader.onWalkToggled?.Invoke(); // Stop walking slow
|
||||
|
||||
// Optionally, play a guard break animation here if you have one
|
||||
// _animator.SetTrigger("GuardBreak");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"Blocked {amount} damage!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"Took {amount} damage!");
|
||||
if (_playerStats != null)
|
||||
{
|
||||
_playerStats.TakeDamage(amount);
|
||||
}
|
||||
|
||||
if (_animator != null)
|
||||
{
|
||||
_animator.SetTrigger(_takeDamageHash);
|
||||
@@ -302,5 +409,28 @@ public class MeleeCombatLayerController : MonoBehaviour
|
||||
|
||||
#endregion
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
// If we are not attacking and the equip weight is 0, don't bend the spine
|
||||
if (_spineBone == null || _cameraTransform == null) return;
|
||||
if (_equipWeight <= 0f && !_isAttacking) return;
|
||||
|
||||
// Calculate the camera's pitch (up/down rotation)
|
||||
// We use the camera's forward vector compared to the horizontal plane
|
||||
Vector3 cameraForward = _cameraTransform.forward;
|
||||
Vector3 horizontalForward = new Vector3(cameraForward.x, 0, cameraForward.z).normalized;
|
||||
float pitch = Vector3.SignedAngle(horizontalForward, cameraForward, _cameraTransform.right);
|
||||
|
||||
// Clamp the pitch so we don't break the spine backward
|
||||
pitch = Mathf.Clamp(pitch, -45f, 45f);
|
||||
|
||||
// Apply pitch to the spine bone.
|
||||
// If we are doing a full-body attack, force the blend weight to 1.0!
|
||||
float blendWeight = _isAttacking ? 1.0f : _equipWeight;
|
||||
|
||||
// We rotate around the character's right axis to lean forward/backward
|
||||
_spineBone.rotation = Quaternion.AngleAxis(pitch * blendWeight, transform.right) * _spineBone.rotation;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user