lot's of fixes, fixed animation retargetting, fixed FSM
This commit is contained in:
@@ -9,6 +9,68 @@ namespace Beyond
|
||||
{
|
||||
public class bControlAIMelee : vControlAIMelee
|
||||
{
|
||||
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start(); // Call the original start method
|
||||
|
||||
// Be absolutely certain the NavMeshAgent is not controlling rotation
|
||||
if (navMeshAgent)
|
||||
{
|
||||
navMeshAgent.updateRotation = false;
|
||||
Debug.Log("Forcing NavMeshAgent.updateRotation to FALSE.", this.gameObject);
|
||||
}
|
||||
}
|
||||
/*
|
||||
/// <summary>
|
||||
/// We override the base Rotate method. If our animation has the special tag,
|
||||
/// we do nothing and let root motion handle it. Otherwise, we call the
|
||||
/// original Invector rotation logic.
|
||||
/// </summary>
|
||||
/// <param name="targetDirection">The direction the AI wants to look.</param>
|
||||
protected override void Rotate(Vector3 targetDirection)
|
||||
{
|
||||
//return;
|
||||
if (IsAnimatorTag("LockRotationToRoot"))
|
||||
{
|
||||
// Our special turn animation is playing, so we block
|
||||
// the AI's attempt to rotate the character manually.
|
||||
return;
|
||||
}
|
||||
// If it's any other animation, let Invector's original code run.
|
||||
base.Rotate(targetDirection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We must also override the second version of the Rotate method that
|
||||
/// takes a Quaternion to be thorough.
|
||||
/// </summary>
|
||||
/// <param name="targetRotation">The rotation the AI wants to have.</param>
|
||||
protected override void Rotate(Quaternion targetRotation)
|
||||
{
|
||||
//return;
|
||||
if (IsAnimatorTag("LockRotationToRoot"))
|
||||
{
|
||||
// Block manual rotation during our turn animation.
|
||||
return;
|
||||
}
|
||||
// Otherwise, run the original code.
|
||||
base.Rotate(targetRotation);
|
||||
}
|
||||
// --- YOUR ORIGINAL OnAnimatorMove CAN NOW BE SIMPLIFIED OR REMOVED ---
|
||||
// Your OnAnimatorMove is no longer strictly necessary because we've stopped
|
||||
// the conflict at the source. However, leaving it in provides a safety net.
|
||||
// It's your choice to keep it or remove it. For now, let's keep it.
|
||||
protected override void OnAnimatorMove()
|
||||
{
|
||||
base.OnAnimatorMove();
|
||||
|
||||
if (useRootMotion && animator != null && Time.deltaTime != 0)
|
||||
{
|
||||
transform.rotation = animator.rootRotation;
|
||||
}
|
||||
}
|
||||
*/
|
||||
public void ResetAnimationTags()
|
||||
{
|
||||
if (animatorStateInfos == null)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
// Paste this code into your existing bMeleeAttackControl.cs file, replacing its content.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Beyond; // For Player, GameStateManager, TimeController
|
||||
@@ -39,42 +41,64 @@ namespace Invector.vMelee
|
||||
public bool resetAttackTrigger;
|
||||
[Tooltip("Normalized time point to reset attack triggers if resetAttackTrigger is true.")]
|
||||
public float resetTriggerBeforeTime = 0.5f;
|
||||
|
||||
// --- NEW ---: Combo Rotation and Position Lerp Settings
|
||||
[Header("Combo & Movement")]
|
||||
[Tooltip("Normalized time to unlock rotation, allowing the player to aim the next attack in a combo. Set to 1 to disable.")]
|
||||
[Range(0,1)]
|
||||
public float unlockRotationTime = 0.7f;
|
||||
|
||||
[Tooltip("Enable to make the character move towards the target during the attack.")]
|
||||
public bool lerpPositionTowardsTarget = false;
|
||||
|
||||
[vHideInInspector("lerpPositionTowardsTarget")]
|
||||
[Tooltip("Max distance from the target to start moving towards it.")]
|
||||
public float maxLerpDistance = 3.5f;
|
||||
|
||||
[vHideInInspector("lerpPositionTowardsTarget")]
|
||||
[Tooltip("How fast the character moves towards the target.")]
|
||||
public float positionLerpSpeed = 2.0f;
|
||||
|
||||
[vHideInInspector("lerpPositionTowardsTarget")]
|
||||
[Tooltip("How close the character should get to the target.")]
|
||||
public float stoppingDistance = 1.2f;
|
||||
// --- END NEW ---
|
||||
|
||||
[Header("Slow Motion Settings")]
|
||||
[Tooltip("Enable slow motion effect during this attack based on conditions below.")]
|
||||
public bool useAttackTimeScale = false;
|
||||
[Tooltip("Distance within which the current auto-target must be for slow motion to consider activating. Analogous to slowMoActivationDistance in vMeleeAttackControl.")]
|
||||
public float maxTargetDistance = 3f; // This will be used as slowMoActivationDistance
|
||||
[Tooltip("Target health threshold below which slow motion might activate (if near).")]
|
||||
public float maxTargetDistance = 3f;
|
||||
public float lowHealthTh = 10f;
|
||||
[Tooltip("Time scale to apply during slow motion.")]
|
||||
public float attackTimeScale = 0.2f;
|
||||
[Tooltip("Normalized time to start the slow motion window. If < 0, uses 'startDamage'.")]
|
||||
public float attackTimeScaleStart = -1f;
|
||||
[Tooltip("Normalized time to end the slow motion window. If < 0, uses 'endDamage'.")]
|
||||
public float attackTimeScaleEnd = -1f;
|
||||
|
||||
[Header("Rotation Settings")]
|
||||
[Tooltip("If true, the character will attempt to rotate towards the auto-target during this attack state.")]
|
||||
public bool rotatePlayerTowardsTarget;
|
||||
[Tooltip("The angle (in degrees from player's forward) within which the auto-target must be for rotation to engage. Analogous to rotationActivationAngle in vMeleeAttackControl.")]
|
||||
public float degreeThreshold = 20f; // This will be used as rotationActivationAngle
|
||||
// rotationSpeed field is now unused, AutoTargetting.playerRotationSpeed will be used.
|
||||
// maxTurnTowardDistance field is now unused.
|
||||
public float degreeThreshold = 20f;
|
||||
|
||||
[Header("Debug")]
|
||||
public bool debug;
|
||||
|
||||
// Private state variables
|
||||
private bool isActive; // Is damage window active
|
||||
private bool isActive;
|
||||
private vIAttackListener mFighter;
|
||||
private bool isAttacking; // Is this attack state logic considered "attacking"
|
||||
private bool m_hasScaledTime; // Has slow motion been activated in this instance of the state
|
||||
private bool isAttacking;
|
||||
private bool m_hasScaledTime;
|
||||
private AutoTargetting _autoTargettingInstance;
|
||||
|
||||
// --- NEW ---: Private variables for new features
|
||||
private bThirdPersonController _characterController;
|
||||
private bool _isRotationLockedByThis; // Tracks if this specific state has locked rotation
|
||||
// --- END NEW ---
|
||||
|
||||
override public void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
|
||||
{
|
||||
mFighter = animator.GetComponent<vIAttackListener>();
|
||||
// --- NEW ---: Get reference to the character controller
|
||||
_characterController = animator.GetComponent<bThirdPersonController>();
|
||||
// --- END NEW ---
|
||||
|
||||
if (Player.Instance != null)
|
||||
{
|
||||
@@ -83,12 +107,21 @@ namespace Invector.vMelee
|
||||
|
||||
if (_autoTargettingInstance == null && debug)
|
||||
{
|
||||
Debug.LogWarning($"({damageType}) AutoTargetting instance not found via Player.Instance.AutoTarget on {animator.name}. Rotation and target-dependent slow-mo will be limited.");
|
||||
Debug.LogWarning($"({damageType}) AutoTargetting instance not found on {animator.name}. Rotation and target-dependent features will be limited.");
|
||||
}
|
||||
|
||||
isAttacking = true;
|
||||
isActive = false;
|
||||
m_hasScaledTime = false;
|
||||
|
||||
// --- NEW ---: Lock character rotation at the beginning of the attack
|
||||
if (_characterController != null)
|
||||
{
|
||||
_characterController.lockRotation = true;
|
||||
_isRotationLockedByThis = true;
|
||||
if (debug) Debug.Log($"({damageType}) Rotation locked by state.");
|
||||
}
|
||||
// --- END NEW ---
|
||||
|
||||
if (mFighter != null)
|
||||
mFighter.OnEnableAttack();
|
||||
@@ -102,24 +135,33 @@ namespace Invector.vMelee
|
||||
|
||||
override public void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
|
||||
{
|
||||
if (Player.Instance.ActiveWeaponTrail) // Existing weapon trail logic
|
||||
if (Player.Instance.ActiveWeaponTrail)
|
||||
{
|
||||
Player.Instance.ActiveWeaponTrail.m_colorMultiplier = Color.white + Color.red * damageMultiplier;
|
||||
}
|
||||
|
||||
float currentNormalizedTime = stateInfo.normalizedTime % 1;
|
||||
if (currentNormalizedTime == 0 && stateInfo.normalizedTime > 0.5f) currentNormalizedTime = 1f;
|
||||
|
||||
// --- MODIFIED ---: Rotation and Movement logic is now more sophisticated
|
||||
// Only execute script-based rotation if the controller's rotation is locked
|
||||
if (_characterController != null && _characterController.lockRotation)
|
||||
{
|
||||
AttemptRotationTowardsAutoTarget(animator);
|
||||
}
|
||||
|
||||
// Handle position lerping
|
||||
AttemptPositionLerp(animator);
|
||||
|
||||
// --- ROTATION LOGIC ---
|
||||
AttemptRotationTowardsAutoTarget(animator);
|
||||
// Handle unlocking rotation for combo aiming
|
||||
UpdateRotationLock(currentNormalizedTime);
|
||||
// --- END MODIFIED ---
|
||||
|
||||
// --- SLOW MOTION LOGIC ---
|
||||
if (useAttackTimeScale)
|
||||
{
|
||||
UpdateSlowMotion(animator, stateInfo, currentNormalizedTime);
|
||||
}
|
||||
|
||||
// --- DAMAGE WINDOW LOGIC ---
|
||||
if (!isActive && currentNormalizedTime >= startDamage && currentNormalizedTime <= endDamage)
|
||||
{
|
||||
if (debug) Debug.Log($"({damageType}) Enable Damage: normTime={currentNormalizedTime:F2} (Start:{startDamage:F2}, End:{endDamage:F2})");
|
||||
@@ -131,13 +173,8 @@ namespace Invector.vMelee
|
||||
if (debug) Debug.Log($"({damageType}) Disable Damage: normTime={currentNormalizedTime:F2} > {endDamage:F2}");
|
||||
isActive = false;
|
||||
ActiveDamage(animator, false);
|
||||
// Note: Original bMeleeAttackControl reset m_hasScaledTime here.
|
||||
// vMeleeAttackControl resets it if currentNormalizedTime > attackTimeScaleEnd.
|
||||
// TimeController handles restoring time, so m_hasScaledTime is more about preventing re-triggering within one state.
|
||||
// It's reset in UpdateSlowMotion or OnStateExit.
|
||||
}
|
||||
|
||||
// --- ATTACK STATE AND TRIGGER RESET LOGIC ---
|
||||
if (isAttacking)
|
||||
{
|
||||
if (currentNormalizedTime > endDamage)
|
||||
@@ -146,15 +183,10 @@ namespace Invector.vMelee
|
||||
isAttacking = false;
|
||||
if (debug) Debug.Log($"({damageType}) OnDisableAttack called: normTime={currentNormalizedTime:F2}");
|
||||
}
|
||||
// Original bMelee had: "if (nTime > .1f && nTime < resetTriggerBeforeTime && isAttacking)"
|
||||
// vMelee has: "else if (resetAttackTrigger && currentNormalizedTime >= resetTriggerBeforeTime)"
|
||||
// Adopting vMelee's more standard approach for early reset:
|
||||
else if (resetAttackTrigger && currentNormalizedTime >= resetTriggerBeforeTime)
|
||||
{
|
||||
if (mFighter != null) mFighter.ResetAttackTriggers();
|
||||
if (debug) Debug.Log($"({damageType}) ResetAttackTriggers called: normTime={currentNormalizedTime:F2}");
|
||||
// To prevent multiple calls, ideally ResetAttackTriggers is idempotent or use a flag.
|
||||
// For now, matching vMelee's potential for multiple calls if time hovers.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,13 +209,22 @@ namespace Invector.vMelee
|
||||
}
|
||||
isAttacking = false;
|
||||
|
||||
m_hasScaledTime = false; // Reset slow motion flag
|
||||
m_hasScaledTime = false;
|
||||
|
||||
if (mFighter != null && resetAttackTrigger) // Final reset if configured
|
||||
if (mFighter != null && resetAttackTrigger)
|
||||
{
|
||||
mFighter.ResetAttackTriggers();
|
||||
if (debug) Debug.Log($"({damageType}) ResetAttackTriggers called on StateExit due to resetAttackTrigger flag.");
|
||||
}
|
||||
|
||||
// --- NEW ---: Ensure rotation is unlocked upon exiting the state
|
||||
if (_characterController != null && _isRotationLockedByThis)
|
||||
{
|
||||
_characterController.lockRotation = false;
|
||||
_isRotationLockedByThis = false;
|
||||
if (debug) Debug.Log($"({damageType}) Rotation unlocked by state on exit.");
|
||||
}
|
||||
// --- END NEW ---
|
||||
}
|
||||
|
||||
private void AttemptRotationTowardsAutoTarget(Animator animator)
|
||||
@@ -193,19 +234,54 @@ namespace Invector.vMelee
|
||||
return;
|
||||
}
|
||||
|
||||
// Using bMeleeAttackControl's degreeThreshold as the activation angle
|
||||
if (_autoTargettingInstance.IsTargetInAngle(animator.transform, _autoTargettingInstance.CurrentTarget, degreeThreshold))
|
||||
{
|
||||
// AutoTargetting.playerRotationSpeed will be used internally by this call
|
||||
_autoTargettingInstance.ExecuteRotationTowardsCurrentTarget(Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
// --- NEW ---
|
||||
private void UpdateRotationLock(float normalizedTime)
|
||||
{
|
||||
if (_characterController != null && _isRotationLockedByThis && normalizedTime >= unlockRotationTime)
|
||||
{
|
||||
_characterController.lockRotation = false;
|
||||
_isRotationLockedByThis = false; // Stop this state from managing the lock
|
||||
if(debug) Debug.Log($"({damageType}) Rotation unlocked for combo aiming at normTime={normalizedTime:F2}");
|
||||
}
|
||||
}
|
||||
|
||||
private void AttemptPositionLerp(Animator animator)
|
||||
{
|
||||
if (!lerpPositionTowardsTarget || _characterController == null || _autoTargettingInstance == null || _autoTargettingInstance.CurrentTarget == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Transform playerTransform = _characterController.transform;
|
||||
Transform targetTransform = _autoTargettingInstance.CurrentTarget.transform;
|
||||
|
||||
float distance = Vector3.Distance(playerTransform.position, targetTransform.position);
|
||||
|
||||
// Only lerp if within max distance and further than stopping distance
|
||||
if (distance <= maxLerpDistance && distance > stoppingDistance)
|
||||
{
|
||||
Vector3 directionToTarget = (targetTransform.position - playerTransform.position).normalized;
|
||||
directionToTarget.y = 0; // Keep movement on the horizontal plane
|
||||
|
||||
// The target position is a point in front of the enemy at the stopping distance
|
||||
Vector3 targetPosition = targetTransform.position - directionToTarget * stoppingDistance;
|
||||
|
||||
// Use MoveTowards for consistent speed. This adds to root motion rather than fighting it.
|
||||
playerTransform.position = Vector3.MoveTowards(playerTransform.position, targetPosition, positionLerpSpeed * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
// --- END NEW ---
|
||||
|
||||
private void UpdateSlowMotion(Animator animator, AnimatorStateInfo stateInfo, float currentNormalizedTime)
|
||||
{
|
||||
// This method is called only if useAttackTimeScale (field) is true.
|
||||
// ... (this method remains unchanged)
|
||||
if (_autoTargettingInstance == null || TimeController.Instance == null) return;
|
||||
|
||||
if (!m_hasScaledTime)
|
||||
{
|
||||
if (currentNormalizedTime >= attackTimeScaleStart && currentNormalizedTime <= attackTimeScaleEnd)
|
||||
@@ -213,65 +289,45 @@ namespace Invector.vMelee
|
||||
bool triggerSlowMo = false;
|
||||
if (_autoTargettingInstance.CurrentTarget != null)
|
||||
{
|
||||
// Use bMeleeAttackControl's maxTargetDistance as the activation distance
|
||||
float distSqr = (_autoTargettingInstance.CurrentTarget.transform.position - animator.transform.position).sqrMagnitude;
|
||||
bool targetNear = distSqr <= maxTargetDistance * maxTargetDistance;
|
||||
|
||||
if (targetNear)
|
||||
{
|
||||
// Mimicking vMeleeAttackControl's effective logic:
|
||||
// If useAttackTimeScale (field) is true (which it is to get here) and target is near, then trigger.
|
||||
// The lowHealthTh can act as an additional, prioritized condition if desired,
|
||||
// but with current structure, `this.useAttackTimeScale` being true makes the second part of OR true.
|
||||
float currentTargetHealth = _autoTargettingInstance.GetCurrentTargetHealth();
|
||||
bool targetHealthLow = currentTargetHealth > 0f && currentTargetHealth < lowHealthTh;
|
||||
|
||||
if (targetHealthLow) // Prioritize if health is low and near
|
||||
if (targetHealthLow)
|
||||
{
|
||||
triggerSlowMo = true;
|
||||
}
|
||||
// else if (this.useAttackTimeScale) // This refers to the field, which is true if we are in this function.
|
||||
// So if target is Near, this path will be taken if not already low health.
|
||||
// Simplified: if targetNear, triggerSlowMo = true because this.useAttackTimeScale is already true.
|
||||
// The following `else if` is essentially `else if (true)`
|
||||
else
|
||||
{
|
||||
triggerSlowMo = true; // General case: near and useAttackTimeScale is on
|
||||
triggerSlowMo = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// else: No current target, so no target-dependent slow motion.
|
||||
|
||||
if (triggerSlowMo)
|
||||
{
|
||||
// Use vMeleeAttackControl's duration calculation for SetTimeScaleForSec
|
||||
float slowMoEffectDuration = (attackTimeScaleEnd - currentNormalizedTime) * stateInfo.length;
|
||||
if (slowMoEffectDuration > 0.01f) // Ensure a meaningful duration
|
||||
if (slowMoEffectDuration > 0.01f)
|
||||
{
|
||||
// The 'true' for forceUnique in bMelee's original TimeController call.
|
||||
// Assuming TimeController.Instance.SetTimeScaleForSec now handles this or has an overload.
|
||||
// If SetTimeScaleForSec(scale, duration, bool forceUnique) exists:
|
||||
// TimeController.Instance.SetTimeScaleForSec(attackTimeScale, slowMoEffectDuration, true);
|
||||
// If not, use the existing TimeController method. For now, assuming vMelee's version:
|
||||
TimeController.Instance.SetTimeScaleForSec(attackTimeScale, slowMoEffectDuration);
|
||||
if (debug) Debug.Log($"({damageType}) Slow-mo ACTIVATED. Target: {_autoTargettingInstance.CurrentTarget?.name ?? "N/A"}. Duration: {slowMoEffectDuration:F2}s. NormTime: {currentNormalizedTime:F2}");
|
||||
}
|
||||
else if (debug) Debug.Log($"({damageType}) Slow-mo trigger met, but calculated duration too short ({slowMoEffectDuration:F2}s). NormTime: {currentNormalizedTime:F2}");
|
||||
|
||||
m_hasScaledTime = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (currentNormalizedTime > attackTimeScaleEnd && m_hasScaledTime) // If slow-mo was active and window has passed
|
||||
else if (currentNormalizedTime > attackTimeScaleEnd && m_hasScaledTime)
|
||||
{
|
||||
m_hasScaledTime = false;
|
||||
if (debug) Debug.Log($"({damageType}) Slow-mo window ended (normTime={currentNormalizedTime:F2}). m_hasScaledTime reset.");
|
||||
// TimeController.Instance is responsible for restoring time scale.
|
||||
}
|
||||
}
|
||||
|
||||
void ActiveDamage(Animator animator, bool value)
|
||||
{
|
||||
// ... (this method remains unchanged)
|
||||
var meleeManager = animator.GetComponent<vMeleeManager>();
|
||||
if (meleeManager)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using Invector;
|
||||
using Invector.vCamera;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -9,11 +9,9 @@ namespace Beyond
|
||||
private static bThirdPersonCamera _instance;
|
||||
public static new bThirdPersonCamera instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return _instance;
|
||||
}
|
||||
get { return _instance; }
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_instance == null)
|
||||
@@ -22,13 +20,11 @@ namespace Beyond
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("bThirdPartyCamera already exists!");
|
||||
Debug.LogError("bThirdPersonCamera already exists!");
|
||||
Destroy(gameObject);
|
||||
}
|
||||
//#if !MOBILE_INPUT
|
||||
// Cursor.lockState = CursorLockMode.Locked;
|
||||
//#endif
|
||||
}
|
||||
|
||||
public Vector3 GetCameraTargetPosition(bool useDefaultRotation)
|
||||
{
|
||||
if (currentTarget)
|
||||
@@ -45,5 +41,151 @@ namespace Beyond
|
||||
}
|
||||
}
|
||||
|
||||
//********************************************************************************//
|
||||
// >> CUSTOM CAMERA LOGIC OVERRIDE << //
|
||||
//********************************************************************************//
|
||||
// This method overrides the base class's CameraMovement function. //
|
||||
// The new logic prioritizes pushing the camera towards the player in narrow //
|
||||
// spaces, and only raises the camera's height as a last resort when it gets //
|
||||
// too close. This prevents the camera from going over the player's head in //
|
||||
// tight corridors. //
|
||||
//********************************************************************************//
|
||||
/*
|
||||
protected override void CameraMovement(bool forceUpdate = false)
|
||||
{
|
||||
if (currentTarget == null || targetCamera == null || (!firstStateIsInit && !forceUpdate))
|
||||
{
|
||||
base.CameraMovement(forceUpdate);
|
||||
return;
|
||||
}
|
||||
|
||||
transformWeight = Mathf.Clamp(transformWeight += Time.fixedDeltaTime, 0f, 1f);
|
||||
if (useSmooth)
|
||||
{
|
||||
currentState.Slerp(lerpState, smoothBetweenState * Time.fixedDeltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState.CopyState(lerpState);
|
||||
}
|
||||
|
||||
if (currentState.useZoom)
|
||||
{
|
||||
currentZoom = Mathf.Clamp(currentZoom, currentState.minDistance, currentState.maxDistance);
|
||||
distance = useSmooth ? Mathf.Lerp(distance, currentZoom, lerpState.smooth * Time.fixedDeltaTime) : currentZoom;
|
||||
}
|
||||
else
|
||||
{
|
||||
distance = useSmooth ? Mathf.Lerp(distance, currentState.defaultDistance, lerpState.smooth * Time.fixedDeltaTime) : currentState.defaultDistance;
|
||||
currentZoom = currentState.defaultDistance;
|
||||
}
|
||||
|
||||
targetCamera.fieldOfView = currentState.fov;
|
||||
cullingDistance = Mathf.Lerp(cullingDistance, currentZoom, smoothBetweenState * Time.fixedDeltaTime);
|
||||
currentSwitchRight = Mathf.Lerp(currentSwitchRight, switchRight, smoothSwitchSide * Time.fixedDeltaTime);
|
||||
var camDir = (currentState.forward * targetLookAt.forward) + ((currentState.right * currentSwitchRight) * targetLookAt.right);
|
||||
|
||||
camDir = camDir.normalized;
|
||||
|
||||
var targetPos = new Vector3(currentTarget.position.x, currentTarget.position.y, currentTarget.position.z) + currentTarget.transform.up * offSetPlayerPivot;
|
||||
currentTargetPos = targetPos;
|
||||
desired_cPos = targetPos + currentTarget.transform.up * currentState.height;
|
||||
|
||||
current_cPos = firstUpdated ? targetPos + currentTarget.transform.up * currentHeight : Vector3.SmoothDamp(current_cPos, targetPos + currentTarget.transform.up * currentHeight, ref cameraVelocityDamp, lerpState.smoothDamp * Time.fixedDeltaTime);
|
||||
firstUpdated = false;
|
||||
RaycastHit hitInfo;
|
||||
|
||||
// --- Start of Modified Collision Logic ---
|
||||
|
||||
if (Physics.SphereCast(targetPos, checkHeightRadius, currentTarget.transform.up, out hitInfo, currentState.cullingHeight + 0.2f, cullingLayer))
|
||||
{
|
||||
var t = hitInfo.distance - 0.2f;
|
||||
t -= currentState.height;
|
||||
t /= (currentState.cullingHeight - currentState.height);
|
||||
cullingHeight = Mathf.Lerp(currentState.height, currentState.cullingHeight, Mathf.Clamp(t, 0.0f, 1.0f));
|
||||
}
|
||||
else
|
||||
{
|
||||
cullingHeight = useSmooth ? Mathf.Lerp(cullingHeight, currentState.cullingHeight, smoothBetweenState * Time.fixedDeltaTime) : currentState.cullingHeight;
|
||||
}
|
||||
|
||||
currentHeight = useSmooth ? Mathf.Lerp(currentHeight, currentState.height, smoothBetweenState * Time.fixedDeltaTime) : currentState.height;
|
||||
var temporary_cPos = targetPos + currentTarget.transform.up * currentHeight;
|
||||
|
||||
ClipPlanePoints clipPoints = targetCamera.NearClipPlanePoints(temporary_cPos + (camDir * distance), clipPlaneMargin);
|
||||
if (CullingRayCast(temporary_cPos, clipPoints, out hitInfo, distance, cullingLayer, Color.cyan))
|
||||
{
|
||||
distance = Mathf.Clamp(hitInfo.distance - clipPlaneMargin, 0.0f, currentState.defaultDistance);
|
||||
}
|
||||
|
||||
if (distance < currentState.cullingMinDist)
|
||||
{
|
||||
var t = distance / currentState.cullingMinDist;
|
||||
currentHeight = Mathf.Lerp(cullingHeight, currentState.height, Mathf.Clamp01(t));
|
||||
}
|
||||
|
||||
current_cPos = targetPos + currentTarget.transform.up * currentHeight;
|
||||
clipPoints = targetCamera.NearClipPlanePoints(current_cPos + (camDir * currentState.defaultDistance), clipPlaneMargin);
|
||||
if (CullingRayCast(current_cPos, clipPoints, out hitInfo, currentState.defaultDistance, cullingLayer, Color.cyan))
|
||||
{
|
||||
distance = Mathf.Clamp(hitInfo.distance - clipPlaneMargin, 0.0f, currentState.defaultDistance);
|
||||
}
|
||||
|
||||
// --- End of Modified Collision Logic ---
|
||||
|
||||
var lookPoint = current_cPos + targetLookAt.forward * targetCamera.farClipPlane;
|
||||
lookPoint += (targetLookAt.right * Vector3.Dot(camDir * (distance), targetLookAt.right));
|
||||
targetLookAt.position = current_cPos;
|
||||
|
||||
float _mouseY = Mathf.LerpAngle(mouseYStart, mouseY, transformWeight);
|
||||
float _mouseX = Mathf.LerpAngle(mouseXStart, mouseX, transformWeight);
|
||||
Quaternion newRot = Quaternion.Euler(_mouseY + offsetMouse.y, _mouseX + offsetMouse.x, 0);
|
||||
targetLookAt.rotation = useSmooth ? Quaternion.Lerp(targetLookAt.rotation, newRot, smoothCameraRotation * Time.fixedDeltaTime) : newRot;
|
||||
selfRigidbody.MovePosition(Vector3.Lerp(startPosition, current_cPos + (camDir * (distance)), transformWeight));
|
||||
var rotation = Quaternion.LookRotation((lookPoint) - selfRigidbody.position);
|
||||
|
||||
if (lockTarget)
|
||||
{
|
||||
CalculeLockOnPoint();
|
||||
|
||||
if (!(currentState.cameraMode.Equals(TPCameraMode.FixedAngle)))
|
||||
{
|
||||
var collider = lockTarget.GetComponent<Collider>();
|
||||
if (collider != null)
|
||||
{
|
||||
var point = (collider.bounds.center + Vector3.up * heightOffset) - selfRigidbody.position;
|
||||
var euler = Quaternion.LookRotation(point).eulerAngles - rotation.eulerAngles;
|
||||
if (isNewTarget)
|
||||
{
|
||||
lookTargetAdjust.x = Mathf.LerpAngle(lookTargetAdjust.x, euler.x, lockTargetWeight);
|
||||
lookTargetAdjust.y = Mathf.LerpAngle(lookTargetAdjust.y, euler.y, lockTargetWeight);
|
||||
lookTargetAdjust.z = Mathf.LerpAngle(lookTargetAdjust.z, euler.z, lockTargetWeight);
|
||||
if (Vector3.Distance(lookTargetAdjust, euler) < .5f)
|
||||
{
|
||||
isNewTarget = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lookTargetAdjust = euler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lookTargetAdjust.x = Mathf.LerpAngle(lookTargetAdjust.x, 0, currentState.smooth * Time.fixedDeltaTime);
|
||||
lookTargetAdjust.y = Mathf.LerpAngle(lookTargetAdjust.y, 0, currentState.smooth * Time.fixedDeltaTime);
|
||||
lookTargetAdjust.z = Mathf.LerpAngle(lookTargetAdjust.z, 0, currentState.smooth * Time.fixedDeltaTime);
|
||||
}
|
||||
var _euler = rotation.eulerAngles + lookTargetAdjust;
|
||||
_euler.z = 0;
|
||||
var _rot = Quaternion.Euler(_euler + currentState.rotationOffSet);
|
||||
selfRigidbody.MoveRotation(Quaternion.Lerp(startRotation, _rot, transformWeight));
|
||||
movementSpeed = Vector2.zero;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,29 @@ namespace Beyond
|
||||
{
|
||||
public class bThirdPersonController : vThirdPersonController
|
||||
{
|
||||
//public bool triggerDieBehaviour = false;
|
||||
protected bool m_isDashing;
|
||||
protected bool m_GodMode = false;
|
||||
|
||||
public bool m_ignoreTriggers = true;
|
||||
|
||||
[Header("Beyond's Custom Settings")]
|
||||
[Tooltip("When 'Use RootMotion' is checked, and this is true, the animation's root motion will control character rotation (for 8-way directional movement). If false, the script will rotate the character to face the input direction.")]
|
||||
public bool useAnimationBasedRotation = false;
|
||||
|
||||
[Header("Beyond's Strafe Combat Settings")]
|
||||
[Tooltip("The minimum horizontal input value to trigger a side roll instead of a forward/backward one.")]
|
||||
[Range(0.1f, 1.0f)]
|
||||
public float strafeRollInputThreshold = 0.3f;
|
||||
|
||||
[Tooltip("The name of the animation state to play when rolling forward while strafing. (This is now disabled)")]
|
||||
public string strafeRollForwardAnim = "Roll_Forward"; // Kept for reference, but won't be used
|
||||
[Tooltip("The name of the animation state to play when rolling backward while strafing.")]
|
||||
public string strafeRollBackwardAnim = "Roll_Backward";
|
||||
[Tooltip("The name of the animation state to play when rolling left while strafing.")]
|
||||
public string strafeRollLeftAnim = "Roll_Left";
|
||||
[Tooltip("The name of the animation state to play when rolling right while strafing.")]
|
||||
public string strafeRollRightAnim = "Roll_Right";
|
||||
|
||||
|
||||
public bool GodMode
|
||||
{
|
||||
get => m_GodMode;
|
||||
@@ -33,92 +50,121 @@ namespace Beyond
|
||||
protected override void Start()
|
||||
{
|
||||
base.Start();
|
||||
//var receiver = GetComponent<vAnimatorEventReceiver>();
|
||||
//var ev = new vAnimatorEvent();
|
||||
// ev.
|
||||
}
|
||||
|
||||
public void OnEvadeStart()
|
||||
protected override void RollBehavior()
|
||||
{
|
||||
if (m_GodMode)
|
||||
// If we are not strafing, use the default Invector roll behavior.
|
||||
if (!isStrafing)
|
||||
{
|
||||
base.RollBehavior();
|
||||
return;
|
||||
isImmortal = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEvadeEnd()
|
||||
{
|
||||
if (m_GodMode)
|
||||
// --- Custom Strafe Roll with Root Motion ---
|
||||
if (!isRolling)
|
||||
{
|
||||
return;
|
||||
isImmortal = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ActionsControl()
|
||||
{
|
||||
base.ActionsControl();
|
||||
m_isDashing = IsAnimatorTag("IsDashing");
|
||||
// We apply the root motion position change directly.
|
||||
Vector3 deltaPosition = new Vector3(animator.deltaPosition.x, 0f, animator.deltaPosition.z);
|
||||
Vector3 v = (deltaPosition / Time.deltaTime) * (1f - stopMoveWeight);
|
||||
|
||||
// Apply gravity to the roll if enabled
|
||||
if (rollUseGravity && animator.GetNormalizedTime(baseLayer) >= rollUseGravityTime)
|
||||
{
|
||||
v.y = _rigidbody.linearVelocity.y;
|
||||
}
|
||||
|
||||
_rigidbody.linearVelocity = v;
|
||||
}
|
||||
|
||||
public override void Roll()
|
||||
{
|
||||
OnRoll.Invoke();
|
||||
isRolling = true;
|
||||
|
||||
animator.CrossFadeInFixedTime("Roll", rollTransition, baseLayer);
|
||||
ReduceStamina(rollStamina, false);
|
||||
currentStaminaRecoveryDelay = 2f;
|
||||
// If we are strafing, use our custom directional logic.
|
||||
if (isStrafing)
|
||||
{
|
||||
TriggerStrafeRoll(strafeRollForwardAnim, strafeRollBackwardAnim, strafeRollLeftAnim, strafeRollRightAnim);
|
||||
}
|
||||
else // Otherwise, use the default free-locomotion roll.
|
||||
{
|
||||
OnRoll.Invoke();
|
||||
isRolling = true;
|
||||
animator.CrossFadeInFixedTime("Roll", rollTransition, baseLayer);
|
||||
ReduceStamina(rollStamina, false);
|
||||
currentStaminaRecoveryDelay = 2f;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Dash()
|
||||
{
|
||||
// The Dash logic now mirrors the Roll logic.
|
||||
if (isStrafing)
|
||||
{
|
||||
TriggerStrafeRoll(strafeRollForwardAnim, strafeRollBackwardAnim, strafeRollLeftAnim, strafeRollRightAnim);
|
||||
}
|
||||
else
|
||||
{
|
||||
OnRoll.Invoke();
|
||||
isRolling = true;
|
||||
animator.CrossFadeInFixedTime("Dash", rollTransition, baseLayer);
|
||||
ReduceStamina(rollStamina, false);
|
||||
currentStaminaRecoveryDelay = 2f;
|
||||
}
|
||||
}
|
||||
|
||||
// This is the private helper method that contains the core logic for this feature.
|
||||
private void TriggerStrafeRoll(string forwardAnim, string backwardAnim, string leftAnim, string rightAnim)
|
||||
{
|
||||
OnRoll.Invoke();
|
||||
isRolling = true;
|
||||
|
||||
animator.CrossFadeInFixedTime("Dash", rollTransition, baseLayer);
|
||||
ReduceStamina(rollStamina, false);
|
||||
currentStaminaRecoveryDelay = 2f;
|
||||
|
||||
string animToPlay;
|
||||
|
||||
// Prioritize side rolls based on horizontal input.
|
||||
if (Mathf.Abs(horizontalSpeed) > strafeRollInputThreshold)
|
||||
{
|
||||
animToPlay = horizontalSpeed > 0 ? rightAnim : leftAnim;
|
||||
}
|
||||
// If horizontal input is not met, always default to the backward roll.
|
||||
// This effectively blocks the forward roll.
|
||||
else
|
||||
{
|
||||
animToPlay = backwardAnim;
|
||||
}
|
||||
|
||||
animator.CrossFadeInFixedTime(animToPlay, rollTransition, baseLayer);
|
||||
}
|
||||
|
||||
public void OnEvadeStart() { if (!m_GodMode) isImmortal = true; }
|
||||
public void OnEvadeEnd() { if (!m_GodMode) isImmortal = false; }
|
||||
public override void ActionsControl() { base.ActionsControl(); m_isDashing = IsAnimatorTag("IsDashing"); }
|
||||
|
||||
protected override void DeadAnimation()
|
||||
{
|
||||
if (!isDead)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!triggerDieBehaviour)
|
||||
{
|
||||
triggerDieBehaviour = true;
|
||||
DeathBehaviour();
|
||||
}
|
||||
|
||||
// death by animation
|
||||
if (!isDead) return;
|
||||
if (!triggerDieBehaviour) { triggerDieBehaviour = true; DeathBehaviour(); }
|
||||
if (deathBy == DeathBy.Animation)
|
||||
{
|
||||
int deadLayer = 0;
|
||||
|
||||
var info = animatorStateInfos.GetStateInfoUsingTag("Dead");
|
||||
if (info != null)
|
||||
{
|
||||
if (!animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.99f && groundDistance <= 0.15f)
|
||||
{
|
||||
RemoveComponents();
|
||||
}
|
||||
if (!animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.99f && groundDistance <= 0.15f) RemoveComponents();
|
||||
}
|
||||
}
|
||||
// death by animation & ragdoll after a time
|
||||
else if (deathBy == DeathBy.AnimationWithRagdoll)
|
||||
{
|
||||
int deadLayer = 0;
|
||||
var info = animatorStateInfos.GetStateInfoUsingTag("Dead");
|
||||
if (info != null)
|
||||
{
|
||||
if (!animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.8f)
|
||||
{
|
||||
onActiveRagdoll.Invoke(null);
|
||||
}
|
||||
if (!animator.IsInTransition(deadLayer) && info.normalizedTime >= 0.8f) onActiveRagdoll.Invoke(null);
|
||||
}
|
||||
}
|
||||
// death by ragdoll
|
||||
else if (deathBy == DeathBy.Ragdoll)
|
||||
{
|
||||
onActiveRagdoll.Invoke(null);
|
||||
@@ -132,17 +178,8 @@ namespace Beyond
|
||||
|
||||
public override void ControlAnimatorRootMotion()
|
||||
{
|
||||
if (!this.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRolling)
|
||||
{
|
||||
RollBehavior();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.enabled) return;
|
||||
if (isRolling) { RollBehavior(); return; }
|
||||
if (customAction || lockAnimMovement)
|
||||
{
|
||||
StopCharacterWithLerp();
|
||||
@@ -151,65 +188,80 @@ namespace Beyond
|
||||
}
|
||||
else if (IsAnimatorTag("Attack"))
|
||||
{
|
||||
StopCharacterWithLerp();
|
||||
if (lockRotation) StopCharacterWithLerp();
|
||||
transform.position = animator.rootPosition;
|
||||
}
|
||||
// commented for test
|
||||
//else if (inputSmooth.magnitude == 0 && isGrounded && !isSliding)
|
||||
//{
|
||||
// if (!ignoreAnimatorMovement)
|
||||
// {
|
||||
// animator.ApplyBuiltinRootMotion();
|
||||
// transform.position = animator.rootPosition;
|
||||
// transform.rotation = animator.rootRotation;
|
||||
// }
|
||||
//}
|
||||
|
||||
if (useRootMotion)
|
||||
{
|
||||
MoveCharacter(moveDirection);
|
||||
}
|
||||
if (useRootMotion) MoveCharacter(moveDirection);
|
||||
}
|
||||
|
||||
protected override void OnTriggerEnter(Collider other)
|
||||
{
|
||||
if (!m_ignoreTriggers)
|
||||
onActionEnter.Invoke(other);
|
||||
if (!m_ignoreTriggers) onActionEnter.Invoke(other);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Call this in OnTriggerEnter or OnTriggerStay to check if enter in triggerActions
|
||||
/// </summary>
|
||||
/// <param name="other">collider trigger</param>
|
||||
protected override void OnTriggerStay(Collider other)
|
||||
{
|
||||
try
|
||||
{
|
||||
CheckForAutoCrouch(other);
|
||||
}
|
||||
catch (UnityException e)
|
||||
{
|
||||
Debug.LogWarning(e.Message);
|
||||
}
|
||||
if (!m_ignoreTriggers)
|
||||
base.OnTriggerStay(other);
|
||||
try { CheckForAutoCrouch(other); }
|
||||
catch (UnityException e) { Debug.LogWarning(e.Message); }
|
||||
if (!m_ignoreTriggers) base.OnTriggerStay(other);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this in OnTriggerExit to check if exit of triggerActions
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
protected override void OnTriggerExit(Collider other)
|
||||
{
|
||||
AutoCrouchExit(other);
|
||||
if (!m_ignoreTriggers)
|
||||
base.OnTriggerExit(other);
|
||||
if (!m_ignoreTriggers) base.OnTriggerExit(other);
|
||||
}
|
||||
|
||||
//animation event handling
|
||||
void DrawWeaponLowLeft() { }
|
||||
|
||||
public override void ControlRotationType()
|
||||
{
|
||||
if (lockAnimRotation || lockRotation || customAction || isRolling) return;
|
||||
bool validInput = input != Vector3.zero || (isStrafing ? strafeSpeed.rotateWithCamera : freeSpeed.rotateWithCamera);
|
||||
if (validInput)
|
||||
{
|
||||
if (lockAnimMovement)
|
||||
{
|
||||
inputSmooth = Vector3.Lerp(inputSmooth, input, (isStrafing ? strafeSpeed.movementSmooth : freeSpeed.movementSmooth) * Time.deltaTime);
|
||||
}
|
||||
Vector3 dir = (isStrafing && isGrounded && (!isSprinting || sprintOnlyFree == false) || (freeSpeed.rotateWithCamera && input == Vector3.zero)) && rotateTarget ? rotateTarget.forward : moveDirection;
|
||||
if (isStrafing || !useAnimationBasedRotation) RotateToDirection(dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override void UpdateAnimatorParameters()
|
||||
{
|
||||
if (disableAnimations) return;
|
||||
animator.SetBool(vAnimatorParameters.IsStrafing, isStrafing);
|
||||
animator.SetBool(vAnimatorParameters.IsSprinting, isSprinting);
|
||||
animator.SetBool(vAnimatorParameters.IsSliding, isSliding && !isRolling);
|
||||
animator.SetBool(vAnimatorParameters.IsCrouching, isCrouching);
|
||||
animator.SetBool(vAnimatorParameters.IsGrounded, isGrounded);
|
||||
animator.SetBool(vAnimatorParameters.IsDead, isDead);
|
||||
animator.SetFloat(vAnimatorParameters.GroundDistance, groundDistance);
|
||||
animator.SetFloat(vAnimatorParameters.GroundAngle, GroundAngleFromDirection());
|
||||
if (!isGrounded) animator.SetFloat(vAnimatorParameters.VerticalVelocity, verticalVelocity);
|
||||
{
|
||||
if (isStrafing)
|
||||
{
|
||||
animator.SetFloat(vAnimatorParameters.InputHorizontal, horizontalSpeed, strafeSpeed.animationSmooth, Time.fixedDeltaTime);
|
||||
animator.SetFloat(vAnimatorParameters.InputVertical, verticalSpeed, strafeSpeed.animationSmooth, Time.fixedDeltaTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
animator.SetFloat(vAnimatorParameters.InputVertical, verticalSpeed, freeSpeed.animationSmooth, Time.fixedDeltaTime);
|
||||
animator.SetFloat(vAnimatorParameters.InputHorizontal, useAnimationBasedRotation ? horizontalSpeed : 0, freeSpeed.animationSmooth, Time.fixedDeltaTime);
|
||||
}
|
||||
animator.SetFloat(vAnimatorParameters.InputMagnitude, Mathf.LerpUnclamped(inputMagnitude, 0f, stopMoveWeight), isStrafing ? strafeSpeed.animationSmooth : freeSpeed.animationSmooth, Time.fixedDeltaTime);
|
||||
if (useLeanMovementAnim && inputMagnitude >= 0.1f)
|
||||
{
|
||||
animator.SetFloat(vAnimatorParameters.RotationMagnitude, rotationMagnitude, leanSmooth, Time.fixedDeltaTime);
|
||||
}
|
||||
else if (useTurnOnSpotAnim && inputMagnitude < 0.1f)
|
||||
{
|
||||
animator.SetFloat(vAnimatorParameters.RotationMagnitude, (float)System.Math.Round(rotationMagnitude, 2), rotationMagnitude == 0 ? 0.1f : 0.01f, Time.fixedDeltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user