integrated Audio system in player and enemies scripts; made ScriptableObjects for SFX Assets; modified AudioManager.cs by _sega to play random SFX from SFX Asset

This commit is contained in:
Данил Ярош 2025-10-24 16:54:12 +03:00
parent 4a750d9404
commit 09f10bd5be
84 changed files with 2613 additions and 394 deletions

View File

@ -5078,4 +5078,11 @@ AnimationClip:
flags: 16
m_HasGenericRootTransform: 0
m_HasMotionFloatCurves: 0
m_Events: []
m_Events:
- time: 0.05
functionName: Step
data:
objectReferenceParameter: {fileID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0

View File

@ -10356,6 +10356,13 @@ AnimationClip:
m_HasGenericRootTransform: 0
m_HasMotionFloatCurves: 0
m_Events:
- time: 0
functionName: OnDeathBegin
data:
objectReferenceParameter: {fileID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
- time: 1.25
functionName: OnDeathEnd
data:

View File

@ -8287,9 +8287,9 @@ AnimationClip:
m_HasMotionFloatCurves: 0
m_Events:
- time: 0
functionName: PlaySfx
functionName: Jump
data:
objectReferenceParameter: {fileID: 8300000, guid: fa0b6b9872cd9b84bb24e35b24e57fdf, type: 3}
objectReferenceParameter: {fileID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0

View File

@ -15829,23 +15829,23 @@ AnimationClip:
m_HasMotionFloatCurves: 0
m_Events:
- time: 0.083333336
functionName: PlaySfx
functionName: Step
data:
objectReferenceParameter: {fileID: 8300000, guid: 144cba17f4a47274d834d5e1fc28acb9, type: 3}
objectReferenceParameter: {fileID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
- time: 0.8833333
functionName: PlaySfx
functionName: Step
data:
objectReferenceParameter: {fileID: 8300000, guid: 144cba17f4a47274d834d5e1fc28acb9, type: 3}
objectReferenceParameter: {fileID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0
- time: 1.7
functionName: PlaySfx
functionName: Step
data:
objectReferenceParameter: {fileID: 8300000, guid: 144cba17f4a47274d834d5e1fc28acb9, type: 3}
objectReferenceParameter: {fileID: 0}
floatParameter: 0
intParameter: 0
messageOptions: 0

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1010,6 +1010,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
config: {fileID: 11400000, guid: d8fdc5ccf56a1ca4389e7927d0b86526, type: 2}
sfxAsset: {fileID: 11400000, guid: 9cb4dd9d30f2420439e056018daf622a, type: 2}
--- !u!114 &-3949889748814893039
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@ -1679,6 +1679,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
config: {fileID: 11400000, guid: d8fdc5ccf56a1ca4389e7927d0b86526, type: 2}
sfxAsset: {fileID: 11400000, guid: 9cb4dd9d30f2420439e056018daf622a, type: 2}
--- !u!114 &-7639911518411260797
MonoBehaviour:
m_ObjectHideFlags: 0

View File

@ -2835,6 +2835,9 @@ MonoBehaviour:
m_Bits: 64
animator: {fileID: 0}
maxHealth: 1
stepSounds: {fileID: 11400000, guid: 37fe07ddcac109a46a86ba44df16d551, type: 2}
hurtSounds: {fileID: 11400000, guid: 89e7f713913aecb40ada8c2f27c69303, type: 2}
jumpSounds: {fileID: 11400000, guid: 9a63566742cc2d84c89928c8b988e4eb, type: 2}
--- !u!114 &9068111837403598740
MonoBehaviour:
m_ObjectHideFlags: 0
@ -2868,6 +2871,7 @@ MonoBehaviour:
launchAxis: {x: 1, y: 0}
launchPoint: {fileID: 6550761234582043067}
attachPoint: {fileID: 6550761234593413718}
throwSounds: {fileID: 11400000, guid: 4b774d97282b77f45b08068d61682269, type: 2}
--- !u!114 &-953906007136484429
MonoBehaviour:
m_ObjectHideFlags: 0

8
Assets/SFX/Enemies.meta Normal file
View File

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

Binary file not shown.

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 4f6a3b9402e60e9469e3b2cf9dd01ff3
guid: 37f7526ec58db8843916100518133785
AudioImporter:
externalObjects: {}
serializedVersion: 8

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: f5084f4cf2fabc74fae40a5a9ebc2718
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: eb475b242c9b6ec448dfec33ec8da9f2
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: a0166fbc7106f42498be14d005a200f4
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: f59cd1f6b437f7d4f8fa63bad1e466a5
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: ce8d9dfcc94fcae46a52bd90ac0b0a19
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: a9399f94f3fa20f44b8d76c30deda71c
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

8
Assets/SFX/Props.meta Normal file
View File

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

Binary file not shown.

View File

@ -0,0 +1,23 @@
fileFormatVersion: 2
guid: caba1143532ae5642aa33fa950eb6269
AudioImporter:
externalObjects: {}
serializedVersion: 8
defaultSettings:
serializedVersion: 2
loadType: 0
sampleRateSetting: 0
sampleRateOverride: 44100
compressionFormat: 1
quality: 1
conversionMode: 0
preloadAudioData: 0
platformSettingOverrides: {}
forceToMono: 0
normalize: 1
loadInBackground: 0
ambisonic: 0
3D: 1
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1017,8 +1017,8 @@ MonoBehaviour:
m_TargetGraphic: {fileID: 434895586}
m_HandleRect: {fileID: 434895585}
m_Direction: 2
m_Value: 0
m_Size: 0.99999815
m_Value: 1
m_Size: 1
m_NumberOfSteps: 0
m_OnValueChanged:
m_PersistentCalls:
@ -4474,7 +4474,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0.0009856848}
m_AnchoredPosition: {x: 0, y: -0.000023633147}
m_SizeDelta: {x: 0, y: 300}
m_Pivot: {x: 0, y: 1}
--- !u!114 &1792218764
@ -4953,6 +4953,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
config: {fileID: 11400000, guid: 1db1421e9cc6f214f9b91a29eba61e76, type: 2}
sfxAsset: {fileID: 0}
--- !u!61 &1925001481
BoxCollider2D:
m_ObjectHideFlags: 0
@ -5025,7 +5026,7 @@ Rigidbody2D:
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 4
m_Constraints: 7
--- !u!95 &1925001483
Animator:
serializedVersion: 7
@ -10191,6 +10192,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
config: {fileID: 11400000, guid: 1db1421e9cc6f214f9b91a29eba61e76, type: 2}
sfxAsset: {fileID: 0}
--- !u!1 &1976298767889346999
GameObject:
m_ObjectHideFlags: 0
@ -10491,6 +10493,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
config: {fileID: 11400000, guid: 1db1421e9cc6f214f9b91a29eba61e76, type: 2}
sfxAsset: {fileID: 0}
--- !u!4 &2347305775932578288
Transform:
m_ObjectHideFlags: 0
@ -16911,7 +16914,7 @@ Rigidbody2D:
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 4
m_Constraints: 7
--- !u!4 &3947579830075241860
Transform:
m_ObjectHideFlags: 0
@ -24510,6 +24513,7 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
config: {fileID: 11400000, guid: 1db1421e9cc6f214f9b91a29eba61e76, type: 2}
sfxAsset: {fileID: 0}
--- !u!4 &4934570997528600992
Transform:
m_ObjectHideFlags: 0
@ -28867,7 +28871,7 @@ Rigidbody2D:
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 4
m_Constraints: 7
--- !u!483693784 &7700946729994996886
TilemapRenderer:
m_ObjectHideFlags: 0
@ -28998,7 +29002,7 @@ Rigidbody2D:
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 4
m_Constraints: 7
--- !u!4 &8062548743945812344
Transform:
m_ObjectHideFlags: 0
@ -31343,7 +31347,7 @@ Rigidbody2D:
m_Interpolate: 0
m_SleepingMode: 1
m_CollisionDetection: 0
m_Constraints: 4
m_Constraints: 7
--- !u!70 &8787207207727294666
CapsuleCollider2D:
m_ObjectHideFlags: 0

View File

@ -1,16 +1,35 @@
using System;
using UnityEngine;
using RR.Game2D.Behaviour;
using RR.Game2D.Behaviour.ScriptableObjects;
using RR.Game2D.Core.Managers;
public class EnemyAnimationEvents : MonoBehaviour
{
private SFXAsset _stepSounds;
private SFXAsset _hurtSounds;
private void Awake()
{
EnemyController controller = GetComponent<EnemyController>();
_stepSounds = controller.sfxAsset.stepSounds;
_hurtSounds = controller.sfxAsset.hurtSounds;
}
public void OnDeathBegin()
{
// Проигрывание SFX для получения урона
if (_hurtSounds && _hurtSounds.soundEffects.Length > 0)
GameManager.Instance.Audio.PlayRandomSfxAt(_hurtSounds.soundEffects, transform.position);
gameObject.layer = LayerMask.NameToLayer("No Collision");
Rigidbody2D rb = GetComponent<Rigidbody2D>();
Collider2D col = GetComponent<Collider2D>();
EnemyController controller = GetComponent<EnemyController>();
EnemyFireScript fireScript = GetComponent<EnemyFireScript>();
if (controller) controller.enabled = false;
if (fireScript) fireScript.enabled = false;
if (col) col.enabled = false;
if (!rb) return;
rb.linearVelocity = Vector2.zero;
@ -21,4 +40,11 @@ public class EnemyAnimationEvents : MonoBehaviour
{
Destroy(gameObject);
}
public void Step()
{
// Проигрывание SFX для шага
if (_stepSounds && _stepSounds.soundEffects.Length > 0)
GameManager.Instance.Audio.PlayRandomSfxAt(_stepSounds.soundEffects, transform.position);
}
}

View File

@ -1,21 +1,32 @@
using RR.Common.Managers;
using RR.Game2D.Behaviour;
using RR.Game2D.Behaviour.ScriptableObjects;
using RR.Game2D.Core.Managers;
using UnityEngine;
public class PlayerAnimationEvents : MonoBehaviour
{
[SerializeField] private AudioClip deathSound;
private PlayerController _controller;
private SFXAsset _stepSounds;
private SFXAsset _hurtSounds;
private SFXAsset _jumpSounds;
private Rigidbody2D _rb;
private void Awake()
{
_controller = GetComponent<PlayerController>();
PlayerController controller = GetComponent<PlayerController>();
_stepSounds = controller.stepSounds;
_hurtSounds = controller.hurtSounds;
_jumpSounds = controller.jumpSounds;
_rb = GetComponent<Rigidbody2D>();
}
public void OnDeathBegin()
{
if (_hurtSounds && _hurtSounds.soundEffects.Length > 0)
GameManager.Instance.Audio.PlayRandomSfxAt(_hurtSounds.soundEffects, transform.position);
}
public void OnDeathEnd()
{
Animator animator = GetComponent<Animator>();
@ -25,10 +36,15 @@ public class PlayerAnimationEvents : MonoBehaviour
GameManager.Instance.RespawnPlayer();
}
public void PlaySfx(AudioClip sound)
public void Step()
{
// Debug.Log($"StepSound: {stepSound}");
if (sound != null) GameManager.Instance.Audio.PlaySfxAt(sound, transform.position, 0.5f);
// AudioManager.Instance.PlaySfxAt(stepSound, transform.position);
if (_stepSounds && _stepSounds.soundEffects.Length > 0)
GameManager.Instance.Audio.PlayRandomSfxAt(_stepSounds.soundEffects, transform.position);
}
public void Jump()
{
if (_jumpSounds && _jumpSounds.soundEffects.Length > 0)
GameManager.Instance.Audio.PlayRandomSfxAt(_jumpSounds.soundEffects, transform.position);
}
}

View File

@ -1,7 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Audio;
using Random = UnityEngine.Random;
namespace RR.Common.Managers
{
@ -233,6 +235,22 @@ namespace RR.Common.Managers
ConfigureAndPlaySfx(src, clip, volume, pitch, Mathf.Clamp01(spatialBlend), position);
}
public void PlayRandomSfx(SoundEffect[] sfxAsset)
{
if (sfxAsset.Length == 0) return;
SoundEffect chosenEffect = sfxAsset[Random.Range(0, sfxAsset.Length - 1)];
PlaySfx(chosenEffect.sound, chosenEffect.volume, chosenEffect.pitch);
}
public void PlayRandomSfxAt(SoundEffect[] sfxAsset, Vector3 position)
{
if (sfxAsset.Length == 0) return;
SoundEffect chosenEffect = sfxAsset[Random.Range(0, sfxAsset.Length - 1)];
PlaySfxAt(chosenEffect.sound, position, chosenEffect.volume, chosenEffect.pitch);
}
// ==== Internal helpers ====
private void StopCrossfadeAndFade()
@ -340,6 +358,15 @@ namespace RR.Common.Managers
src.Play();
}
#endregion
}
[Serializable]
public struct SoundEffect
{
public AudioClip sound;
[Min(0f)]public float volume;
public float pitch;
}
}

View File

@ -9,24 +9,24 @@ public class CameraControlTrigger : MonoBehaviour
{
public CustomInspectorObjects customInspectorObjects;
private Collider2D col;
private bool cameraPanned = false; // Флаг для отслеживания состояния
private Collider2D _col;
private bool _cameraPanned = false; // Флаг для отслеживания состояния
private void Start()
{
col = GetComponent<Collider2D>();
_col = GetComponent<Collider2D>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.CompareTag("Player"))
{
if (customInspectorObjects.panCameraOnContact && !cameraPanned)
if (customInspectorObjects.panCameraOnContact && !_cameraPanned)
{
if (CameraManager.instance != null)
{
CameraManager.instance.PanCameraOnContact(customInspectorObjects.panDistance, customInspectorObjects.panTime, customInspectorObjects.panDirection, false);
cameraPanned = true;
_cameraPanned = true;
}
}
}
@ -36,12 +36,12 @@ public class CameraControlTrigger : MonoBehaviour
{
if (collision.CompareTag("Player"))
{
if (customInspectorObjects.panCameraOnContact && cameraPanned)
if (customInspectorObjects.panCameraOnContact && _cameraPanned)
{
if (CameraManager.instance != null)
{
CameraManager.instance.PanCameraOnContact(customInspectorObjects.panDistance, customInspectorObjects.panTime, customInspectorObjects.panDirection, true);
cameraPanned = false;
_cameraPanned = false;
}
}
}

View File

@ -13,32 +13,32 @@ namespace RR.Game2D.Core.Managers
[Header("Behaviour")]
[SerializeField] protected bool snapOnFound = true;
protected Transform player;
protected Transform _player;
//private float lastX;
protected Coroutine bindRoutine;
protected Coroutine _bindRoutine;
private void OnEnable()
{
offset = new Vector2(Mathf.Abs(offset.x), offset.y);
bindRoutine = StartCoroutine(BindLoop());
_bindRoutine = StartCoroutine(BindLoop());
}
private void OnDisable()
{
if (bindRoutine != null) { StopCoroutine(bindRoutine); bindRoutine = null; }
if (_bindRoutine != null) { StopCoroutine(_bindRoutine); _bindRoutine = null; }
}
private void FixedUpdate()
{
if (!player) return; // ждём появления
if (!_player) return; // ждём появления
float currentX = player.position.x;
float currentX = _player.position.x;
//if (currentX > lastX + 0.01f) faceLeft = false;
//else if (currentX < lastX - 0.01f) faceLeft = true;
faceLeft = Mathf.Abs(player.localEulerAngles.y) >= 180 ? true : false;
faceLeft = Mathf.Abs(_player.localEulerAngles.y) >= 180 ? true : false;
//lastX = currentX;
Vector3 target = player.position;
Vector3 target = _player.position;
target.x += faceLeft ? -offset.x : +offset.x;
target.y += offset.y;
target.z = transform.position.z;
@ -53,12 +53,12 @@ namespace RR.Game2D.Core.Managers
{
while (true)
{
if (!player)
if (!_player)
{
var t = ResolvePlayer();
if (t)
{
player = t;
_player = t;
//lastX = player.position.x;
if (snapOnFound) SnapToPlayer(faceLeft);
}
@ -89,8 +89,8 @@ namespace RR.Game2D.Core.Managers
public virtual void FindPlayer(bool playerFaceLeft)
{
faceLeft = playerFaceLeft;
player = ResolvePlayer();
if (!player)
_player = ResolvePlayer();
if (!_player)
{
Debug.Log("CameraFollow2D: player not found yet, will wait.");
return;
@ -105,8 +105,8 @@ namespace RR.Game2D.Core.Managers
/// <param name="playerFaceLeft">Повернут ли игрок налево</param>
private void SnapToPlayer(bool playerFaceLeft)
{
if (!player) return;
Vector3 p = player.position;
if (!_player) return;
Vector3 p = _player.position;
p.x += (playerFaceLeft ? -offset.x : offset.x);
p.y += offset.y;
p.z = transform.position.z;

View File

@ -4,42 +4,29 @@ using UnityEngine;
public class CinemachineFindPlayer : MonoBehaviour
{
//[Header("Параметры смещения")]
//[SerializeField] float interpolationTime = 10f;
private GameObject tPlayer;
private Transform tFollowTarget;
private CinemachineCamera vcam;
//private CinemachineFollow followScript;
//private bool playerFacedLeft = false;
private GameObject _tPlayer;
private Transform _tFollowTarget;
private CinemachineCamera _virtualCamera;
void Start()
{
vcam = GetComponent<CinemachineCamera>();
//followScript = GetComponent<CinemachineFollow>();
_virtualCamera = GetComponent<CinemachineCamera>();
if (tPlayer == null)
{
tPlayer = GameManager.Instance.PlayerObj;
if (tPlayer == null) tPlayer = GameObject.FindWithTag("Player");
}
if (_tPlayer != null) return;
_tPlayer = GameManager.Instance.PlayerObj;
if (_tPlayer == null) _tPlayer = GameObject.FindWithTag("Player");
}
void FixedUpdate()
{
if (tPlayer != null)
{
tFollowTarget = tPlayer.transform;
vcam.LookAt = tFollowTarget;
vcam.Follow = tFollowTarget;
//playerFacedLeft = Mathf.Abs(tPlayer.transform.localEulerAngles.y) >= 180f;
////Debug.Log($"Faced left: {playerFacedLeft}");
//Vector3 cameraOffset = followScript.FollowOffset;
//cameraOffset.x = playerFacedLeft ? -offset.x : offset.x;
//followScript.FollowOffset = Vector3.Lerp(followScript.FollowOffset, cameraOffset, Time.deltaTime);
}
if (_tPlayer == null) return;
_tFollowTarget = _tPlayer.transform;
_virtualCamera.LookAt = _tFollowTarget;
_virtualCamera.Follow = _tFollowTarget;
}
}

View File

@ -22,7 +22,7 @@ namespace RR.Game2D.Entities
// Текущий активный чекпоинт (read-only)
public Checkpoint Active => _active;
string PrefKey => $"CP_{SceneManager.GetActiveScene().name}";
private string PrefKey => $"CP_{SceneManager.GetActiveScene().name}";
void Awake()
{

View File

@ -11,6 +11,7 @@ namespace RR.Game2D.Behaviour
typeof(Animator))]
public class EnemyController : MonoBehaviour, IDamageable
{
[SerializeField] public EnemySFXAsset sfxAsset;
[SerializeField] private EnemyControllerConfiguration config;
private Health health;
@ -18,25 +19,25 @@ namespace RR.Game2D.Behaviour
[HideInInspector] public float MaxHealth => health?.MaxHealth ?? 0;
private Animator animator;
private Rigidbody2D rb;
private Collider2D col;
private Animator _animator;
private Rigidbody2D _rb;
private Collider2D _col;
private GameObject playerObj;
private GameObject _playerObj;
// Аниматор
private static readonly int Speed = Animator.StringToHash("Speed");
void Start()
{
rb = GetComponent<Rigidbody2D>();
col = GetComponent<Collider2D>();
animator = GetComponent<Animator>();
_rb = GetComponent<Rigidbody2D>();
_col = GetComponent<Collider2D>();
_animator = GetComponent<Animator>();
playerObj = GameManager.Instance.PlayerObj;
if (playerObj == null)
_playerObj = GameManager.Instance.PlayerObj;
if (_playerObj == null)
{
playerObj = GameObject.FindGameObjectWithTag("Player");
_playerObj = GameObject.FindGameObjectWithTag("Player");
}
health = GetComponent<Health>();
@ -51,10 +52,10 @@ namespace RR.Game2D.Behaviour
void FixedUpdate()
{
if (!playerObj) return;
if (!_playerObj) return;
// if (rb.linearVelocity.x > 0.0001f) Debug.Log(rb.linearVelocity.x);
Vector3 playerCenter = playerObj.transform.position;
Vector3 playerCenter = _playerObj.transform.position;
if (PlayerInSight())
{
@ -63,7 +64,7 @@ namespace RR.Game2D.Behaviour
if (dist > 0)
{
rb.linearVelocity = new Vector2(config.moveSpeed * toPlayer.normalized.x, 0);
_rb.linearVelocity = new Vector2(config.moveSpeed * toPlayer.normalized.x, 0);
}
// if (dist > 0)
// rb.linearVelocity = toPlayer / dist * moveSpeed;
@ -72,11 +73,11 @@ namespace RR.Game2D.Behaviour
}
else
{
rb.linearVelocity = Vector2.MoveTowards(
rb.linearVelocity, Vector2.zero, config.acceleration * Time.fixedDeltaTime);
_rb.linearVelocity = Vector2.MoveTowards(
_rb.linearVelocity, Vector2.zero, config.acceleration * Time.fixedDeltaTime);
}
animator.SetFloat(Speed, Mathf.Abs(rb.linearVelocity.magnitude));
_animator.SetFloat(Speed, Mathf.Abs(_rb.linearVelocity.magnitude));
}
void OnCollisionEnter2D(Collision2D collision)
@ -84,7 +85,7 @@ namespace RR.Game2D.Behaviour
if (collision.collider.CompareTag("Player"))
{
Rigidbody2D playerRb = collision.collider.GetComponent<Rigidbody2D>();
playerObj.TryGetComponent(out PlayerController playerController);
_playerObj.TryGetComponent(out PlayerController playerController);
if (playerRb == null) return;
Vector2 contactNormal = collision.GetContact(0).normal;
if (contactNormal.y < -0.5f)
@ -102,13 +103,13 @@ namespace RR.Game2D.Behaviour
private bool PlayerInSight()
{
if (!playerObj) return false;
if (!_playerObj) return false;
Vector3 playerPos = playerObj.transform.position;
Collider2D playerCol = playerObj.GetComponent<Collider2D>();
Vector3 playerPos = _playerObj.transform.position;
Collider2D playerCol = _playerObj.GetComponent<Collider2D>();
playerPos.y = playerCol.bounds.center.y;
Vector2 eyePos = new(rb.position.x, col.bounds.center.y);
Vector2 eyePos = new(_rb.position.x, _col.bounds.center.y);
Vector2 toPlayer = (Vector2)playerPos - eyePos;
float dist = toPlayer.magnitude;
@ -122,7 +123,7 @@ namespace RR.Game2D.Behaviour
int mask = config.obstacleMask;
mask &= ~(1 << gameObject.layer);
mask &= ~(1 << playerObj.layer);
mask &= ~(1 << _playerObj.layer);
Vector2 origin = eyePos + forward.normalized * 0.05f;
@ -142,16 +143,16 @@ namespace RR.Game2D.Behaviour
}
public void Die()
{
animator.Play("Death");
_animator.Play("Death");
// Destroy(gameObject);
}
#if UNITY_EDITOR
private void OnDrawGizmos()
{
if (!playerObj) return;
if (!_playerObj) return;
Vector2 origin = new(rb.position.x, col.bounds.center.y);
Vector2 origin = new(_rb.position.x, _col.bounds.center.y);
Vector2 forward = transform.localEulerAngles.y >= 180f ? Vector2.right : Vector2.left;
float half = config.viewAngle * 0.5f;
@ -163,11 +164,11 @@ namespace RR.Game2D.Behaviour
Gizmos.DrawLine(origin, origin + edgeB * config.viewDistance);
Gizmos.DrawWireSphere(origin, config.viewDistance);
Vector2 toPlayer = (Vector2)playerObj.transform.position - origin;
Vector2 toPlayer = (Vector2)_playerObj.transform.position - origin;
if (toPlayer.magnitude <= config.viewDistance)
{
Gizmos.color = Color.red;
Gizmos.DrawLine(transform.position, playerObj.transform.position);
Gizmos.DrawLine(transform.position, _playerObj.transform.position);
}
}
#endif

View File

@ -2,6 +2,7 @@
using UnityEngine.InputSystem;
using RR.Common.Interfaces;
using RR.Game2D.Behaviour.ScriptableObjects;
using RR.Game2D.Core.Managers;
namespace RR.Game2D.Behaviour
@ -37,15 +38,20 @@ namespace RR.Game2D.Behaviour
[Header("Здоровье")]
[SerializeField] private int maxHealth = 3;
private Health health;
public float CurrentHealth => health?.CurrentHealth ?? 0;
public float MaxHealth => health?.MaxHealth ?? 0;
private Health _health;
public float CurrentHealth => _health?.CurrentHealth ?? 0;
public float MaxHealth => _health?.MaxHealth ?? 0;
[Header("FX")]
[SerializeField] public SFXAsset stepSounds;
[SerializeField] public SFXAsset hurtSounds;
[SerializeField] public SFXAsset jumpSounds;
private Rigidbody2D rb;
// private Collider2D col;
private float inputX;
private bool jumpPressed, jumpReleased, jumpHold;
private float _inputX;
private bool _jumpPressed, _jumpReleased, _jumpHold;
private float _fallSpeedYDampingChangeThreshold;
@ -84,28 +90,28 @@ namespace RR.Game2D.Behaviour
if (animator == null)
Debug.LogError($"[{name}] Animator не найден!");
health = GetComponent<Health>();
if (health == null)
_health = GetComponent<Health>();
if (_health == null)
{
health = gameObject.AddComponent<Health>();
_health = gameObject.AddComponent<Health>();
}
health.ResetHealth();
health.SetOwner(this);
health.SetMaxHealth(maxHealth);
_health.ResetHealth();
_health.SetOwner(this);
_health.SetMaxHealth(maxHealth);
}
void Update()
{
// --- Ввод ---
Vector2 move = moveAction ? moveAction.action.ReadValue<Vector2>() : Vector2.zero;
inputX = Mathf.Abs(move.x) > 0.01f ? Mathf.Sign(move.x) : 0f;
_inputX = Mathf.Abs(move.x) > 0.01f ? Mathf.Sign(move.x) : 0f;
jumpPressed |= jumpAction.action.WasPressedThisFrame();
jumpReleased |= jumpAction.action.WasReleasedThisFrame();
jumpHold |= jumpAction.action.IsPressed();
_jumpPressed |= jumpAction.action.WasPressedThisFrame();
_jumpReleased |= jumpAction.action.WasReleasedThisFrame();
_jumpHold |= jumpAction.action.IsPressed();
if (inputX != 0f)
transform.localEulerAngles = Mathf.Sign(inputX) > 0 ? Vector3.zero : new Vector3(0, 180f, 0);
if (_inputX != 0f)
transform.localEulerAngles = Mathf.Sign(_inputX) > 0 ? Vector3.zero : new Vector3(0, 180f, 0);
if (rb.linearVelocity.y < _fallSpeedYDampingChangeThreshold && !CameraManager.instance.IsLerpingYDamping && !CameraManager.instance.LerpedFromPlayerFalling)
{
@ -127,7 +133,7 @@ namespace RR.Game2D.Behaviour
private void FixedUpdate()
{
// Горизонтальное движение
float target = inputX * moveSpeed;
float target = _inputX * moveSpeed;
float rate = (Mathf.Abs(target) > 0.01) ? accel : decel;
float velocityX = Mathf.MoveTowards(rb.linearVelocity.x, target, rate * Time.fixedDeltaTime);
@ -135,15 +141,15 @@ namespace RR.Game2D.Behaviour
newVelocity.x = velocityX;
// Вертикальное движение
if ((jumpPressed || jumpHold)&& IsGrounded)
if ((_jumpPressed || _jumpHold)&& IsGrounded)
newVelocity.y = jumpVelocity;
if (jumpReleased && newVelocity.y > 0)
if (_jumpReleased && newVelocity.y > 0)
newVelocity.y /= Mathf.Max(1f, jumpCut);
rb.linearVelocity = newVelocity;
jumpPressed = jumpReleased = jumpHold = false;
_jumpPressed = _jumpReleased = _jumpHold = false;
// Смерть
// if (transform.position.y < deathY) Die();
@ -170,11 +176,11 @@ namespace RR.Game2D.Behaviour
public void TakeDamage(float amount)
{
health.TakeDamage(amount);
_health.TakeDamage(amount);
}
public void Heal(float amount)
{
health.Heal(amount);
_health.Heal(amount);
}
public void Die()

View File

@ -8,14 +8,14 @@ namespace RR.Game2D.Behaviour
{
[SerializeField] private float maxHealth = 3;
[SerializeField] private float currentHealth;
private IDamageable owner;
private IDamageable _owner;
public float MaxHealth => maxHealth;
public float CurrentHealth => currentHealth;
public void SetOwner(IDamageable healthOwner)
{
owner = healthOwner;
_owner = healthOwner;
}
public void SetMaxHealth(float value)
@ -29,7 +29,7 @@ namespace RR.Game2D.Behaviour
currentHealth -= amount;
if (currentHealth <= 0)
{
owner?.Die();
_owner?.Die();
}
}

View File

@ -7,16 +7,16 @@ namespace RR.Game2D.Behaviour
public class JumpPad2D : MonoBehaviour
{
[SerializeField] private float verticalBand = 0.5f;
private BoxCollider2D col;
private BoxCollider2D _col;
public Action<GameObject> PlayerJumped;
private void Awake()
{
col = GetComponent<BoxCollider2D>();
_col = GetComponent<BoxCollider2D>();
if (!col.isTrigger)
col.isTrigger = true;
if (!_col.isTrigger)
_col.isTrigger = true;
}
//private void OnTriggerEnter2D(Collider2D other)
@ -60,7 +60,7 @@ namespace RR.Game2D.Behaviour
PlayerController playerController = other.GetComponent<PlayerController>();
Rigidbody2D playerRB = other.GetComponent<Rigidbody2D>();
if (Mathf.Abs(other.transform.position.y - col.bounds.max.y) <= verticalBand)
if (Mathf.Abs(other.transform.position.y - _col.bounds.max.y) <= verticalBand)
{
Vector2 newPlayerVelocity = playerRB.linearVelocity;
newPlayerVelocity.y = playerController.jumpVelocity;

View File

@ -29,13 +29,13 @@ public class LightDimmer : MonoBehaviour
[SerializeField] [Min(0)] private float startBloomIntensity = 0f;
[SerializeField] [Min(0)] private float targetBloomIntensity = 1f;
private Transform playerTransform;
private Transform _playerTransform;
// Храним все коллайдеры игрока, которые в данный момент пересекают триггер
private readonly HashSet<Collider2D> playerColliders = new HashSet<Collider2D>();
private readonly HashSet<Collider2D> _playerColliders = new HashSet<Collider2D>();
private float boundsMin;
private float boundsMax;
private float _boundsMin;
private float _boundsMax;
private Volume[] _volumes;
/// <summary>
@ -75,7 +75,7 @@ public class LightDimmer : MonoBehaviour
// Пытаемся найти игрока на сцене для удобства — это позволит скрипту работать, даже если playerTransform не задан вручную.
var playerObj = GameObject.FindGameObjectWithTag(playerTag);
if (playerObj != null)
playerTransform = playerObj.transform;
_playerTransform = playerObj.transform;
// Кэшируем Volume (если есть) для каждого источника света. Volume может находиться на родителе или выше.
for (int i = 0; i < lights.Length; i++)
@ -112,7 +112,7 @@ public class LightDimmer : MonoBehaviour
UpdateBounds();
if (playerTransform == null) playerTransform = GameManager.Instance.PlayerObj.transform;
if (_playerTransform == null) _playerTransform = GameManager.Instance.PlayerObj.transform;
}
/// <summary>
@ -122,18 +122,18 @@ public class LightDimmer : MonoBehaviour
private void Update()
{
// Требуем наличие: playerTransform, массив lights и targetCollider
if (!playerTransform || lights == null || lights.Length == 0 || targetCollider == null)
if (!_playerTransform || lights == null || lights.Length == 0 || targetCollider == null)
return;
// Debug.Log($"{name}: Player Transform Detected");
// Проверяем, что игрок действительно внутри триггера, учитываем все коллайдеры (на случай CompositeCollider/мультиколлайдеров)
bool playerInside = playerColliders.Count > 0;
bool playerInside = _playerColliders.Count > 0;
if (!playerInside)
return;
// Debug.Log($"{name}: Player Inside Trigger");
Vector3 playerPos = playerTransform.position;
Vector3 playerPos = _playerTransform.position;
// Дополнительная проверка: AABB bounds коллайдера (быстрая фильтрация), чтобы избежать лишних вычислений
// if (!targetCollider.bounds.Contains(playerPos))
@ -148,10 +148,10 @@ public class LightDimmer : MonoBehaviour
float t = 0f;
// Если boundsMin==boundsMax — делить нельзя, поэтому проверяем приблизительное равенство
if (!Mathf.Approximately(boundsMax, boundsMin))
if (!Mathf.Approximately(_boundsMax, _boundsMin))
{
// Нормализуем позицию игрока по отрезку [boundsMin, boundsMax]
t = Mathf.InverseLerp(boundsMin, boundsMax, playerCurrent);
t = Mathf.InverseLerp(_boundsMin, _boundsMax, playerCurrent);
t = Mathf.Clamp01(t);
}
@ -207,11 +207,11 @@ public class LightDimmer : MonoBehaviour
if (!other.CompareTag(playerTag)) return;
// Запоминаем коллайдер игрока
playerColliders.Add(other);
_playerColliders.Add(other);
// Сохраняем Transform игрока (на случай, если раньше не найден)
if (playerTransform == null)
playerTransform = other.transform;
if (_playerTransform == null)
_playerTransform = other.transform;
}
/// <summary>
@ -223,7 +223,7 @@ public class LightDimmer : MonoBehaviour
if (!other.CompareTag(playerTag)) return;
// Убираем коллайдер из множества; если множество пустое — игрок полностью вышел
playerColliders.Remove(other);
_playerColliders.Remove(other);
}
/// <summary>
@ -233,13 +233,13 @@ public class LightDimmer : MonoBehaviour
private void UpdateBounds()
{
var b = targetCollider.bounds;
boundsMin = b.min.x;
boundsMax = b.max.x;
_boundsMin = b.min.x;
_boundsMax = b.max.x;
if (useYProgress)
{
boundsMin = b.min.y;
boundsMax = b.max.y;
_boundsMin = b.min.y;
_boundsMax = b.max.y;
}
}
@ -257,10 +257,10 @@ public class LightDimmer : MonoBehaviour
Gizmos.DrawLine(new Vector3(b.max.x, b.max.y, 0f), new Vector3(b.min.x, b.max.y, 0f));
Gizmos.DrawLine(new Vector3(b.min.x, b.max.y, 0f), new Vector3(b.min.x, b.min.y, 0f));
if (playerTransform != null)
if (_playerTransform != null)
{
Gizmos.color = Color.cyan;
Vector3 p = playerTransform.position;
Vector3 p = _playerTransform.position;
Gizmos.DrawSphere(new Vector3(p.x, p.y, 0f), 0.05f);
}
}

View File

@ -7,23 +7,23 @@ namespace RR.Game2D.Core.Managers
[SerializeField][Range(-1f, 3f)] private float parallaxFactorX = 0.5f;
[SerializeField][Range(-1f, 3f)] private float parallaxFactorY = 0.5f;
[SerializeField] private bool lockYAxis = false;
private Transform cameraTransform;
private Vector3 previousCameraPosition;
private Transform _cameraTransform;
private Vector3 _previousCameraPosition;
void Start()
{
if (Camera.main != null) cameraTransform = Camera.main.transform;
previousCameraPosition = cameraTransform.position;
if (Camera.main != null) _cameraTransform = Camera.main.transform;
_previousCameraPosition = _cameraTransform.position;
}
void FixedUpdate()
{
Vector3 delta = cameraTransform.position - previousCameraPosition;
Vector3 delta = _cameraTransform.position - _previousCameraPosition;
if (lockYAxis)
transform.position += new Vector3(delta.x * parallaxFactorX, 0, 0);
else
transform.position += new Vector3(delta.x * parallaxFactorX, delta.y * parallaxFactorY, 0);
previousCameraPosition = cameraTransform.position;
_previousCameraPosition = _cameraTransform.position;
}
}
}

View File

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

View File

@ -1,6 +1,7 @@
using UnityEngine;
using RR.Game2D.Entities.Projectiles;
using RR.Game2D.Behaviour.ScriptableObjects;
using RR.Game2D.Core.Managers;
namespace RR.Game2D.Behaviour
{
@ -9,7 +10,7 @@ namespace RR.Game2D.Behaviour
[SerializeField] private EnemyFireConfiguration config;
private float lastThrowTime = -999f;
private EnemyBasicProjectile projectile;
private EnemyBasicProjectile _projectile;
void Start()
{
@ -35,7 +36,7 @@ namespace RR.Game2D.Behaviour
private void OnDestroy()
{
// При смерти врага сго снаряд тоже пропадает
if (projectile != null) projectile.Despawn();
if (_projectile != null) _projectile.Despawn();
}
/// <summary>
@ -49,18 +50,21 @@ namespace RR.Game2D.Behaviour
Collider2D enemyCol = GetComponent<Collider2D>();
Vector2 throwPosition = new(1.4f * throwDirection.x + transform.position.x, enemyCol.bounds.center.y);
projectile = Instantiate(config.projectilePrefab, throwPosition, Quaternion.identity);
projectile.owner = gameObject;
projectile.maxLifetime = config.projectileLifetime;
projectile.despawnInstantly = config.projectileDespawnInstantly;
projectile.fallGravityScale = config.projectileFallGravityScale;
_projectile = Instantiate(config.projectilePrefab, throwPosition, Quaternion.identity);
_projectile.owner = gameObject;
_projectile.maxLifetime = config.projectileLifetime;
_projectile.despawnInstantly = config.projectileDespawnInstantly;
_projectile.fallGravityScale = config.projectileFallGravityScale;
Collider2D projectileCol = projectile.GetComponent<Collider2D>();
Collider2D projectileCol = _projectile.GetComponent<Collider2D>();
projectile.transform.localRotation = throwDirection.x < 0 ? Quaternion.Inverse(projectile.transform.localRotation) : projectile.transform.localRotation;
_projectile.transform.localRotation = throwDirection.x < 0 ? Quaternion.Inverse(_projectile.transform.localRotation) : _projectile.transform.localRotation;
projectile.transform.position = throwPosition;
projectile.Launch(throwDirection, config.throwSpeed);
_projectile.transform.position = throwPosition;
_projectile.Launch(throwDirection, config.throwSpeed);
if (config.shotSounds.Length > 0)
GameManager.Instance.Audio.PlayRandomSfxAt(config.shotSounds, throwPosition);
if (enemyCol && projectileCol) Physics2D.IgnoreCollision(enemyCol, projectileCol, true);

View File

@ -1,4 +1,6 @@
using UnityEngine;
using RR.Game2D.Behaviour.ScriptableObjects;
using RR.Game2D.Core.Managers;
using UnityEngine;
using UnityEngine.InputSystem;
using RR.Game2D.Entities.Projectiles;
@ -20,14 +22,17 @@ namespace RR.Game2D.Behaviour
[SerializeField] private Transform launchPoint;
[SerializeField] public Transform attachPoint;
private PickableProjectile pickableProjectile;
private Collider2D col;
[Header("FX")]
[SerializeField] private SFXAsset throwSounds;
private PickableProjectile _pickableProjectile;
private Collider2D _col;
void Awake()
{
if (!attachPoint)
Debug.LogWarning("PlayerFireScript: attachPoint не задан. Будет использован корень игрока.");
col = GetComponent<Collider2D>();
_col = GetComponent<Collider2D>();
}
void Update()
@ -35,10 +40,10 @@ namespace RR.Game2D.Behaviour
//if (!Input.GetButtonDown("Fire1")) return;
if (!fireButton.action.WasPressedThisFrame()) return;
if (pickableProjectile == null)
pickableProjectile = PickableProjectile.Instance;
if (_pickableProjectile == null)
_pickableProjectile = PickableProjectile.Instance;
if (pickableProjectile == null)
if (_pickableProjectile == null)
{
if (!spawnIfMissing || projectilePrefab == null)
{
@ -47,12 +52,12 @@ namespace RR.Game2D.Behaviour
}
Vector3 spawnPos = attachPoint ? attachPoint.position : transform.position;
pickableProjectile = Instantiate(projectilePrefab, spawnPos, Quaternion.identity);
pickableProjectile.TryPickup(gameObject, attachPoint);
_pickableProjectile = Instantiate(projectilePrefab, spawnPos, Quaternion.identity);
_pickableProjectile.TryPickup(gameObject, attachPoint);
return;
}
// Бросок
if (pickableProjectile.CurrentState == PickableProjectile.State.Held && pickableProjectile.owner == gameObject) Throw();
if (_pickableProjectile.CurrentState == PickableProjectile.State.Held && _pickableProjectile.owner == gameObject) Throw();
}
/// <summary>
@ -68,18 +73,20 @@ namespace RR.Game2D.Behaviour
Vector2 launchPos = new (1.4f * facing + launchPoint.position.x, launchPoint.position.y);
//Debug.Log(col.bounds.center.y);
launchAxis.x = launchAxis.x == 0 ? launchAxis.x : directionX.x;
if (throwSounds.soundEffects.Length > 0)
GameManager.Instance.Audio.PlayRandomSfxAt(throwSounds.soundEffects, transform.position);
Vector3 projectileScale = pickableProjectile.transform.localScale;
pickableProjectile.transform.localScale = new Vector3(Mathf.Abs(projectileScale.x) * -facing, projectileScale.y, projectileScale.z);
pickableProjectile.transform.position = launchPos;
pickableProjectile.Launch(launchAxis, throwSpeed, gameObject);
Vector3 projectileScale = _pickableProjectile.transform.localScale;
_pickableProjectile.transform.localScale = new Vector3(Mathf.Abs(projectileScale.x) * -facing, projectileScale.y, projectileScale.z);
_pickableProjectile.transform.position = launchPos;
_pickableProjectile.Launch(launchAxis, throwSpeed, gameObject);
}
/// <summary>
/// Метод для привязки снаряда к игроку.
/// </summary>
/// <param name="proj">Снаряд</param>
public void BindProjectile(PickableProjectile proj) => pickableProjectile = proj;
public void BindProjectile(PickableProjectile proj) => _pickableProjectile = proj;
}
}

View File

@ -36,9 +36,9 @@ namespace RR.Common.Entities
[SerializeField] private bool modulateColor = false;
[SerializeField] private Gradient colorOverCycle;
float blinkOffset;
Material _matInstance;
Color _baseEmission = Color.white;
private float _blinkOffset;
private Material _matInstance;
private Color _baseEmission = Color.white;
private void Reset()
{
@ -58,7 +58,7 @@ namespace RR.Common.Entities
_matInstance.EnableKeyword("_EMISSION");
}
}
blinkOffset = Random.Range(blinkJitter.x, blinkJitter.y);
_blinkOffset = Random.Range(blinkJitter.x, blinkJitter.y);
}
private void Update()
@ -72,7 +72,7 @@ namespace RR.Common.Entities
{
case Mode.Blink:
// пила с джиттером
phase = Mathf.Repeat((time + blinkOffset) * Mathf.Max(0.0001f, frequency), 1f);
phase = Mathf.Repeat((time + _blinkOffset) * Mathf.Max(0.0001f, frequency), 1f);
k = phase < Mathf.Clamp01(duty) ? 1f : 0f;
break;

View File

@ -7,6 +7,7 @@ namespace RR.Common.Behaviour
[SerializeField, Range(0f, 10f)] private float angleX = 0f;
[SerializeField, Range(0f, 10f)] private float angleY = 0f;
[SerializeField, Range(0f, 10f)] private float angleZ = 0f;
private void FixedUpdate()
{
transform.Rotate(new Vector3(angleX, angleY, angleZ));

View File

@ -8,11 +8,11 @@ namespace RR.Game2D.Entities.Hazards
[RequireComponent(typeof(Collider2D))]
public class Trap : MonoBehaviour
{
private Collider2D col;
private Collider2D _col;
private void Awake()
{
col = GetComponent<Collider2D>();
_col = GetComponent<Collider2D>();
}
private void OnCollisionEnter2D(Collision2D collision)
@ -32,7 +32,7 @@ namespace RR.Game2D.Entities.Hazards
private void OnTriggerEnter2D(Collider2D other)
{
if (!col.isTrigger) return;
if (!_col.isTrigger) return;
if (other.CompareTag("Player"))
{

View File

@ -10,21 +10,21 @@ public class PlayerRunVFX : MonoBehaviour
[SerializeField] private Vector2 offset = Vector2.zero;
[SerializeField] private float requiredPlayerRunTime;
private GameObject player;
private Rigidbody2D playerRB;
private PlayerController playerController;
private float startRunTime = 0;
private GameObject _player;
private Rigidbody2D _playerRB;
private PlayerController _playerController;
private float _startRunTime = 0;
private Vector3 particlesVelocityDirection;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{
player = GameManager.Instance.PlayerObj;
playerRB = player.GetComponent<Rigidbody2D>();
playerController = player.GetComponent<PlayerController>();
_player = GameManager.Instance.PlayerObj;
_playerRB = _player.GetComponent<Rigidbody2D>();
_playerController = _player.GetComponent<PlayerController>();
if (player == null) player = GameObject.FindGameObjectWithTag("Player");
if (_player == null) _player = GameObject.FindGameObjectWithTag("Player");
particlesVelocityDirection = runVFX.GetVector3("ParticlesVelocityDirection");
runVFX.Stop();
@ -35,34 +35,34 @@ public class PlayerRunVFX : MonoBehaviour
void Update()
{
if (playerController.IsGrounded) transform.position = player.transform.position
+ new Vector3(offset.x * Mathf.Sign(playerRB.linearVelocityX), offset.y, 0f);
else transform.position = new Vector3(player.transform.position.x, transform.position.y, transform.position.z);
if (_playerController.IsGrounded) transform.position = _player.transform.position
+ new Vector3(offset.x * Mathf.Sign(_playerRB.linearVelocityX), offset.y, 0f);
else transform.position = new Vector3(_player.transform.position.x, transform.position.y, transform.position.z);
if (!runVFX.enabled) runVFX.enabled = true;
if (Mathf.Abs(playerRB.linearVelocityX) >= 0.01f && playerController.IsGrounded)
if (Mathf.Abs(_playerRB.linearVelocityX) >= 0.01f && _playerController.IsGrounded)
{
if (startRunTime == 0) startRunTime = Time.time;
if (_startRunTime == 0) _startRunTime = Time.time;
if (Time.time - startRunTime < requiredPlayerRunTime) return;
if (Time.time - _startRunTime < requiredPlayerRunTime) return;
runVFX.Play();
runVFX.SetVector3("ParticlesVelocityDirection", new Vector3(particlesVelocityDirection.x * Mathf.Sign(playerRB.linearVelocityX),
runVFX.SetVector3("ParticlesVelocityDirection", new Vector3(particlesVelocityDirection.x * Mathf.Sign(_playerRB.linearVelocityX),
particlesVelocityDirection.y,
particlesVelocityDirection.z));
return;
}
if (playerRB.linearVelocityY >= 0.25f * playerController.jumpVelocity)
if (_playerRB.linearVelocityY >= 0.25f * _playerController.jumpVelocity)
{
runVFX.SetVector3("ParticlesVelocityDirection", new Vector3(particlesVelocityDirection.x * Mathf.Sign(playerRB.linearVelocityX),
playerRB.linearVelocityY,
runVFX.SetVector3("ParticlesVelocityDirection", new Vector3(particlesVelocityDirection.x * Mathf.Sign(_playerRB.linearVelocityX),
_playerRB.linearVelocityY,
particlesVelocityDirection.z));
runVFX.Play();
if (playerRB.linearVelocityX == 0) startRunTime = 0;
if (_playerRB.linearVelocityX == 0) _startRunTime = 0;
return;
}
startRunTime = 0;
_startRunTime = 0;
runVFX.Stop();
}
}

View File

@ -8,23 +8,8 @@ namespace RR.Game2D.Entities.Projectiles
{
base.Awake();
rb.bodyType = RigidbodyType2D.Kinematic;
col.isTrigger = false;
_rb.bodyType = RigidbodyType2D.Kinematic;
_col.isTrigger = false;
}
//protected override void OnHit(Collision2D target, Vector2 point, Vector2 relativeVelocity)
//{
//var player = GameManager.Instance.PlayerObj;
//if (target.collider.CompareTag("Player"))
// player.GetComponent<PlayerController>().Die();
//else if (target.collider.CompareTag("Projectile") || target.collider.CompareTag("Enemy"))
// Despawn();
// if (!target.collider.CompareTag(owner.tag))
// Despawn();
// base.OnHit(target, point, relativeVelocity);
//}
}
}

View File

@ -13,19 +13,19 @@ namespace RR.Game2D.Entities.Projectiles
{
base.Awake();
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = 0;
_rb.bodyType = RigidbodyType2D.Dynamic;
_rb.gravityScale = 0;
}
private void Update()
{
if (Time.time - launchTime>= maxLifetime)
if (Time.time - _launchTime>= maxLifetime)
{
if (despawnInstantly) Despawn();
else
{
rb.bodyType = RigidbodyType2D.Dynamic;
rb.gravityScale = fallGravityScale;
_rb.bodyType = RigidbodyType2D.Dynamic;
_rb.gravityScale = fallGravityScale;
}
}
}

View File

@ -4,34 +4,28 @@ namespace RR.Game2D.Entities.Projectiles
{
public class PhysicsProjectile : BasicProjectile
{
//protected override void OnEnable()
//{
// remainingPierce = maxPierce;
// if (maxLifetime > 0f)
// Invoke(nameof(Despawn), maxLifetime);
//}
protected override void Awake()
{
base.Awake();
rb.bodyType = RigidbodyType2D.Dynamic;
_rb.bodyType = RigidbodyType2D.Dynamic;
}
public override void Launch(Vector2 direction, float speed = -1f, GameObject thrower = null)
{
this.owner = thrower;
launched = true;
_launched = true;
Vector2 dir = direction.sqrMagnitude > 0.0001f ? direction.normalized : Vector2.right;
float finalSpeed = (speed > 0f) ? speed : initialSpeed;
rb.simulated = true;
rb.bodyType = RigidbodyType2D.Dynamic;
_rb.simulated = true;
_rb.bodyType = RigidbodyType2D.Dynamic;
rb.linearVelocity = Vector2.zero;
rb.angularVelocity = 0f;
_rb.linearVelocity = Vector2.zero;
_rb.angularVelocity = 0f;
rb.AddForce(finalSpeed * rb.mass * dir, ForceMode2D.Impulse);
_rb.AddForce(finalSpeed * _rb.mass * dir, ForceMode2D.Impulse);
transform.right = dir;

View File

@ -71,11 +71,11 @@ namespace RR.Game2D.Entities.Projectiles
protected override void Awake()
{
// Получение ссылок на компоненты Rigidbody2D и Collider
rb = GetComponent<Rigidbody2D>();
_rb = GetComponent<Rigidbody2D>();
col = GetComponent<CapsuleCollider2D>();
_col = GetComponent<CapsuleCollider2D>();
// Пока в Awake оставляем коллайдер в режиме триггера — это переставим в Start/другие состояния по необходимости
col.isTrigger = true;
_col.isTrigger = true;
// Простая реализация singleton-а — предотвращаем дублирование снаряда, если это ожидаемое поведение проекта
if (Instance != null && Instance != this)
@ -119,8 +119,8 @@ namespace RR.Game2D.Entities.Projectiles
/// </summary>
private void Start()
{
rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
col.isTrigger = false;
_rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
_col.isTrigger = false;
if (CurrentState != State.Held)
BecomeGrounded();
@ -136,16 +136,16 @@ namespace RR.Game2D.Entities.Projectiles
// Если не нужно блокировать при приземлении — проверяем скорость и переводим в состояние 'Grounded'
if (!lockWhenLanded && CurrentState == State.Airborne)
{
if (rb.linearVelocity.sqrMagnitude < (groundVelocity * groundVelocity))
if (_rb.linearVelocity.sqrMagnitude < (groundVelocity * groundVelocity))
BecomeGrounded();
}
// В полёте и при возврате снаряд ориентируется по вектору скорости
if ((CurrentState == State.Airborne || CurrentState == State.Returning) && rb.linearVelocity.sqrMagnitude > 0.0001f)
transform.up = rb.linearVelocity.normalized;
if ((CurrentState == State.Airborne || CurrentState == State.Returning) && _rb.linearVelocity.sqrMagnitude > 0.0001f)
transform.up = _rb.linearVelocity.normalized;
// Дополнительная страховка перехода в grounded по скорости
if (CurrentState == State.Airborne && rb.linearVelocity.magnitude < groundVelocity)
if (CurrentState == State.Airborne && _rb.linearVelocity.magnitude < groundVelocity)
BecomeGrounded();
// Если снаряд когда-то был в руках — он может начать возвращаться автоматически
@ -319,10 +319,10 @@ namespace RR.Game2D.Entities.Projectiles
holderAttachPoint = newOwner.transform;
// Отключаем физическое поведение и делаем снаряд кинематическим, чтобы он "встал" в руки игрока
rb.linearVelocity = Vector2.zero;
rb.angularVelocity = 0f;
rb.bodyType = RigidbodyType2D.Kinematic;
col.enabled = false;
_rb.linearVelocity = Vector2.zero;
_rb.angularVelocity = 0f;
_rb.bodyType = RigidbodyType2D.Kinematic;
_col.enabled = false;
transform.SetParent(holderAttachPoint);
// Устанавливаем позицию и вращение относительно точки привязки.
@ -334,7 +334,7 @@ namespace RR.Game2D.Entities.Projectiles
//StartCoroutine(IgnoreOwnerTemporarily(owner, ignoreOwnerTimeOnThrow));
CurrentState = State.Held;
launched = false;
_launched = false;
hadBeenHeld = true;
// При подборе убираем jumpPad, чтобы игрок не случайно "прыгнул" с него
@ -384,7 +384,7 @@ namespace RR.Game2D.Entities.Projectiles
if (bounceCounter > maxBounces) return false;
Vector2 velocityIn = rb.linearVelocity;
Vector2 velocityIn = _rb.linearVelocity;
float speed = velocityIn.magnitude;
if (speed < minBounceSpeed) return false;
@ -393,7 +393,7 @@ namespace RR.Game2D.Entities.Projectiles
Vector2 direction = Vector2.Reflect(velocityIn, collision.contacts[0].normal);
// Здесь используется bounciness физического материала коллайдера
rb.linearVelocity = col.sharedMaterial.bounciness * direction;
_rb.linearVelocity = _col.sharedMaterial.bounciness * direction;
if (collision.collider.CompareTag("Ground") || collision.collider.CompareTag("Trap"))
@ -430,18 +430,18 @@ namespace RR.Game2D.Entities.Projectiles
lastKnownHolder = thrower;
holderAttachPoint = null;
rb.bodyType = RigidbodyType2D.Dynamic;
col.enabled = true;
col.isTrigger = false;
_rb.bodyType = RigidbodyType2D.Dynamic;
_col.enabled = true;
_col.isTrigger = false;
launched = true;
_launched = true;
Vector2 launchDirection = direction.sqrMagnitude > 0.0001f ? direction.normalized : Vector2.right;
float finalSpeed = (speed > 0f) ? speed : initialSpeed;
rb.linearVelocity = Vector2.zero;
rb.angularVelocity = 0f;
rb.AddForce(finalSpeed * rb.mass * launchDirection, ForceMode2D.Impulse);
_rb.linearVelocity = Vector2.zero;
_rb.angularVelocity = 0f;
_rb.AddForce(finalSpeed * _rb.mass * launchDirection, ForceMode2D.Impulse);
// Ориентируем снаряд по направлению скорости
transform.up = launchDirection;
@ -496,11 +496,11 @@ namespace RR.Game2D.Entities.Projectiles
else
{
// Если камеры нет — проверяем дистанцию до владельца как запасной критерий видимости
Vector2 toOwnerTest = (Vector2)attachPoint.position - rb.position;
Vector2 toOwnerTest = (Vector2)attachPoint.position - _rb.position;
isVisible = (toOwnerTest.magnitude <= recallStartDistance);
}
Vector2 toOwner = (Vector2)attachPoint.position - rb.position;
Vector2 toOwner = (Vector2)attachPoint.position - _rb.position;
float dist = toOwner.magnitude;
if (CurrentState != State.Returning)
@ -509,8 +509,8 @@ namespace RR.Game2D.Entities.Projectiles
{
// Переходим в режим возврата: включаем физику и делаем триггер, чтобы не мешать столкновениям
CurrentState = State.Returning;
rb.bodyType = RigidbodyType2D.Dynamic;
col.isTrigger = true;
_rb.bodyType = RigidbodyType2D.Dynamic;
_col.isTrigger = true;
}
else
{
@ -519,11 +519,11 @@ namespace RR.Game2D.Entities.Projectiles
}
}
Vector2 dir = (dist > 0.001f) ? (toOwner / dist) : rb.linearVelocity.normalized;
rb.linearVelocity = dir * recallSpeed;
Vector2 dir = (dist > 0.001f) ? (toOwner / dist) : _rb.linearVelocity.normalized;
_rb.linearVelocity = dir * recallSpeed;
if (rb.linearVelocity.sqrMagnitude > 0.01f)
transform.up = rb.linearVelocity.normalized;
if (_rb.linearVelocity.sqrMagnitude > 0.01f)
transform.up = _rb.linearVelocity.normalized;
// Если достаточно близко — автоматически подбираем
if (dist <= recallArriveDistance)
@ -547,23 +547,23 @@ namespace RR.Game2D.Entities.Projectiles
/// </summary>
private void BecomeGrounded()
{
launched = false;
_launched = false;
CurrentState = State.Grounded;
if (lockWhenLanded)
{
// Полностью останавливаем и делаем кинематическим
rb.linearVelocity = Vector2.zero;
rb.angularVelocity = 0f;
rb.bodyType = RigidbodyType2D.Kinematic;
_rb.linearVelocity = Vector2.zero;
_rb.angularVelocity = 0f;
_rb.bodyType = RigidbodyType2D.Kinematic;
}
else
{
rb.bodyType = RigidbodyType2D.Dynamic;
_rb.bodyType = RigidbodyType2D.Dynamic;
}
col.enabled = true;
col.isTrigger = true;
_col.enabled = true;
_col.isTrigger = true;
// Отвязываем от родителя
transform.SetParent(null, true);
@ -580,8 +580,8 @@ namespace RR.Game2D.Entities.Projectiles
private void MoveJumpPad()
{
// Размеры коллайдера используются для определения смещения платформы
float sizeX = col.bounds.size.y;
float sizeY = col.bounds.size.x;
float sizeX = _col.bounds.size.y;
float sizeY = _col.bounds.size.x;
Vector3 jumpPadPos = transform.position;

View File

@ -17,10 +17,6 @@ namespace RR.Game2D.Entities.Projectiles
[SerializeField] protected LayerMask hitLayers = ~0;
[SerializeField] protected int maxPierce = 0;
//[Header("Гравитация/физика")]
//[SerializeField] protected bool useGravity = false;
//[SerializeField] protected float gravityScale = 1f;
[Header("FX")]
[SerializeField] protected GameObject hitVfxPrefab;
[SerializeField] protected AudioClip hitSfx;
@ -28,44 +24,34 @@ namespace RR.Game2D.Entities.Projectiles
[Header("Другое")]
[SerializeField] private bool alignRotationToVelocity = true;
//[SerializeField] float damage = 10f;
//[SerializeField] float knockback = 0f;
protected Rigidbody2D rb;
protected Collider2D col;
protected Rigidbody2D _rb;
protected Collider2D _col;
protected int remainingPierce;
protected bool launched;
protected float launchTime;
protected int _remainingPierce;
protected bool _launched;
protected float _launchTime;
public GameObject owner;
protected virtual void Awake()
{
rb = GetComponent<Rigidbody2D>();
col = GetComponent<Collider2D>();
//// Базовые настройки физики
//rb.bodyType = RigidbodyType2D.Dynamic;
////rb.gravityScale = useGravity ? gravityScale : 0f;
//rb.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
//col.isTrigger = false;
_rb = GetComponent<Rigidbody2D>();
_col = GetComponent<Collider2D>();
}
protected virtual void OnEnable()
{
remainingPierce = maxPierce;
//if (maxLifetime > 0f)
// Invoke(nameof(Despawn), maxLifetime);
_remainingPierce = maxPierce;
}
protected virtual void FixedUpdate()
{
if (!launched) return;
if (!_launched) return;
if (alignRotationToVelocity && rb.linearVelocity.sqrMagnitude > 0.0001f)
if (alignRotationToVelocity && _rb.linearVelocity.sqrMagnitude > 0.0001f)
{
float angle = Mathf.Atan2(rb.linearVelocity.y, rb.linearVelocity.x) * Mathf.Rad2Deg;
rb.SetRotation(angle);
float angle = Mathf.Atan2(_rb.linearVelocity.y, _rb.linearVelocity.x) * Mathf.Rad2Deg;
_rb.SetRotation(angle);
}
}
@ -78,25 +64,25 @@ namespace RR.Game2D.Entities.Projectiles
protected virtual void OnTriggerEnter2D(Collider2D other)
{
if (!col.isTrigger) return;
if (!_col.isTrigger) return;
if (!IsInHitLayer(other.gameObject.layer)) return;
Vector2 point = other.ClosestPoint(transform.position);
OnHit(other, point, rb.linearVelocity);
OnHit(other, point, _rb.linearVelocity);
}
public virtual void Launch(Vector2 direction, float speed = -1f, GameObject thrower = null)
{
this.owner = thrower;
launched = true;
_launched = true;
float finalSpeed = (speed > 0f) ? speed : initialSpeed;
rb.linearVelocity = direction.normalized * finalSpeed;
_rb.linearVelocity = direction.normalized * finalSpeed;
if (thrower != null && ignoreOwnerTime > 0f)
StartCoroutine(IgnoreOwnerTemporarily(thrower, ignoreOwnerTime));
launchTime = Time.time;
_launchTime = Time.time;
}
/// <summary>
@ -108,12 +94,12 @@ namespace RR.Game2D.Entities.Projectiles
{
Collider2D[] ownerColliders = who.GetComponentsInChildren<Collider2D>();
foreach (var oc in ownerColliders)
if (oc && col) Physics2D.IgnoreCollision(col, oc, true);
if (oc && _col) Physics2D.IgnoreCollision(_col, oc, true);
yield return new WaitForSeconds(time);
foreach (var oc in ownerColliders)
if (oc && col) Physics2D.IgnoreCollision(col, oc, false);
if (oc && _col) Physics2D.IgnoreCollision(_col, oc, false);
}
/// <summary>
@ -156,9 +142,9 @@ namespace RR.Game2D.Entities.Projectiles
PlayFX(point);
if (remainingPierce > 0)
if (_remainingPierce > 0)
{
remainingPierce--;
_remainingPierce--;
return;
}
@ -176,9 +162,9 @@ namespace RR.Game2D.Entities.Projectiles
TryDealDamage(otherCol, point, relativeVelocity);
PlayFX(point);
if (remainingPierce > 0)
if (_remainingPierce > 0)
{
remainingPierce--;
_remainingPierce--;
return;
}

View File

@ -18,3 +18,16 @@ MonoBehaviour:
projectileLifetime: 1
projectileDespawnInstantly: 1
projectileFallGravityScale: 3
shotSounds:
- sound: {fileID: 8300000, guid: f5084f4cf2fabc74fae40a5a9ebc2718, type: 3}
volume: 1.5
pitch: 1
- sound: {fileID: 8300000, guid: eb475b242c9b6ec448dfec33ec8da9f2, type: 3}
volume: 1
pitch: 1
- sound: {fileID: 8300000, guid: f5084f4cf2fabc74fae40a5a9ebc2718, type: 3}
volume: 1.5
pitch: 0.9
- sound: {fileID: 8300000, guid: eb475b242c9b6ec448dfec33ec8da9f2, type: 3}
volume: 1
pitch: 0.9

View File

@ -18,3 +18,7 @@ MonoBehaviour:
projectileLifetime: 999
projectileDespawnInstantly: 1
projectileFallGravityScale: 3
shotSounds:
- sound: {fileID: 8300000, guid: a0166fbc7106f42498be14d005a200f4, type: 3}
volume: 1
pitch: 1

View File

@ -18,3 +18,16 @@ MonoBehaviour:
projectileLifetime: 1
projectileDespawnInstantly: 0
projectileFallGravityScale: 3
shotSounds:
- sound: {fileID: 8300000, guid: f5084f4cf2fabc74fae40a5a9ebc2718, type: 3}
volume: 1.5
pitch: 1
- sound: {fileID: 8300000, guid: eb475b242c9b6ec448dfec33ec8da9f2, type: 3}
volume: 1
pitch: 1
- sound: {fileID: 8300000, guid: f5084f4cf2fabc74fae40a5a9ebc2718, type: 3}
volume: 1.5
pitch: 0.9
- sound: {fileID: 8300000, guid: eb475b242c9b6ec448dfec33ec8da9f2, type: 3}
volume: 1
pitch: 0.9

View File

@ -0,0 +1,16 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7d0ab526e27a165469927c990b5cfaba, type: 3}
m_Name: MainEnemySFXAsset
m_EditorClassIdentifier: Assembly-CSharp::RR.Game2D.Behaviour.ScriptableObjects.EnemySFXAsset
stepSounds: {fileID: 11400000, guid: 37fe07ddcac109a46a86ba44df16d551, type: 2}
hurtSounds: {fileID: 11400000, guid: 16067cac1ebba8144b39d4b576bf38b0, type: 2}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9cb4dd9d30f2420439e056018daf622a
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,7 +2,7 @@ using UnityEngine;
namespace RR.Game2D.Behaviour.ScriptableObjects
{
[CreateAssetMenu(fileName = "EnemyControllerConfiguration", menuName = "Scriptable Objects/EnemyControllerConfiguration")]
[CreateAssetMenu(fileName = "EnemyControllerConfiguration", menuName = "Behaviour/Controllers/EnemyControllerConfiguration")]
public class EnemyControllerConfiguration : ScriptableObject
{
[Header("Обзор")]

View File

@ -1,9 +1,11 @@
using System;
using UnityEngine;
using RR.Common.Managers;
using RR.Game2D.Entities.Projectiles;
namespace RR.Game2D.Behaviour.ScriptableObjects
{
[CreateAssetMenu(fileName = "EnemyFireConfiguration", menuName = "Scriptable Objects/EnemyFireConfiguration")]
[CreateAssetMenu(fileName = "EnemyFireConfiguration", menuName = "Behaviour/Fire/EnemyFireConfiguration")]
public class EnemyFireConfiguration : ScriptableObject
{
[Header("Бросок")]
@ -15,6 +17,9 @@ namespace RR.Game2D.Behaviour.ScriptableObjects
public float projectileLifetime = 1f;
public bool projectileDespawnInstantly = false;
public float projectileFallGravityScale = 3f;
[Header("FX (Опционально)")]
public SoundEffect[] shotSounds;
}
}

View File

@ -0,0 +1,11 @@
using UnityEngine;
namespace RR.Game2D.Behaviour.ScriptableObjects
{
[CreateAssetMenu(fileName = "SFXAsset", menuName = "Audio/SFX Assets/EnemySFXAsset")]
public class EnemySFXAsset : ScriptableObject
{
public SFXAsset stepSounds;
public SFXAsset hurtSounds;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7d0ab526e27a165469927c990b5cfaba

View File

@ -0,0 +1,12 @@
using UnityEngine;
using RR.Common.Managers;
namespace RR.Game2D.Behaviour.ScriptableObjects
{
[CreateAssetMenu(fileName = "SFXAsset", menuName = "Audio/SFX Assets/SFXAsset")]
public class SFXAsset : ScriptableObject
{
public SoundEffect[] soundEffects;
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8b7a812250cb02a439b8c382ae42dd27

View File

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

View File

@ -0,0 +1,24 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8b7a812250cb02a439b8c382ae42dd27, type: 3}
m_Name: EnemyHurtSounds
m_EditorClassIdentifier: Assembly-CSharp::RR.Game2D.Behaviour.ScriptableObjects.SFXAsset
soundEffects:
- sound: {fileID: 8300000, guid: 37f7526ec58db8843916100518133785, type: 3}
volume: 1
pitch: 1
- sound: {fileID: 8300000, guid: 37f7526ec58db8843916100518133785, type: 3}
volume: 1.2
pitch: 0.9
- sound: {fileID: 8300000, guid: 37f7526ec58db8843916100518133785, type: 3}
volume: 0.9
pitch: 1.1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 16067cac1ebba8144b39d4b576bf38b0
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,18 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8b7a812250cb02a439b8c382ae42dd27, type: 3}
m_Name: PlayerJumpSounds
m_EditorClassIdentifier: Assembly-CSharp::RR.Game2D.Behaviour.ScriptableObjects.SFXAsset
soundEffects:
- sound: {fileID: 8300000, guid: fa0b6b9872cd9b84bb24e35b24e57fdf, type: 3}
volume: 1
pitch: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9a63566742cc2d84c89928c8b988e4eb
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,24 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8b7a812250cb02a439b8c382ae42dd27, type: 3}
m_Name: PlayerThrowSounds
m_EditorClassIdentifier: Assembly-CSharp::RR.Game2D.Behaviour.ScriptableObjects.SFXAsset
soundEffects:
- sound: {fileID: 8300000, guid: a9399f94f3fa20f44b8d76c30deda71c, type: 3}
volume: 1
pitch: 1
- sound: {fileID: 8300000, guid: a9399f94f3fa20f44b8d76c30deda71c, type: 3}
volume: 1.1
pitch: 0.95
- sound: {fileID: 8300000, guid: a9399f94f3fa20f44b8d76c30deda71c, type: 3}
volume: 0.95
pitch: 1.05

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 4b774d97282b77f45b08068d61682269
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8b7a812250cb02a439b8c382ae42dd27, type: 3}
m_Name: PlayetHurtSounds
m_EditorClassIdentifier: Assembly-CSharp::RR.Game2D.Behaviour.ScriptableObjects.SFXAsset
soundEffects:
- sound: {fileID: 8300000, guid: f59cd1f6b437f7d4f8fa63bad1e466a5, type: 3}
volume: 1
pitch: 1
- sound: {fileID: 8300000, guid: ce8d9dfcc94fcae46a52bd90ac0b0a19, type: 3}
volume: 1
pitch: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 89e7f713913aecb40ada8c2f27c69303
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,33 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 8b7a812250cb02a439b8c382ae42dd27, type: 3}
m_Name: StepsSounds_Level_1
m_EditorClassIdentifier: Assembly-CSharp::RR.Game2D.Behaviour.ScriptableObjects.SFXAsset
soundEffects:
- sound: {fileID: 8300000, guid: 144cba17f4a47274d834d5e1fc28acb9, type: 3}
volume: 1
pitch: 1
- sound: {fileID: 8300000, guid: 5f68c3b1c31c833458e189d0b7d622dd, type: 3}
volume: 1
pitch: 1
- sound: {fileID: 8300000, guid: 144cba17f4a47274d834d5e1fc28acb9, type: 3}
volume: 1
pitch: 1.05
- sound: {fileID: 8300000, guid: 5f68c3b1c31c833458e189d0b7d622dd, type: 3}
volume: 1
pitch: 1.05
- sound: {fileID: 8300000, guid: 144cba17f4a47274d834d5e1fc28acb9, type: 3}
volume: 1
pitch: 0.95
- sound: {fileID: 8300000, guid: 5f68c3b1c31c833458e189d0b7d622dd, type: 3}
volume: 1
pitch: 0.95

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 37fe07ddcac109a46a86ba44df16d551
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -10,13 +10,13 @@ namespace RR.Common.UI
[SerializeField] TextMeshProUGUI title;
[SerializeField] Button btn;
private int world;
private int stage;
private int _world;
private int _stage;
public void Setup(string levelSceneName, int levelWorld, int levelStage)
{
world = levelWorld;
stage = levelStage;
_world = levelWorld;
_stage = levelStage;
if (title) title.text = levelSceneName; // "1-3, 1-1 и т.д."
if (btn) { btn.onClick.RemoveAllListeners(); btn.onClick.AddListener(OnClick); }
}
@ -34,7 +34,7 @@ namespace RR.Common.UI
void OnClick()
{
// через GameManager (предпочтительно)
GameManager.Instance.LoadLevel(world, stage);
GameManager.Instance.LoadLevel(_world, _stage);
// Фоллбэк, если нужно:
// UnityEngine.SceneManagement.SceneManager.LoadScene(sceneName);

View File

@ -20,7 +20,7 @@ namespace RR.Common.UI
[SerializeField] private Button closeButton;
[SerializeField] private float fadeDuration = 0.15f;
readonly List<LevelButton> levelsPool = new();
private readonly List<LevelButton> _levelsPool = new();
void Awake()
{
@ -56,8 +56,8 @@ namespace RR.Common.UI
void BuildIfNeeded()
{
// Очистим/пересоберём (можно кэшировать, но пересборка надёжнее при изменениях Build Settings)
foreach (var b in levelsPool) if (b) Destroy(b.gameObject);
levelsPool.Clear();
foreach (var b in _levelsPool) if (b) Destroy(b.gameObject);
_levelsPool.Clear();
var levels = EnumerateLevelScenesInBuild()
.OrderBy(e => e.world)
@ -72,7 +72,7 @@ namespace RR.Common.UI
if (lv.world == 1) btn.SetColor(Color.green);
else if (lv.world == 2) btn.SetColor(Color.blue);
levelsPool.Add(btn);
_levelsPool.Add(btn);
}
}

View File

@ -10,11 +10,6 @@ namespace RR.Common.UI
[SerializeField] private Button newGameButton;
[SerializeField] private Button quitButton;
//private void Awake()
//{
// Bootstrap();
//}
private void Start()
{
Bootstrap();

View File

@ -16,11 +16,11 @@ namespace RR.Common.UI
[Tooltip("Следить и возвращать фокус внутрь модалки, если он уходит наружу.")]
public bool enforce = true;
bool wasOpenLastFrame;
GameObject prevSelectedGlobal;
readonly List<Selectable> outside = new();
readonly Dictionary<Selectable, bool> outsidePrev = new();
Selectable lastModalSelected;
private bool _wasOpenLastFrame;
private GameObject _prevSelectedGlobal;
private readonly List<Selectable> _outside = new();
private readonly Dictionary<Selectable, bool> _outsidePrev = new();
private Selectable _lastModalSelected;
void Reset()
{
@ -31,40 +31,35 @@ namespace RR.Common.UI
}
}
//void OnEnable()
//{
// if (modalRoot != null && modalRoot.activeSelf) OnModalOpened();
//}
void Update()
{
if (modalRoot == null) return;
bool isOpen = modalRoot.activeSelf;
if (isOpen && !wasOpenLastFrame) OnModalOpened();
else if (!isOpen && wasOpenLastFrame) OnModalClosed();
if (isOpen && !_wasOpenLastFrame) OnModalOpened();
else if (!isOpen && _wasOpenLastFrame) OnModalClosed();
wasOpenLastFrame = isOpen;
_wasOpenLastFrame = isOpen;
if (isOpen && enforce) EnsureFocusInsideModal();
}
void OnDisable()
{
if (wasOpenLastFrame) OnModalClosed();
if (_wasOpenLastFrame) OnModalClosed();
}
void OnModalOpened()
{
// запомнить, что было выделено до открытия
prevSelectedGlobal = EventSystem.current ? EventSystem.current.currentSelectedGameObject : null;
_prevSelectedGlobal = EventSystem.current ? EventSystem.current.currentSelectedGameObject : null;
// выключить интерактив вне модалки
CollectOutsideSelectables();
foreach (var s in outside)
foreach (var s in _outside)
{
if (!s) continue;
outsidePrev[s] = s.interactable;
_outsidePrev[s] = s.interactable;
s.interactable = false;
}
@ -82,36 +77,36 @@ namespace RR.Common.UI
{
EventSystem.current.SetSelectedGameObject(null);
EventSystem.current.SetSelectedGameObject(toSelect);
lastModalSelected = toSelect.GetComponent<Selectable>();
_lastModalSelected = toSelect.GetComponent<Selectable>();
}
}
void OnModalClosed()
{
foreach (var kv in outsidePrev)
foreach (var kv in _outsidePrev)
{
var s = kv.Key;
if (s) s.interactable = kv.Value;
}
outsidePrev.Clear();
outside.Clear();
_outsidePrev.Clear();
_outside.Clear();
if (EventSystem.current)
{
if (prevSelectedGlobal && prevSelectedGlobal.activeInHierarchy)
EventSystem.current.SetSelectedGameObject(prevSelectedGlobal);
if (_prevSelectedGlobal && _prevSelectedGlobal.activeInHierarchy)
EventSystem.current.SetSelectedGameObject(_prevSelectedGlobal);
else
EventSystem.current.SetSelectedGameObject(null);
}
lastModalSelected = null;
prevSelectedGlobal = null;
_lastModalSelected = null;
_prevSelectedGlobal = null;
}
void CollectOutsideSelectables()
{
outside.Clear();
outsidePrev.Clear();
_outside.Clear();
_outsidePrev.Clear();
// ✅ Новый API Unity 6
var all = Object.FindObjectsByType<Selectable>(
@ -122,7 +117,7 @@ namespace RR.Common.UI
{
if (!s || !s.gameObject.activeInHierarchy) continue;
if (!IsUnderModal(s.transform))
outside.Add(s);
_outside.Add(s);
}
}
@ -143,19 +138,19 @@ namespace RR.Common.UI
if (cur == null || !IsUnderModal(cur.transform))
{
GameObject fallback =
(lastModalSelected && lastModalSelected.gameObject.activeInHierarchy) ? lastModalSelected.gameObject :
(_lastModalSelected && _lastModalSelected.gameObject.activeInHierarchy) ? _lastModalSelected.gameObject :
(firstSelected && firstSelected.gameObject.activeInHierarchy) ? firstSelected.gameObject :
modalRoot.GetComponentInChildren<Selectable>()?.gameObject;
if (fallback)
{
EventSystem.current.SetSelectedGameObject(fallback);
lastModalSelected = fallback.GetComponent<Selectable>();
_lastModalSelected = fallback.GetComponent<Selectable>();
}
}
else
{
lastModalSelected = cur.GetComponent<Selectable>();
_lastModalSelected = cur.GetComponent<Selectable>();
}
}
}