chunk 1: core gameplay scripts scenes runtime assets

This commit is contained in:
2026-04-06 11:02:34 +03:00
parent fa0388bc79
commit 0d11a097d8
703 changed files with 2292651 additions and 0 deletions

View File

@@ -0,0 +1,588 @@
using System.Collections;
using System.Collections.Generic;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using TMPro;
public class CharacterMovement : MonoBehaviour
{
//jump system variables
private bool jump;
GroundCheck groundCheck;
public FallCheck fallCheck;
[SerializeField] private float jumpForce = 20f;
[SerializeField] private float gravity = -9.81f;
[SerializeField] private float gravityScale = 5f;
private float floatSpeed;
private float gvelocity;
//camera direction variables
private bool useCharacterForward = false;
private bool lockToCameraForward = false;
[SerializeField] private float turnSpeed = 10f;
public GameObject player;
public GameObject enemy;
public float distance;
private AnimatorClipInfo[] m_CurrentClipInfo;
private float m_CurrentClipLength;
private string m_ClipName;
public float nextAnimationTime;
public float animationInterval;
private float turnSpeedMultiplier;
private float direction = 0f;
private bool isSprinting = false;
public Animator anim;
private Vector3 targetDirection;
private Quaternion freeRotation;
private Camera mainCamera;
private float velocity;
// Cached reference: avoids FindFirstObjectByType every FixedUpdate (was 300 calls/sec)
private PlayerManagerNew _cachedPlayerManager;
private bool _playerManagerCached;
public bool block;
Vector2 input;
public PlayerScript playerScript;
// New modular input handler (used if PlayerScript is not present)
private CharacterInputHandler inputHandler;
private AnimatorClipInfo[] playerclipinfo;
private Vector3 playerDirection;
private float rotationSpeed;
[SerializeField] private float joystickSensitivity = 1f;
private Vector2 joystickInput;
float inputThreshold;
// Scale protection: some animation clips contain baked scale curves from FBX import
// that can shrink the character model. We save the original scale and restore it every frame.
private Vector3 originalScale;
[Header("Grounding")]
[Tooltip("Layers to raycast against for ground detection. Must exclude character layers to prevent floating.")]
[SerializeField] private LayerMask groundLayerMask = 0;
// Animation transition parameters
private float animationBlendTime = 0.15f; // Time to blend between animations
private float movementSmoothTime = 0.1f; // Time to smooth movement speed change
private float rotationSmoothTime = 0.1f; // Time to smooth rotation
private float currentMoveSpeed = 0f; // Current movement speed for smooth transitions
private float currentTurnSpeed = 0f; // Current turn speed for smooth transitions
[SerializeField] private float smoothTurnSpeed = 5f; // Lower = smoother, for realistic turning
// Walk/Run sound effects
[Header("Footstep Sound Effects")]
public AudioClip walkSoundEffect;
public AudioClip runSoundEffect;
private AudioSource footstepAudioSource;
private bool isFootstepPlaying = false;
private float lastSpeedParam = 0f;
//Link to Mission Handler Script
//Initialize the scripts
MissionHandler mission_handler;
GameStatsManager game_statsManager;
TextMeshProUGUI txt;
// Add reference to AnimationManager
private AnimationManager animationManager;
void OnEnable(){
GameObject gamestatsmanager_go = GameObject.Find("GameStatsManager");
if (gamestatsmanager_go != null) {
game_statsManager = gamestatsmanager_go.GetComponent<GameStatsManager>();
mission_handler = gamestatsmanager_go.GetComponent<MissionHandler>();
} else {
DevLog.LogWarning("GameStatsManager not found - mission tracking will be disabled");
}
//txt = gamestatsmanager_go.transform.Find("Canvas/move_txt").GetComponent<TextMeshProUGUI>();
if (mission_handler != null){
//DevLog.LogWarning("Character Movement Found");
}
}
// Start is called before the first frame update
void Start()
{
// Save original scale before anything can modify it
originalScale = transform.localScale;
jumpForce = 40;
gravity = -9.81f;
gravityScale = 5;
rotationSpeed = 2;
input = Vector2.zero;
anim = GetComponent<Animator>();
// Set animator transition parameters
if (anim != null)
{
foreach (var layer in new int[] { 0, 1, 2 }) // Check multiple layers if needed
{
if (layer < anim.layerCount)
{
for (int i = 0; i < anim.parameters.Length; i++)
{
// Adjust transition times in the Animator
AnimatorControllerParameter param = anim.parameters[i];
if (param.type == AnimatorControllerParameterType.Float && param.name == "TransitionTime")
{
anim.SetFloat(param.name, animationBlendTime);
}
}
}
}
}
mainCamera = Camera.main;
player = GameObject.FindGameObjectWithTag("Player");
enemy = GameObject.FindGameObjectWithTag("Enemy");
animationInterval = 3.0f;
block = false;
// Get PlayerScript from this character - this is essential for movement
// Try new modular CharacterInputHandler first, fall back to PlayerScript
inputHandler = GetComponent<CharacterInputHandler>();
playerScript = GetComponent<PlayerScript>();
if (inputHandler == null && playerScript == null)
{
DevLog.LogWarning($"[CharacterMovement] No CharacterInputHandler or PlayerScript found on {gameObject.name}. Movement will not work for player-controlled characters.");
}
else if (inputHandler != null)
{
DevLog.Log($"[CharacterMovement] Using new modular CharacterInputHandler on {gameObject.name}");
}
else
{
DevLog.Log($"[CharacterMovement] Using legacy PlayerScript on {gameObject.name}");
}
// Get or add required components with null checks
groundCheck = GetComponent<GroundCheck>();
if (groundCheck == null) {
groundCheck = gameObject.AddComponent<GroundCheck>();
DevLog.LogWarning("GroundCheck component not found, adding one automatically.");
}
fallCheck = GetComponent<FallCheck>();
if (fallCheck == null) {
fallCheck = gameObject.AddComponent<FallCheck>();
DevLog.LogWarning("FallCheck component not found, adding one automatically.");
}
inputThreshold = 0.3f;
// Get or add AnimationManager component
animationManager = GetComponent<AnimationManager>();
if (animationManager == null)
{
animationManager = gameObject.AddComponent<AnimationManager>();
}
// Setup footstep audio source
footstepAudioSource = GetComponent<AudioSource>();
if (footstepAudioSource == null)
{
footstepAudioSource = gameObject.AddComponent<AudioSource>();
}
footstepAudioSource.spatialBlend = 1f; // 3D sound
footstepAudioSource.loop = true;
footstepAudioSource.playOnAwake = false;
}
void FixedUpdate()
{
// Cached PlayerManagerNew lookup — avoids FindFirstObjectByType every FixedUpdate
if (!_playerManagerCached)
{
_cachedPlayerManager = GameObject.FindFirstObjectByType<PlayerManagerNew>();
_playerManagerCached = true;
}
if (_cachedPlayerManager != null && _cachedPlayerManager.pause)
return;
// Determine if movement should be blocked based on available input source
bool isBlocking = false;
bool isDodging = false;
bool hasInputSource = false;
if (inputHandler != null)
{
isBlocking = inputHandler.block;
isDodging = inputHandler.dodge;
hasInputSource = true;
}
else if (playerScript != null)
{
isBlocking = playerScript.block;
isDodging = playerScript.dodge;
hasInputSource = true;
}
// Always update footstep sounds based on animator state (for AI and all characters)
UpdateFootstepSoundsFromAnimator();
// No input source - don't process player movement
// This is fine for AI characters which use NavMeshAgent
if (!hasInputSource)
{
return;
}
// Block movement only during blocking or dodging
if (isBlocking || isDodging)
return;
gvelocity += gravity * gravityScale * Time.deltaTime;
if (groundCheck != null && groundCheck.isGrounded && gvelocity < 0)
{
//print("jump reset");
gvelocity = 0;
jump = false;
}
// Get input from available source (prefer new modular handler)
if (inputHandler != null) {
input = inputHandler.input;
} else if (playerScript != null) {
input = playerScript.input;
}
inputThreshold = 0.3f; // Adjust this value as needed
//ensure the character always stays upright
if (transform.rotation.z > 0.5 || transform.rotation.x > 0.5 || transform.rotation.z < -0.5 || transform.rotation.x < -0.5)
{
print("make player upright");
Quaternion rotation = Quaternion.Euler(0, transform.rotation.y, 0);
//transform.rotation = rotation;
// Dampen towards the target rotation
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime);
}
//ensure that the character is always grounded if they are not jumping
if (jump == false)
{
// Auto-configure ground layer mask if not set (exclude character layers)
if (groundLayerMask.value == 0)
{
groundLayerMask = ~0; // Start with everything
int playerLayer = LayerMask.NameToLayer("Player");
int enemyLayer = LayerMask.NameToLayer("Enemy");
int characterLayer = LayerMask.NameToLayer("Character");
int ignoreRaycast = LayerMask.NameToLayer("Ignore Raycast");
if (playerLayer >= 0) groundLayerMask &= ~(1 << playerLayer);
if (enemyLayer >= 0) groundLayerMask &= ~(1 << enemyLayer);
if (characterLayer >= 0) groundLayerMask &= ~(1 << characterLayer);
if (ignoreRaycast >= 0) groundLayerMask &= ~(1 << ignoreRaycast);
}
RaycastHit hit;
// CRITICAL: Use groundLayerMask to ignore character body colliders
// Without this, raycast hits character chest/head colliders and thinks "ground" is at chest height
if (Physics.Raycast(transform.position + Vector3.up * 0.5f, Vector3.down, out hit, 50f, groundLayerMask))
{
float groundY = hit.point.y;
if (transform.position.y > groundY + 0.15f)
{
// Instant snap to ground instead of slow 1f/frame drift
transform.position = new Vector3(transform.position.x, groundY + 0.05f, transform.position.z);
}
}
}
// Protect against animation clips overwriting scale
if (transform.localScale != originalScale)
{
transform.localScale = originalScale;
}
Move();
}
public virtual void UpdateTargetDirection()
{
if (playerScript != null) {
input = playerScript.input;
}
if (mainCamera == null)
mainCamera = Camera.main;
Vector3 forward = mainCamera.transform.forward;
Vector3 right = mainCamera.transform.right;
// Project onto XZ plane
forward.y = 0f;
right.y = 0f;
forward.Normalize();
right.Normalize();
// input.x = horizontal (A/D or left/right), input.y = vertical (W/S or up/down)
targetDirection = right * input.x + forward * input.y;
}
public void Move()
{
if (IsInHitReactionAnimation())//
return;
UpdateTargetDirection();
// Calculate raw input magnitude for animation
float inputMagnitude = input.magnitude;
// Define thresholds
float walkThreshold = 0.1f; // Minimum input to start walking
float runThreshold = 0.7f; // Input magnitude to start running
float speedParam = 0f;
if (inputMagnitude > walkThreshold && inputMagnitude < runThreshold)
{
// Walking
speedParam = 0.2f; // Match your blend tree's walk value
}
else if (inputMagnitude >= runThreshold)
{
// Running
speedParam = 1f;
}
else
{
// Idle
speedParam = 0f;
}
// Debug logging for movement diagnosis
if (inputMagnitude > 0.01f)
{
DevLog.Log($"[CharacterMovement] Input: ({input.x:F2}, {input.y:F2}), Magnitude: {inputMagnitude:F2}, Speed: {speedParam}");
}
anim.SetFloat("Speed", speedParam);
// --- Footstep Sound Logic ---
if (speedParam == 0f)
{
if (footstepAudioSource.isPlaying)
footstepAudioSource.Stop();
isFootstepPlaying = false;
}
else if (speedParam > 0f && !isFootstepPlaying)
{
if (speedParam < 0.7f && walkSoundEffect != null)
{
footstepAudioSource.clip = walkSoundEffect;
footstepAudioSource.pitch = 1f;
footstepAudioSource.Play();
isFootstepPlaying = true;
}
else if (speedParam >= 0.7f && runSoundEffect != null)
{
footstepAudioSource.clip = runSoundEffect;
footstepAudioSource.pitch = 1.1f;
footstepAudioSource.Play();
isFootstepPlaying = true;
}
}
else if (isFootstepPlaying)
{
// Switch between walk/run sound if needed
if (speedParam < 0.7f && footstepAudioSource.clip != walkSoundEffect && walkSoundEffect != null)
{
footstepAudioSource.clip = walkSoundEffect;
footstepAudioSource.pitch = 1f;
footstepAudioSource.Play();
}
else if (speedParam >= 0.7f && footstepAudioSource.clip != runSoundEffect && runSoundEffect != null)
{
footstepAudioSource.clip = runSoundEffect;
footstepAudioSource.pitch = 1.1f;
footstepAudioSource.Play();
}
}
lastSpeedParam = speedParam;
// --- End Footstep Sound Logic ---
// Only move and rotate if there's actual input
if (inputMagnitude > walkThreshold)
{
Vector3 moveAmount = targetDirection * 4f * Time.deltaTime;
transform.Translate(moveAmount, Space.World);
if (targetDirection != Vector3.zero)
{
Quaternion targetRotation = Quaternion.LookRotation(targetDirection, Vector3.up);
// Use a lower turn speed for smoother, more realistic turning
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
smoothTurnSpeed * Time.deltaTime
);
}
}
else if (inputMagnitude <= walkThreshold && speedParam == 0f)
{
anim.SetFloat("Speed", 0f);
}
}
private bool IsInHitReactionAnimation()
{
// Use AnimationManager to check if playing hit reaction animations
if (animationManager != null)
{
return animationManager.IsPlayingAnimation(
// Active animation reactions only
AnimationNames.JabReaction,
AnimationNames.HaymakerReaction,
AnimationNames.SpinningBackfistReaction,
AnimationNames.ElbowSmashReaction,
AnimationNames.ChargedPunchReaction,
AnimationNames.AirPunchReaction,
AnimationNames.DropKickReaction,
AnimationNames.LowKickReaction
/* Commented out other reactions for future use
// Original hit reactions
AnimationNames.RightHookHitReaction,
AnimationNames.BodyKickHitReaction,
AnimationNames.HookHitReaction,
AnimationNames.JabHitReaction,
AnimationNames.LegKickHitReaction,
AnimationNames.UppercutPowerHitReaction,
AnimationNames.OverhandHitReaction,
// Other new hit reactions
AnimationNames.ChopReaction,
AnimationNames.GroundPoundReaction,
AnimationNames.AirKickReaction,
AnimationNames.SignSwingReaction,
AnimationNames.SignOverheadSlamReaction,
AnimationNames.SignChargeAttackReaction
*/
);
}
// Fall back to original implementation if AnimationManager is not available
AnimatorClipInfo[] m_CurrentClipInfo;
m_CurrentClipInfo = gameObject.GetComponent<Animator>().GetCurrentAnimatorClipInfo(0);
if (m_CurrentClipInfo.Length == 0)
{
// No clips are currently playing
return false;
}
m_ClipName = m_CurrentClipInfo[0].clip.name;
// Check for active animation reactions only
return m_ClipName == AnimationNames.JabReaction ||
m_ClipName == AnimationNames.HaymakerReaction ||
m_ClipName == AnimationNames.SpinningBackfistReaction ||
m_ClipName == AnimationNames.ElbowSmashReaction ||
m_ClipName == AnimationNames.ChargedPunchReaction ||
m_ClipName == AnimationNames.AirPunchReaction ||
m_ClipName == AnimationNames.DropKickReaction ||
m_ClipName == AnimationNames.LowKickReaction;
/* Commented out other reactions for future use
m_ClipName == AnimationNames.RightHookHitReaction ||
m_ClipName == AnimationNames.BodyKickHitReaction ||
m_ClipName == AnimationNames.HookHitReaction ||
m_ClipName == AnimationNames.JabHitReaction ||
m_ClipName == AnimationNames.LegKickHitReaction ||
m_ClipName == AnimationNames.UppercutPowerHitReaction ||
m_ClipName == AnimationNames.OverhandHitReaction ||
// Other new hit reactions
m_ClipName == AnimationNames.ChopReaction ||
m_ClipName == AnimationNames.GroundPoundReaction ||
m_ClipName == AnimationNames.AirKickReaction ||
m_ClipName == AnimationNames.SignSwingReaction ||
m_ClipName == AnimationNames.SignOverheadSlamReaction ||
m_ClipName == AnimationNames.SignChargeAttackReaction;
*/
}
/// <summary>
/// Updates footstep sounds based on animator Speed parameter.
/// Called for all characters (including AI) to ensure footstep sounds play.
/// </summary>
private void UpdateFootstepSoundsFromAnimator()
{
if (anim == null || footstepAudioSource == null) return;
// Read current speed from animator
float speedParam = anim.GetFloat("Speed");
// Use a slight threshold to avoid noise
if (speedParam < 0.05f)
{
if (footstepAudioSource.isPlaying)
{
footstepAudioSource.Stop();
}
isFootstepPlaying = false;
}
else if (speedParam > 0.05f && !isFootstepPlaying)
{
// Start playing appropriate sound
if (speedParam < 0.7f && walkSoundEffect != null)
{
footstepAudioSource.clip = walkSoundEffect;
footstepAudioSource.pitch = 1f;
footstepAudioSource.Play();
isFootstepPlaying = true;
}
else if (speedParam >= 0.7f && runSoundEffect != null)
{
footstepAudioSource.clip = runSoundEffect;
footstepAudioSource.pitch = 1.1f;
footstepAudioSource.Play();
isFootstepPlaying = true;
}
}
else if (isFootstepPlaying)
{
// Switch between walk/run sound if needed
if (speedParam < 0.7f && footstepAudioSource.clip != walkSoundEffect && walkSoundEffect != null)
{
footstepAudioSource.clip = walkSoundEffect;
footstepAudioSource.pitch = 1f;
footstepAudioSource.Play();
}
else if (speedParam >= 0.7f && footstepAudioSource.clip != runSoundEffect && runSoundEffect != null)
{
footstepAudioSource.clip = runSoundEffect;
footstepAudioSource.pitch = 1.1f;
footstepAudioSource.Play();
}
}
lastSpeedParam = speedParam;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 97b9ca0aadc16ea45a696d486f7dd113
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e1a2f1e631e75afafbaed530d5b74123
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bd8ecc5a79f4d1129955dfdc8e112ac5
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 790aaee9a2520455683e6327971975e3
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c306275fdc26f41a1bf80a465060ef6e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: