29 Commits
Author SHA1 Message Date
MrTorex d51527b7f3 это спермаггедон 2025-11-28 10:47:28 +03:00
MrTorex d14f9c2a9f анус рвётся не по-детски 2025-11-28 09:45:49 +03:00
MrTorex 4b6909fe84 Допил наклонов (они такое себе)
Хуже, чем были раньше, но лучше, чем ничего. Плавность переходов имеется, и на том спасибо
2025-11-28 01:15:44 +03:00
MrTorex 766609489d Я ненавижу наклоны (не прорвёмся) 2025-11-28 00:51:20 +03:00
MrTorex d10d2cdca6 прорвёмся (добавлен walljump) 2025-11-27 21:36:19 +03:00
MrTorex 01d3b0adec чел не липнет к воздуху 2025-11-27 21:10:23 +03:00
MrTorex b271042137 инишиал титан падать второй мувмент (но пока очень говно) 2025-11-27 20:37:16 +03:00
MrTorex 7130513471 Допил спавнера мобов
Работает как скрипт на emptyGameObject, через сериалайзы задаётся радиус спавна/деспавна и тип моба на спавн
2025-11-27 10:13:02 +03:00
MrTorex ee7cf3946f фикс траблов с мерджем 2025-11-27 09:24:56 +03:00
MrTorex 175567e4dd Merge branch 'Dev' into mobs-dev 2025-11-27 08:55:20 +03:00
MrTorex 97c60c0925 Подготовка к мерджу 2025-11-27 08:49:02 +03:00
MrTorex bd495a644f инишиал спавнер мобов, нуждаюсь в консультации 2025-11-26 18:23:27 +03:00
ThomasCharester 95cb51db79 Update OrderService.cs 2025-11-23 15:22:25 +03:00
ThomasCharester 2b9fd70c85 Update AudioService.cs 2025-11-23 15:17:56 +03:00
ThomasCharester cd384239ff Загрузчик не юзлес + больше интерфейса 2025-11-23 15:06:35 +03:00
ThomasCharester f3a83eb909 AudioSourceConfig 2025-11-23 01:39:08 +03:00
ThomasCharester 35f703a8d2 Озвучка при уроне, новая музыка 2025-11-23 01:17:45 +03:00
ThomasCharester e5f4f292c2 Интерфейс, конфиги камер 2025-11-23 00:51:58 +03:00
ThomasCharester efad72b4d9 Меню красивое 2025-11-22 01:24:33 +03:00
MrTorex e654a0daf4 Иду делать твиа :( 2025-11-20 10:23:33 +03:00
MrTorex cfe5b39cc9 Добавлены модельки/материалы деда, мобы переделаны под меши 2025-11-20 10:06:45 +03:00
MrTorex c14880457d Merge branch 'mobs-dev' into GraphicsModelsGrossGlossBeuaty 2025-11-20 08:43:00 +03:00
MrTorex 344470978d страдание 2025-11-20 08:41:37 +03:00
MrTorex dab970737a Добавлен конфиг для фога
Завтра будут спавнпоинты + модельки
2025-11-20 08:34:20 +03:00
MrTorex afdd523682 добавлены конфиги для дурнев 2025-11-19 20:11:24 +03:00
MrTorex 575e66425a грязюка 2025-11-19 17:56:53 +03:00
MrTorex c470f63409 я ебаный бездарь 2025-11-19 14:46:16 +03:00
MrTorex 7024e1daf0 темп 2025-11-18 12:27:35 +03:00
MrTorex 020934e07f Инишиал мобы 2025-11-17 14:45:51 +03:00
228 changed files with 36379 additions and 2551 deletions

No files matched your search

@@ -15,7 +15,7 @@ MonoBehaviour:
m_DefaultGroup: d447fc60305c17a17bf20948ab295a45 m_DefaultGroup: d447fc60305c17a17bf20948ab295a45
m_currentHash: m_currentHash:
serializedVersion: 2 serializedVersion: 2
Hash: db97f0adbdaa5d76ea9341571ea44391 Hash: f3899bd8b96717b1b70a2db310a9fcf2
m_OptimizeCatalogSize: 0 m_OptimizeCatalogSize: 0
m_BuildRemoteCatalog: 0 m_BuildRemoteCatalog: 0
m_CatalogRequestsTimeout: 0 m_CatalogRequestsTimeout: 0
@@ -15,21 +15,23 @@ MonoBehaviour:
m_GroupName: Default Local Group m_GroupName: Default Local Group
m_GUID: d447fc60305c17a17bf20948ab295a45 m_GUID: d447fc60305c17a17bf20948ab295a45
m_SerializeEntries: m_SerializeEntries:
- m_GUID: 03e51c504fa7e0624ba855ec35b60bcc
m_Address: absolutely
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 111011400ce13ae859c52c757aef859e
m_Address: ahfreeman
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 144ab5188f8ad7a0a92c75703bd12e60 - m_GUID: 144ab5188f8ad7a0a92c75703bd12e60
m_Address: launch_upmenu1 m_Address: launch_upmenu1
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 16562a5c87213914cad415f10c65a3b0
m_Address: "Assets/Resources_moved/Audio/Music/LASKX3I - \u041F\u0440\u043E\u0442\u0438\u0432
\u0446\u0430\u0440\u044F (\u041A\u0430\u0440\u0430\u043E\u043A\u0435).mp3"
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 170b1a70ddee4d948abea041b9b805e4
m_Address: Assets/Resources_moved/Audio/Music/Half Life Original Soundtrack -
Track 8 - Diabolical Adrenaline Guitar.mp3
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 1d4767c722c05b5d6b96bb226d378c65 - m_GUID: 1d4767c722c05b5d6b96bb226d378c65
m_Address: Assets/Resources_moved/Prefabs/Player/ArmsMesh.prefab m_Address: Assets/Resources_moved/Prefabs/Player/ArmsMesh.prefab
m_ReadOnly: 0 m_ReadOnly: 0
@@ -45,11 +47,6 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 2301400160a23924eb27c4bdbe797739
m_Address: 'Audio/Music/Nightmargin (Oneshot OST) - The Prophecy '
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 23e11d2c8a4f12f4ea9a7a43d1118464 - m_GUID: 23e11d2c8a4f12f4ea9a7a43d1118464
m_Address: Gameplay m_Address: Gameplay
m_ReadOnly: 0 m_ReadOnly: 0
@@ -65,6 +62,11 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 2ed2d93a9f39d4edba511cb7396c7bb6
m_Address: Assets/Resources_moved/Prefabs/Enemies/Cockroach.prefab
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 2f6134357dc2a5641a846a7478ce4c2c - m_GUID: 2f6134357dc2a5641a846a7478ce4c2c
m_Address: bravo! m_Address: bravo!
m_ReadOnly: 0 m_ReadOnly: 0
@@ -115,6 +117,12 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 8187ea542d6ed824d81230966a3b7499
m_Address: Assets/Resources_moved/Audio/Music/Bauhaus - Bela Lugosi's Dead (No
Vocals Remix).mp3
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 82c71411174bfa86ca1f676b9d389f94 - m_GUID: 82c71411174bfa86ca1f676b9d389f94
m_Address: bastard! m_Address: bastard!
m_ReadOnly: 0 m_ReadOnly: 0
@@ -140,13 +148,13 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: b2c9f83c47783069aabb564f68cb0cbb - m_GUID: b2e8dc2aeb0311cd8a826b51cf8e94dc
m_Address: absolutelynot m_Address: GameOver
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: b2e8dc2aeb0311cd8a826b51cf8e94dc - m_GUID: b33855c694de89386a25c56cbe463a0e
m_Address: GameOver m_Address: Assets/Resources_moved/Prefabs/Enemies/DrunkenGrandpa.prefab
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
@@ -170,8 +178,8 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: f4b6fa54ecac6cc418f98824491aa17a - m_GUID: f3d7dc64b34919ad7b50a3dd307a935b
m_Address: Audio/Music/SANABI - JUSTICE Is Watching You m_Address: Assets/Resources_moved/Prefabs/Enemies/Fog.prefab
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
@@ -1,41 +1,76 @@
using Code.App.StateMachine; using Code.App.StateMachine;
using Code.Scenes; using Code.Gameplay.Player;
using Code.Input;
using Code.StateMachine; using Code.StateMachine;
using Code.UI; using Code.UI;
using Cysharp.Threading.Tasks;
using Reflex.Core;
using Reflex.Extensions;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
namespace Code.App.AppStates namespace Code.App.AppStates
{ {
public class GameOverState : BaseState<AppStateMachineContext> public class GameOverState : BaseState<AppStateMachineContext>
{ {
public GameOverState(AppStateMachineContext context) : base(context) public GameOverState(AppStateMachineContext context) : base(context)
{ } {
}
private IPlayerScore _playerScore;
private float damnTimer = 3f; // TODO Удалить при первой возможности private GameOverPanel _gameOverPanel;
public override void EnterState() public override void EnterState()
{ {
Context.UIService.SwitchPanelTo(UIPanel.GameOver); Container sceneContainer = SceneManager.GetActiveScene().GetSceneContainer();
_playerScore = sceneContainer.Resolve<IPlayerScore>();
_scoreCounter = 0;
Context.InputService.EnableMap(ActionMappings.UI);
Context.InputService.DebugBindAction((int)ActionMappings.UI,(int)UIActions.Continue, TryToExit);
GameOver();
base.EnterState(); base.EnterState();
} }
private void TryToExit(InputAction.CallbackContext context)
{
ToMainMenu();
}
private async UniTaskVoid GameOver()
{
await Context.UIService.AsyncSwitchPanelTo(UIPanel.GameOver);
_gameOverPanel = (GameOverPanel)Context.UIService.CurrentPanel;
}
private void ToMainMenu() private void ToMainMenu()
{ {
Context.StateMachine.SwitchState(AppState.MainMenu) Context.StateMachine.SwitchState(AppState.MainMenu)
.AllowStateTransition(AppState.Gameplay); .AllowStateTransition(AppState.Gameplay);
} }
private float _scoreCounter;
public override void Tick() public override void Tick()
{ {
damnTimer -= Time.deltaTime; if (!_gameOverPanel) return;
if (damnTimer <= float.Epsilon) ToMainMenu();
_scoreCounter += Time.deltaTime * 100;
// UnityEngine.Debug.Log($"Score: {_scoreCounter}");
if((int)_scoreCounter >= _playerScore.Score)
{
_gameOverPanel.UpdateScoreText(_playerScore.Score);
}
else _gameOverPanel.UpdateScoreText((int)_scoreCounter);
} }
public override void ExitState() public override void ExitState()
{ {
damnTimer = 5f; Context.InputService.DisableMap(ActionMappings.UI);
Context.InputService.DebugUnbindAction((int)ActionMappings.UI,(int)UIActions.Continue, TryToExit);
Context.UIService.LoadingScreen.Show();
base.ExitState(); base.ExitState();
} }
} }
@@ -1,12 +1,10 @@
using System.Globalization; using Code.App.Loading;
using System.Reflection.Emit;
using Code.App.StateMachine; using Code.App.StateMachine;
using Code.Audio; using Code.Audio;
using Code.Gameplay.Camera; using Code.Gameplay.Camera;
using Code.Gameplay.Camera.CameraStates; using Code.Gameplay.Camera.CameraStates;
using Code.Gameplay.Character.Core; using Code.Gameplay.Character.Core;
using Code.Gameplay.Orders.Order; using Code.Gameplay.Orders.Order;
using Code.Gameplay.Orders.Order;
using Code.Gameplay.Player; using Code.Gameplay.Player;
using Code.Input; using Code.Input;
using Code.Scenes; using Code.Scenes;
@@ -17,7 +15,6 @@ using Code.UI.Panels;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using Reflex.Core; using Reflex.Core;
using Reflex.Extensions; using Reflex.Extensions;
using Reflex.Injectors;
using UnityEngine; using UnityEngine;
using UnityEngine.InputSystem; using UnityEngine.InputSystem;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
@@ -26,15 +23,12 @@ namespace Code.App.AppStates
{ {
public class GameplayState : BaseState<AppStateMachineContext> public class GameplayState : BaseState<AppStateMachineContext>
{ {
private PlayerHealth _playerHealth; private PlayerHealth _playerHealth;
private PlayerMoney _playerMoney; private IPlayerScore _playerScore;
private IScoreService _scoreService;
private IOrderService _orderService; private IOrderService _orderService;
private CharacterProvider _playerCharacter; private CharacterProvider _playerCharacter;
public GameplayState(AppStateMachineContext context) : base(context) public GameplayState(AppStateMachineContext context) : base(context)
{ {
} }
@@ -43,68 +37,104 @@ namespace Code.App.AppStates
{ {
Context.UIService.LoadingScreen.Show(); Context.UIService.LoadingScreen.Show();
_playerMoney = Context.PlayerFactory.CreatePlayerMoney(); Context.LoadingManager
_playerHealth = Context.PlayerFactory.CreatePlayerHealth(); .RegisterLoading(LoadingWork.LoadScene)
.RegisterLoading(LoadingWork.LoadUI)
// .RegisterLoading(LoadingWork.LoadModelResource)
.RegisterLoading(LoadingWork.LoadAudio);
_playerHealth.OnDeath += EndGame; //_playerHealth = Context.PlayerFactory.CreatePlayerHealth();
Context.AudioService.TransitToSnapshot(AudioSnapshotState.Peaceful); //_playerHealth.OnDamage += PlayerDamageTaken;
//_playerHealth.OnDeath += EndGame;
Context.AudioService.TransitToSnapshot(AudioSnapshotState.None);
Context.InputService.EnableMap(ActionMappings.Gameplay); Context.InputService.EnableMap(ActionMappings.Gameplay);
Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug1, GiveOrderDEBUG); Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug1, InstaKillDEBUG);
Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug2, RecieveOrderDEBUG);
Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug3, DeliverOrderDEBUG);
Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug4, RoomPassedDEBUG); Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug4, RoomPassedDEBUG);
Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug5, Tube); Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug5, Tube);
// Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug6, PlayDebugDispatcher);
Context.SceneLoaderService.LoadSceneAsyncAwaitable((int)SceneIds.Gameplay);
Context.LoadingManager.OnLoadingEnded += AfterSceneLoaded;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
WaitForSceneToLoad(); WaitForLoad();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
} }
private async UniTask WaitForPlayerCharacterLoad() => _playerCharacter = await Context.PlayerFactory.CreatePlayer(); private async UniTask WaitForPlayerCharacterLoad() => _playerCharacter = await Context.PlayerFactory.CreatePlayer();
private async UniTaskVoid WaitForSceneToLoad() private async UniTaskVoid WaitForLoad()
{ {
await Context.SceneLoaderService.LoadSceneAsyncAwaitable((int)SceneIds.Gameplay);
await UniTask.WhenAll( await UniTask.WhenAll(
Context.UIService.AsyncSwitchPanelTo(UIPanel.Gameplay), Context.UIService.AsyncSwitchPanelTo(UIPanel.Gameplay),
// TODO
Context.AudioFactory.WarmGameplayMusic(), Context.AudioFactory.WarmGameplayMusic(),
Context.AudioFactory.WarmSFX(), Context.AudioFactory.WarmSFX(),
Context.AudioFactory.WarmDispatcher(), Context.AudioFactory.WarmDispatcher()
// TODO );
Context.LoadingManager
.UnregisterLoading(LoadingWork.LoadUI)
.UnregisterLoading(LoadingWork.LoadAudio);
InputServiceInitialize();
MusicInitialize();
}
private async void AfterSceneLoaded()
{
Context.LoadingManager
.RegisterLoading(LoadingWork.LoadModelResource);
Context.LoadingManager.OnLoadingEnded -= AfterSceneLoaded;
Context.LoadingManager.OnLoadingEnded += AfterPlayerLoaded;
await UniTask.WhenAll(
WaitForPlayerArmsToInitialize(), WaitForPlayerArmsToInitialize(),
WaitForPlayerCharacterLoad() WaitForPlayerCharacterLoad()
); );
MusicInitialize();
_playerHealth = Context.PlayerProvider.Health;
_playerHealth.OnDamage += PlayerDamageTaken;
_playerHealth.OnDeath += EndGame;
Context.LoadingManager
.UnregisterLoading(LoadingWork.LoadModelResource);
}
private void AfterPlayerLoaded()
{
Container sceneContainer = SceneManager.GetActiveScene().GetSceneContainer(); Container sceneContainer = SceneManager.GetActiveScene().GetSceneContainer();
_playerScore = sceneContainer.Resolve<IPlayerScore>();
GameplaySceneEnvironment sceneEnvironment = SceneEnvironmentVariables(sceneContainer); GameplaySceneEnvironment sceneEnvironment = SceneEnvironmentVariables(sceneContainer);
PlayerInitialize(sceneEnvironment); PlayerInitialize(sceneEnvironment);
UIInitialize();
CameraInitialize(); CameraInitialize();
InputServiceInitialize();
GameplayServicesInitialize(sceneContainer); GameplayServicesInitialize(sceneContainer);
_tickEnabled = true; UIInitialize();
Context.UIService.LoadingScreen.Hide(); Context.LoadingManager.OnLoadingEnded -= AfterPlayerLoaded;
_tickEnabled = true;
} }
private async UniTask WaitForPlayerArmsToInitialize() => _armsMesh = await Context.PlayerFactory.CreateArms(Context.CameraStateMachine.Camera.transform); private async UniTask WaitForPlayerArmsToInitialize() => _armsMesh = await Context.PlayerFactory.CreateArms(Context.CameraStateMachine.Camera.transform);
private void OrderGiven(float time) private void OrderGiven(float time)
{ {
_gameplayPanel.ShowTimer();
Context.AudioService.TransitToSnapshot(AudioSnapshotState.Action);
}
private void OrderFailure()
{
_playerHealth.InstaKill();
} }
private void OrderRecieved(float time) private void OrderRecieved(float time)
{ {
@@ -112,22 +142,22 @@ namespace Code.App.AppStates
} }
private void OrderDelivered(int score, float time) private void OrderDelivered(int score, float time)
{ {
_gameplayPanel.HideTimer();
if(time < 0f) Context.AudioService.PlayDispatcher((DispatcherSounds)Random.Range((int)DispatcherSounds.Late1,(int)DispatcherSounds.Late2)); if(time < 0f) Context.AudioService.PlayDispatcher((DispatcherSounds)Random.Range((int)DispatcherSounds.Late1,(int)DispatcherSounds.Late2));
_playerMoney.GiveMoney(score); _playerScore.UpdateScore(score);
}
// DEBUG Context.AudioService.TransitToSnapshot(AudioSnapshotState.Peaceful);
private void GiveOrderDEBUG(InputAction.CallbackContext context)
{
_orderService.OrderGive();
} }
private void RecieveOrderDEBUG(InputAction.CallbackContext context) private void PlayerDamageTaken(int damage, PlayerHealth health)
{ {
_orderService.OrderRecieved(); Context.AudioService.PlayDispatcher((DispatcherSounds)Random.Range((int)DispatcherSounds.TakeDamage1,(int)DispatcherSounds.TakeDamage4));
} }
private void DeliverOrderDEBUG(InputAction.CallbackContext context) // DEBUG
private void InstaKillDEBUG(InputAction.CallbackContext context)
{ {
_orderService.OrderDelivered(); _playerHealth.InstaKill();
} }
private void RoomPassedDEBUG(InputAction.CallbackContext context) private void RoomPassedDEBUG(InputAction.CallbackContext context)
{ {
@@ -145,8 +175,7 @@ namespace Code.App.AppStates
{ {
if (!_tickEnabled) return; if (!_tickEnabled) return;
// TODO ГРЯЗь _orderService.DecreaseTimer(Time.deltaTime);
_gameplayPanel.Timer.text = ((int)_orderService.DecreaseTimer(Time.deltaTime)).ToString(CultureInfo.CurrentCulture);
} }
private void EndGame() private void EndGame()
@@ -158,7 +187,7 @@ namespace Code.App.AppStates
public override void ExitState() public override void ExitState()
{ {
_playerHealth = null; _playerHealth = null;
_playerMoney = null; _playerScore = null;
_tickEnabled = false; _tickEnabled = false;
@@ -166,18 +195,19 @@ namespace Code.App.AppStates
_playerCharacter = null; _playerCharacter = null;
Context.PlayerFactory.ReleaseArms(_armsMesh); Context.PlayerFactory.ReleaseArms(_armsMesh);
_armsMesh = null; _armsMesh = null;
_orderService = null;
Context.AudioService.OnDispatcherStarted -= _gameplayPanel.SummonDispatcher;
Context.AudioService.OnDispatcherEnded -= _gameplayPanel.CloseDispatcher;
Context.InputService.DisableMap(ActionMappings.Gameplay); Context.InputService.DisableMap(ActionMappings.Gameplay);
Context.AudioService.GladlyAskToStopMusic(MusicType.Action, callback: Context.AudioFactory.ReleaseActionMusic); Context.AudioService.GladlyAskToStopMusic(MusicType.Action, callback: Context.AudioFactory.ReleaseActionMusic);
Context.AudioService.GladlyAskToStopMusic(MusicType.Peaceful, callback: Context.AudioFactory.ReleasePeacefulMusic); Context.AudioService.GladlyAskToStopMusic(MusicType.Peaceful, callback: Context.AudioFactory.ReleasePeacefulMusic);
Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug1, GiveOrderDEBUG); Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug1, InstaKillDEBUG);
Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug2, RecieveOrderDEBUG);
Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug3, DeliverOrderDEBUG);
Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug4, RoomPassedDEBUG); Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug4, RoomPassedDEBUG);
Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug5, Tube); Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug5, Tube);
// Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug6, PlayDebugDispatcher);
base.ExitState(); base.ExitState();
} }
@@ -198,12 +228,15 @@ namespace Code.App.AppStates
private void UIInitialize() private void UIInitialize()
{ {
_gameplayPanel = (GameplayPanel)Context.UIService.CurrentPanel; _gameplayPanel = (GameplayPanel)Context.UIService.CurrentPanel;
_gameplayPanel.Initialize(_playerMoney, _playerHealth); _gameplayPanel.Initialize(_playerScore, _playerHealth, _orderService);
Context.AudioService.OnDispatcherStarted += _gameplayPanel.SummonDispatcher;
Context.AudioService.OnDispatcherEnded += _gameplayPanel.CloseDispatcher;
} }
private void MusicInitialize() private void MusicInitialize()
{ {
Context.AudioService.PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(MusicType.Peaceful); Context.AudioService.PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(MusicType.Peaceful);
Context.AudioService.PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(MusicType.Action); Context.AudioService.PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(MusicType.Action);
Context.AudioService.TransitToSnapshot(AudioSnapshotState.Peaceful);
} }
private void PlayerInitialize(GameplaySceneEnvironment sceneEnvironment) private void PlayerInitialize(GameplaySceneEnvironment sceneEnvironment)
{ {
@@ -212,12 +245,12 @@ namespace Code.App.AppStates
} }
private void GameplayServicesInitialize(Container sceneContainer) private void GameplayServicesInitialize(Container sceneContainer)
{ {
_scoreService = sceneContainer.Resolve<IScoreService>();
_orderService = sceneContainer.Resolve<IOrderService>(); _orderService = sceneContainer.Resolve<IOrderService>();
_orderService.OnOrderGiven += OrderGiven; _orderService.OnOrderGiven += OrderGiven;
_orderService.OnOrderRecieved += OrderRecieved; _orderService.OnOrderRecieved += OrderRecieved;
_orderService.OnOrderDelivered += OrderDelivered; _orderService.OnOrderDelivered += OrderDelivered;
_orderService.OnOrderFailure += OrderFailure;
} }
private GameplaySceneEnvironment SceneEnvironmentVariables(Container sceneContainer) private GameplaySceneEnvironment SceneEnvironmentVariables(Container sceneContainer)
{ {
@@ -1,3 +1,4 @@
using Code.App.Loading;
using Code.App.StateMachine; using Code.App.StateMachine;
using Code.Audio; using Code.Audio;
using Code.Gameplay.Camera; using Code.Gameplay.Camera;
@@ -24,44 +25,79 @@ namespace Code.App.AppStates
public override void EnterState() public override void EnterState()
{ {
Context.UIService.LoadingScreen.Show(); Context.LoadingManager
.RegisterLoading(LoadingWork.LoadScene)
.RegisterLoading(LoadingWork.LoadUI)
.RegisterLoading(LoadingWork.LoadAudio);
Context.CameraStateMachine.SwitchState(CameraMode.UltraStatic); // Context.UIService.LoadingScreen.Show();
Context.CameraStateMachine.SwitchState(CameraMode.MainMenu);
Context.SceneLoaderService.LoadSceneAsyncAwaitable((int)SceneIds.MainMenu);
Context.LoadingManager.OnLoadingEnded += AfterSceneLoaded;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
WaitForSceneToLoad(); WaitForLoad();
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
} }
private CinematicDoorOpening _damnDoor = new(); private async UniTaskVoid WaitForLoad()
private async UniTaskVoid WaitForSceneToLoad()
{ {
await UniTask.WhenAll( await UniTask.WhenAll(
Context.SceneLoaderService.LoadSceneAsyncAwaitable((int)SceneIds.MainMenu),
Context.UIService.AsyncSwitchPanelTo(UIPanel.MainMenu), Context.UIService.AsyncSwitchPanelTo(UIPanel.MainMenu),
Context.AudioFactory.WarmMenuMusic() Context.AudioFactory.WarmMenuMusic()
); );
Context.LoadingManager
.UnregisterLoading(LoadingWork.LoadUI)
.UnregisterLoading(LoadingWork.LoadAudio);
InitializeAudio();
InitializeInput();
}
private void AfterSceneLoaded()
{
var mainMenuEnvironment = (MainMenuEnvironment)SceneManager.GetActiveScene()
.GetSceneContainer().Resolve<CurrentSceneEnvironment>();
InitializeUI(mainMenuEnvironment);
InitializeCamera(mainMenuEnvironment);
Context.LoadingManager.OnLoadingEnded -= AfterSceneLoaded;
}
private void InitializeCamera(MainMenuEnvironment mainMenuEnvironment)
{
((Gameplay.Camera.CameraStates.MainMenuState)Context.CameraStateMachine.CurrentBaseState).SetFollowTransform(mainMenuEnvironment
.cameraPosition);
}
private void InitializeInput()
{
Context.InputService.EnableMap(ActionMappings.MainMenu);
}
private void InitializeAudio()
{
Context.AudioService.TransitToSnapshot(AudioSnapshotState.Menu); Context.AudioService.TransitToSnapshot(AudioSnapshotState.Menu);
Context.AudioService.PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(MusicType.Menu); Context.AudioService.PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(MusicType.Menu);
}
var hui = ((MainMenuEnvironment)SceneManager.GetActiveScene() private void InitializeUI(MainMenuEnvironment mainMenuEnvironment)
.GetSceneContainer().Resolve<CurrentSceneEnvironment>()); {
_damnDoor.DamnDoor = hui.damnDoor;
_damnDoor.DamnDoor.SetPhysics(false);
_damnDoor.DoorLight = hui.doorLight;
var menuPanel = (MainMenuPanel)Context.UIService.CurrentPanel; var menuPanel = (MainMenuPanel)Context.UIService.CurrentPanel;
menuPanel.Initialize(mainMenuEnvironment.uiVariables,
mainMenuEnvironment.startButton,
mainMenuEnvironment.settingButton,
mainMenuEnvironment.exitButton);
menuPanel.startButton.button.onClick.AddListener(StartGame); menuPanel.startButton.button.onClick.AddListener(StartGame);
menuPanel.exitButton.button.onClick.AddListener(ExitGame); menuPanel.exitButton.button.onClick.AddListener(ExitGame);
menuPanel.CinematicDoorOpening = _damnDoor;
((UltraStaticState)Context.CameraStateMachine.CurrentBaseState).SetFollowTransform(hui.cameraPosition); mainMenuEnvironment.menuButtonsCanvas.worldCamera = Context.CameraStateMachine.Camera;
Context.UIService.LoadingScreen.Hide();
Context.InputService.EnableMap(ActionMappings.MainMenu);
} }
public override void Tick() public override void Tick()
@@ -1,8 +1,11 @@
using System.Collections.Generic;
using Code.App.Loading;
using Code.App.StateMachine; using Code.App.StateMachine;
using Code.Audio; using Code.Audio;
using Code.Configs; using Code.Configs;
using Code.Constants; using Code.Constants;
using Code.Gameplay.Camera; using Code.Gameplay.Camera;
using Code.Gameplay.Enemies;
using Code.Gameplay.Orders.Order; using Code.Gameplay.Orders.Order;
using Code.Gameplay.Player; using Code.Gameplay.Player;
using Code.Input; using Code.Input;
@@ -26,10 +29,15 @@ namespace Code.App
InstallInfrastructureUnit(containerBuilder, staticDataService); InstallInfrastructureUnit(containerBuilder, staticDataService);
containerBuilder.AddSingleton(typeof(LoadingManager), typeof(ILoadingManager));
containerBuilder.AddSingleton(typeof(InputService), typeof(IInputService)); containerBuilder.AddSingleton(typeof(InputService), typeof(IInputService));
containerBuilder.AddSingleton(typeof(PlayerFactory), typeof(IPlayerFactory)); containerBuilder.AddSingleton(typeof(PlayerFactory), typeof(IPlayerFactory));
containerBuilder.AddSingleton(typeof(EnemyFactory), typeof(IEnemyFactory));
containerBuilder.AddSingleton(typeof(PlayerProvider), typeof(IPlayerProvider));
InitializeCamera(containerBuilder, staticDataService); InitializeCamera(containerBuilder, staticDataService);
containerBuilder.AddSingleton(typeof(UIFactory), typeof(IUIFactory)); containerBuilder.AddSingleton(typeof(UIFactory), typeof(IUIFactory));
containerBuilder.AddSingleton(typeof(UIService), typeof(IUIService)); containerBuilder.AddSingleton(typeof(UIService), typeof(IUIService));
@@ -72,17 +80,23 @@ namespace Code.App
containerBuilder.AddSingleton(staticDataService.InputConfig, typeof(InputConfig)); containerBuilder.AddSingleton(staticDataService.InputConfig, typeof(InputConfig));
containerBuilder.AddSingleton(staticDataService.CameraConfig, typeof(CameraConfig)); containerBuilder.AddSingleton(staticDataService.CameraConfig, typeof(CameraConfig));
containerBuilder.AddSingleton(staticDataService.CameraSettingsConfigs, typeof(Dictionary<CameraMode,CameraSettingsConfig>));
containerBuilder.AddSingleton(staticDataService.PlayerConfig, typeof(PlayerConfig)); containerBuilder.AddSingleton(staticDataService.PlayerConfig, typeof(PlayerConfig));
containerBuilder.AddSingleton(staticDataService.CharacterConfig, typeof(CharacterConfig)); containerBuilder.AddSingleton(staticDataService.CharacterConfig, typeof(CharacterConfig));
containerBuilder.AddSingleton(staticDataService.UIConfig, typeof(UIConfig)); containerBuilder.AddSingleton(staticDataService.UIConfig, typeof(UIConfig));
containerBuilder.AddSingleton(staticDataService.DrunkenGrandpaConfig, typeof(DrunkenGrandpaConfig));
containerBuilder.AddSingleton(staticDataService.CockroachConfig, typeof(CockroachConfig));
containerBuilder.AddSingleton(staticDataService.FogConfig, typeof(FogConfig));
containerBuilder.AddSingleton(staticDataService.AudioMixerConfig, typeof(AudioMixerConfig)); containerBuilder.AddSingleton(staticDataService.AudioMixerConfig, typeof(AudioMixerConfig));
containerBuilder.AddSingleton(staticDataService.SoundtrackListConfig, typeof(SoundtrackListConfig)); containerBuilder.AddSingleton(staticDataService.SoundtrackListConfig, typeof(SoundtrackListConfig));
containerBuilder.AddSingleton(staticDataService.SFXListConfig, typeof(SFXListConfig)); containerBuilder.AddSingleton(staticDataService.SFXListConfig, typeof(SFXListConfig));
containerBuilder.AddSingleton(staticDataService.DispatcherSoundListConfig, typeof(DispatcherSoundListConfig)); containerBuilder.AddSingleton(staticDataService.DispatcherSoundListConfig, typeof(DispatcherSoundListConfig));
containerBuilder.AddSingleton(staticDataService.EnemiesSoundListConfig, typeof(EnemiesSoundListConfig)); containerBuilder.AddSingleton(staticDataService.EnemiesSoundListConfig, typeof(EnemiesSoundListConfig));
containerBuilder.AddSingleton(staticDataService.AudioSourceSettingsConfigs, typeof(Dictionary<AudioSourceType, AudioSourceSettingsConfig>));
containerBuilder.AddSingleton(staticDataService.OrderServiceConfig, typeof(OrderServiceConfig)); containerBuilder.AddSingleton(staticDataService.OrderServiceConfig, typeof(OrderServiceConfig));
containerBuilder.AddSingleton(staticDataService.OrderBoxConfig, typeof(OrderBoxConfig)); containerBuilder.AddSingleton(staticDataService.OrderBoxConfig, typeof(OrderBoxConfig));
@@ -106,14 +120,22 @@ namespace Code.App
} }
private async UniTaskVoid BootstrapFinish(Container container) private async UniTaskVoid BootstrapFinish(Container container)
{ {
var loadingScreen = GameObject.FindWithTag(Tags.LoadingScreen); var loadingScreen = GameObject.FindWithTag(Tags.LoadingScreen).GetComponent<LoadingScreen>();
DontDestroyOnLoad(loadingScreen); DontDestroyOnLoad(loadingScreen.gameObject);
ILoadingManager loadingManager = container.Resolve<ILoadingManager>();
loadingManager.OnLoadingStarted += loadingScreen.Show;
loadingManager.OnLoadingEnded += loadingScreen.Hide;
loadingManager.RegisterLoading(LoadingWork.Bootstrap);
await UniTask.WhenAll( await UniTask.WhenAll(
container.Resolve<IUIService>().Initialize(loadingScreen.GetComponent<LoadingScreen>()), container.Resolve<IUIService>().Initialize(loadingScreen),
container.Resolve<IAudioService>().Initialize() container.Resolve<IAudioService>().Initialize()
); );
loadingManager.UnregisterLoading(LoadingWork.Bootstrap);
_appStateMachine _appStateMachine
.SwitchState(AppState.MainMenu) .SwitchState(AppState.MainMenu)
.AllowStateTransition(AppState.Gameplay); .AllowStateTransition(AppState.Gameplay);
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 08b54658296d4279b85a2bfc3cf10e62
timeCreated: 1763842256
@@ -0,0 +1,16 @@
using System.Collections.ObjectModel;
using Cysharp.Threading.Tasks;
namespace Code.App.Loading
{
public interface ILoadingManager
{
delegate void OnLoadingDelegate();
event OnLoadingDelegate OnLoadingStarted;
event OnLoadingDelegate OnLoadingEnded;
ReadOnlyCollection<LoadingWork> LoadingThings { get; }
LoadingManager RegisterLoading(LoadingWork loadingThingName);
LoadingManager UnregisterLoading(LoadingWork loadingThingName);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bfb3837590d04da3b04ca8b0f0aca437
timeCreated: 1763842985
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Cysharp.Threading.Tasks;
namespace Code.App.Loading
{
public class LoadingManager : ILoadingManager
{
public event ILoadingManager.OnLoadingDelegate OnLoadingStarted;
public event ILoadingManager.OnLoadingDelegate OnLoadingEnded;
private List<LoadingWork> _loadingThings = new();
public ReadOnlyCollection<LoadingWork> LoadingThings => _loadingThings.AsReadOnly();
public LoadingManager RegisterLoading(LoadingWork loadingThingName)
{
if(_loadingThings.Count == 0) OnLoadingStarted?.Invoke();
_loadingThings.Add(loadingThingName);
return this;
}
public LoadingManager UnregisterLoading(LoadingWork loadingThingName)
{
_loadingThings.Remove(loadingThingName);
if(_loadingThings.Count == 0) OnLoadingEnded?.Invoke();
return this;
}
}
// public static class LoadingManagerExtensions
// {
// public static LoadingManager AddLoading(this LoadingManager manager, LoadingWork loadingThingName)
// {
// manager.RegisterLoading(loadingThingName);
// return manager;
// }
// public static LoadingManager RemoveLoading(this LoadingManager manager, LoadingWork loadingThingName)
// {
// manager.UnregisterLoading(loadingThingName);
// return manager;
// }
// }
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 185988ac74a24a39864238cd7f5cb626
timeCreated: 1763842268
@@ -0,0 +1,12 @@
namespace Code.App.Loading
{
public enum LoadingWork
{
Bootstrap,
LoadUI,
LoadAudio,
LoadScene,
LoadModelResource,
WaitingForTime
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 318d1b29b41d4b96bc09334acbf36506
timeCreated: 1763843934
@@ -1,8 +1,11 @@
using Code.App.Loading;
using Code.Gameplay.Orders.Order; using Code.Gameplay.Orders.Order;
using Code.Gameplay.Player;
using Code.Scenes; using Code.Scenes;
using Code.Scenes.SceneEnvironments; using Code.Scenes.SceneEnvironments;
using Reflex.Core; using Reflex.Core;
using UnityEngine; using UnityEngine;
using IPlayerScore = Code.Gameplay.Player.IPlayerScore;
namespace Code.App.SceneInstallers namespace Code.App.SceneInstallers
{ {
@@ -13,7 +16,7 @@ namespace Code.App.SceneInstallers
public void InstallBindings(ContainerBuilder containerBuilder) public void InstallBindings(ContainerBuilder containerBuilder)
{ {
containerBuilder.AddScoped(typeof(GameplaySceneEnvironment), typeof(CurrentSceneEnvironment)); containerBuilder.AddScoped(typeof(GameplaySceneEnvironment), typeof(CurrentSceneEnvironment));
containerBuilder.AddScoped(typeof(ScoreService), typeof(IScoreService)); containerBuilder.AddScoped(typeof(PlayerScore), typeof(IPlayerScore));
containerBuilder.AddScoped(typeof(OrderService), typeof(IOrderService)); containerBuilder.AddScoped(typeof(OrderService), typeof(IOrderService));
containerBuilder.OnContainerBuilt += Initialize; containerBuilder.OnContainerBuilt += Initialize;
@@ -21,9 +24,14 @@ namespace Code.App.SceneInstallers
private void Initialize(Container container) private void Initialize(Container container)
{ {
ILoadingManager loadingManager = container.Resolve<ILoadingManager>();
GameplaySceneEnvironment env = (GameplaySceneEnvironment)container.Resolve<CurrentSceneEnvironment>(); GameplaySceneEnvironment env = (GameplaySceneEnvironment)container.Resolve<CurrentSceneEnvironment>();
env.TransitVariables(_gameplaySceneEnvironment); env.TransitVariables(_gameplaySceneEnvironment);
loadingManager
.UnregisterLoading(LoadingWork.LoadScene);
} }
} }
} }
@@ -1,8 +1,8 @@
using Code.App.Loading;
using Code.Scenes; using Code.Scenes;
using Code.Scenes.SceneEnvironments; using Code.Scenes.SceneEnvironments;
using Reflex.Core; using Reflex.Core;
using UnityEngine; using UnityEngine;
using UnityEngine.Serialization;
namespace Code.App.SceneInstallers namespace Code.App.SceneInstallers
{ {
@@ -22,6 +22,11 @@ namespace Code.App.SceneInstallers
MainMenuEnvironment env = (MainMenuEnvironment)container.Resolve<CurrentSceneEnvironment>(); MainMenuEnvironment env = (MainMenuEnvironment)container.Resolve<CurrentSceneEnvironment>();
env.TransitVariables(_mainMenuEnvironment); env.TransitVariables(_mainMenuEnvironment);
ILoadingManager loadingManager = container.Resolve<ILoadingManager>();
loadingManager
.UnregisterLoading(LoadingWork.LoadScene);
} }
} }
} }
@@ -1,5 +1,7 @@
using Code.App.Loading;
using Code.Audio; using Code.Audio;
using Code.Gameplay.Camera; using Code.Gameplay.Camera;
using Code.Gameplay.Enemies;
using Code.Gameplay.Player; using Code.Gameplay.Player;
using Code.Input; using Code.Input;
using Code.Scenes; using Code.Scenes;
@@ -10,13 +12,16 @@ namespace Code.App.StateMachine
{ {
public class AppStateMachineContext public class AppStateMachineContext
{ {
[Inject] public ILoadingManager LoadingManager { get; private set; }
[Inject] public ISceneLoaderService SceneLoaderService { get; private set; } [Inject] public ISceneLoaderService SceneLoaderService { get; private set; }
[Inject] public IInputService InputService { get; private set; } [Inject] public IInputService InputService { get; private set; }
[Inject] public AppStateMachine StateMachine { get; private set; } [Inject] public AppStateMachine StateMachine { get; private set; }
[Inject] public CameraStateMachine CameraStateMachine { get; private set; } [Inject] public CameraStateMachine CameraStateMachine { get; private set; }
[Inject] public IUIService UIService { get; private set; } [Inject] public IUIService UIService { get; private set; }
[Inject] public IPlayerFactory PlayerFactory { get; private set; } [Inject] public IPlayerFactory PlayerFactory { get; private set; }
[Inject] public IPlayerProvider PlayerProvider { get; private set; }
[Inject] public IAudioFactory AudioFactory { get; private set; } [Inject] public IAudioFactory AudioFactory { get; private set; }
[Inject] public IAudioService AudioService { get; private set; } [Inject] public IAudioService AudioService { get; private set; }
[Inject] public IEnemyFactory EnemyFactory { get; private set; }
} }
} }
@@ -16,9 +16,6 @@ namespace Code.Audio
} }
public enum DispatcherSounds public enum DispatcherSounds
{ {
Absolutely,
AbsolutelyNot,
AhFreeman,
Recieve1, Recieve1,
Recieve2, Recieve2,
TakeDamage1, TakeDamage1,
@@ -31,13 +31,16 @@ namespace Code.Audio
_courierGroup, _courierGroup,
_enemiesGroup; _enemiesGroup;
private readonly AudioSourcePool _audioSourcePool = new(); private readonly AudioSourcePool _audioSourcePool;
private Dictionary<AudioSnapshotState, AudioMixerSnapshot> _snapshots = new(); private Dictionary<AudioSnapshotState, AudioMixerSnapshot> _snapshots = new();
private Dictionary<AudioSourceType, AudioSourceSettingsConfig> _settings;
public AudioService(IAudioFactory audioFactory) public AudioService(IAudioFactory audioFactory, Dictionary<AudioSourceType,AudioSourceSettingsConfig> settings)
{ {
_audioFactory = audioFactory; _audioFactory = audioFactory;
_audioSourcePool = new(settings);
_settings = settings;
} }
public async UniTask Initialize() public async UniTask Initialize()
@@ -86,7 +89,7 @@ namespace Code.Audio
if(!_musicAudioSources[musicType]) if(!_musicAudioSources[musicType])
{ {
_musicAudioSources[musicType] = _audioSourcePool.GetSource(); _musicAudioSources[musicType] = _audioSourcePool.GetSource();
_musicAudioSources[musicType].spatialBlend = 0f; _musicAudioSources[musicType].spatialBlend = _settings[AudioSourceType.NoBlend].spatialBlend;
_musicAudioSources[musicType].outputAudioMixerGroup = _musicAudioGroups[musicType]; _musicAudioSources[musicType].outputAudioMixerGroup = _musicAudioGroups[musicType];
} }
@@ -164,9 +167,13 @@ namespace Code.Audio
_audioSourcePool.PlayOneShot(_audioFactory.EnemiesAudios[(int)enemySoundId], position, _enemiesGroup); _audioSourcePool.PlayOneShot(_audioFactory.EnemiesAudios[(int)enemySoundId], position, _enemiesGroup);
} }
// Чинить долго и дорого пусть Артёмов будет много, ведь он наш друг
public event IAudioService.OnAudioPlayingDelegate OnDispatcherStarted;
public event IAudioService.OnAudioPlayingDelegate OnDispatcherEnded;
public void PlayDispatcher(DispatcherSounds dispatcherSoundId) public void PlayDispatcher(DispatcherSounds dispatcherSoundId)
{ {
_audioSourcePool.PlayShizo(_audioFactory.DispatcherAudios[(int)dispatcherSoundId], _dispatcherGroup); OnDispatcherStarted?.Invoke();
_audioSourcePool.PlayOneShot(_audioFactory.DispatcherAudios[(int)dispatcherSoundId], _dispatcherGroup, AudioSourceType.NoBlend, () => OnDispatcherEnded?.Invoke());
} }
} }
} }
@@ -1,119 +0,0 @@
using System.Collections.Generic;
using Code.Constants;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Serialization;
namespace Code.Audio
{
public class AudioServiceDebug : MonoBehaviour
{
[SerializeField] private AudioMixer mixer;
private AudioSource _actionMusicSource;
private AudioSource _peacefulMusicSource;
[SerializeField] private AudioSource prefab;
[SerializeField] private AudioResource[] peacefulAudio;
[SerializeField] private AudioResource[] actionAudio;
private AudioMixerGroup _masterGroup,
_musicGroup,
_actionMusicGroup,
_peacefulMusicGroup,
_SFXGroup,
_creaturesGroup,
_dispatcherGroup,
_courierGroup,
_enemiesGroup;
private readonly AudioSourcePool _audioSourcePool = new();
private Dictionary<AudioSnapshotState, AudioMixerSnapshot> _snapshots = new();
[FormerlySerializedAs("sfxAudio")] [SerializeField] private AudioClip[] sfxAudios;
[SerializeField] private AudioClip[] enemiesAudios;
[SerializeField] private AudioClip[] dispatcherAudios;
private void MixerCreated()
{
_masterGroup = mixer.FindMatchingGroups(AudioGroups.MasterGroup)[0];
_actionMusicGroup = mixer.FindMatchingGroups(AudioGroups.ActionMusicGroup)[0];
_peacefulMusicGroup = mixer.FindMatchingGroups(AudioGroups.PeacefulMusicGroup)[0];
_SFXGroup = mixer.FindMatchingGroups(AudioGroups.SFXGroup)[0];
_musicGroup = mixer.FindMatchingGroups(AudioGroups.MusicGroup)[0];
_creaturesGroup = mixer.FindMatchingGroups(AudioGroups.CreaturesGroup)[0];
_dispatcherGroup = mixer.FindMatchingGroups(AudioGroups.DispatcherMusicGroup)[0];
_courierGroup = mixer.FindMatchingGroups(AudioGroups.CourierMusicGroup)[0];
_enemiesGroup = mixer.FindMatchingGroups(AudioGroups.EnemiesMusicGroup)[0];
_snapshots[AudioSnapshotState.Peaceful] = mixer.FindSnapshot(AudioGroups.PeacefulSnapshot);
_snapshots[AudioSnapshotState.Action] = mixer.FindSnapshot(AudioGroups.ActionSnapshot);
// _audioSourcePool.Initialize(10,prefab);
InitializeMusicSources();
}
private void InitializeMusicSources()
{
_actionMusicSource = _audioSourcePool.GetSource();
_actionMusicSource.outputAudioMixerGroup = _actionMusicGroup;
_actionMusicSource.spatialBlend = 0f;
_peacefulMusicSource = _audioSourcePool.GetSource();
_peacefulMusicSource.outputAudioMixerGroup = _peacefulMusicGroup;
_peacefulMusicSource.spatialBlend = 0f;
}
private void Start()
{
MixerCreated();
}
public void PlayRandomGameplayMusic()
{
_peacefulMusicSource.resource = peacefulAudio[Random.Range(0, peacefulAudio.Length)];
_actionMusicSource.resource = actionAudio[Random.Range(0, actionAudio.Length)];
_peacefulMusicSource.Play();
_actionMusicSource.Play();
}
public void TransitToSnapshot(AudioSnapshotState state)
{
_snapshots[state].TransitionTo(1f);
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public void PlaySFX(SFXSounds sfxId, Vector3 position)
{
_audioSourcePool.PlayOneShot(sfxAudios[(int)sfxId], position, _SFXGroup);
}
public void PlayDebugSFX()
{
_audioSourcePool.PlayOneShot(sfxAudios[(int)SFXSounds.Crowbar], transform.position, _SFXGroup);
}
public void PlayEnemy(EnemiesSounds enemySoundId, Vector3 position)
{
_audioSourcePool.PlayOneShot(enemiesAudios[(int)enemySoundId], position, _enemiesGroup);
}
public void PlayDebugEnemySFX()
{
_audioSourcePool.PlayOneShot(enemiesAudios[(int)EnemiesSounds.Bastard], transform.position, _enemiesGroup);
}
public void PlayDispatcher(DispatcherSounds dispatcherSoundId)
{
_audioSourcePool.PlayShizo(dispatcherAudios[(int)dispatcherSoundId], _dispatcherGroup);
}
public void PlayDebugDispatcherSFX()
{
_audioSourcePool.PlayShizo(dispatcherAudios[(int)DispatcherSounds.AhFreeman], _dispatcherGroup);
}
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a42cad8f28f54a53bef23d9313014abd
timeCreated: 1762805867
@@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
using Code.Configs;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.Audio; using UnityEngine.Audio;
using UnityEngine.Pool; using UnityEngine.Pool;
using Object = UnityEngine.Object;
namespace Code.Audio namespace Code.Audio
{ {
@@ -11,6 +15,10 @@ namespace Code.Audio
private IObjectPool<AudioSource> _pool; private IObjectPool<AudioSource> _pool;
private GameObject _sourcePrefab; private GameObject _sourcePrefab;
private Dictionary<AudioSourceType, AudioSourceSettingsConfig> _settings;
public AudioSourcePool(Dictionary<AudioSourceType, AudioSourceSettingsConfig> settings) => _settings = settings;
public void Initialize(GameObject sourcePrefab) public void Initialize(GameObject sourcePrefab)
{ {
_sourcePrefab = sourcePrefab; _sourcePrefab = sourcePrefab;
@@ -55,27 +63,27 @@ namespace Code.Audio
Object.Destroy(source.gameObject); Object.Destroy(source.gameObject);
} }
public async UniTaskVoid PlayOneShot(AudioClip clip, Vector3 position, AudioMixerGroup audioMixerGroup) public async UniTaskVoid PlayOneShot(AudioClip clip, Vector3 position, AudioMixerGroup audioMixerGroup, AudioSourceType audioSourceType = AudioSourceType.NoBlend, Action onComplete = null)
{ {
AudioSource source = _pool.Get(); AudioSource source = _pool.Get();
source.PlayOneShot(clip); source.PlayOneShot(clip);
source.spatialBlend = 1f; source.spatialBlend = _settings[audioSourceType].spatialBlend;
source.transform.position = position; source.transform.position = position;
source.outputAudioMixerGroup = audioMixerGroup; source.outputAudioMixerGroup = audioMixerGroup;
await UniTask.WaitUntilValueChanged(source, x => x.isPlaying); await UniTask.WaitUntilValueChanged(source, x => x.isPlaying);
onComplete?.Invoke();
_pool.Release(source); _pool.Release(source);
} }
public async UniTaskVoid PlayShizo(AudioClip clip, AudioMixerGroup audioMixerGroup) public async UniTaskVoid PlayOneShot(AudioClip clip, AudioMixerGroup audioMixerGroup, AudioSourceType audioSourceType = AudioSourceType.NoBlend, Action onComplete = null)
{ {
AudioSource source = _pool.Get(); AudioSource source = _pool.Get();
source.PlayOneShot(clip); source.PlayOneShot(clip);
source.spatialBlend = 0f; source.spatialBlend = _settings[audioSourceType].spatialBlend;
source.outputAudioMixerGroup = audioMixerGroup; source.outputAudioMixerGroup = audioMixerGroup;
await UniTask.WaitUntilValueChanged(source, x => x.isPlaying); await UniTask.WaitUntilValueChanged(source, x => x.isPlaying);
onComplete?.Invoke();
_pool.Release(source); _pool.Release(source);
} }
public AudioSource GetSource() => _pool.Get(); public AudioSource GetSource() => _pool.Get();
@@ -0,0 +1,8 @@
namespace Code.Audio
{
public enum AudioSourceType
{
Full3D,
NoBlend
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 260bbb19b55049928b9ab4d55b32fc3f
timeCreated: 1763849966
@@ -14,6 +14,11 @@ namespace Code.Audio
void TransitToSnapshot(AudioSnapshotState state, float time = 1f); void TransitToSnapshot(AudioSnapshotState state, float time = 1f);
void PlaySFX(SFXSounds sfxId, Vector3 position); void PlaySFX(SFXSounds sfxId, Vector3 position);
void PlayEnemy(EnemiesSounds enemySoundId, Vector3 position); void PlayEnemy(EnemiesSounds enemySoundId, Vector3 position);
delegate void OnAudioPlayingDelegate();
event OnAudioPlayingDelegate OnDispatcherStarted;
event OnAudioPlayingDelegate OnDispatcherEnded;
void PlayDispatcher(DispatcherSounds dispatcherSoundId); void PlayDispatcher(DispatcherSounds dispatcherSoundId);
} }
} }
@@ -0,0 +1,14 @@
using Code.Audio;
using UnityEngine;
namespace Code.Configs
{
[CreateAssetMenu(fileName = "AudioSourceSettingsConfig", menuName = "Audio/AudioSourceSettingsConfig")]
public class AudioSourceSettingsConfig : ScriptableObject
{
// Дополнять по мере необходимости
public float spatialBlend;
public AudioSourceType sourceType;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e6406ab390b74d5ca336d7d4d0e1a732
timeCreated: 1763850008
@@ -0,0 +1,14 @@
using Code.Gameplay.Camera;
using UnityEngine;
namespace Code.Configs
{
[CreateAssetMenu(fileName = "CameraSettingsConfig", menuName = "Config/CameraSettingsConfig")]
public class CameraSettingsConfig : ScriptableObject
{
// Дополнять по мере необходимости
public float fov;
public CameraMode cameraMode;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6610b94e9823401c94533e1756a97195
timeCreated: 1763846744
@@ -23,17 +23,11 @@ namespace Code.Configs
slideDuration = 0.8f, // Длительность слайда в секундах slideDuration = 0.8f, // Длительность слайда в секундах
slideCooldown = 1.5f; // Время перезарядки слайда в секундах slideCooldown = 1.5f; // Время перезарядки слайда в секундах
// Залазание на стены
public float
wallClimbSpeedFactor = 0.8f, // Коэффициент преобразования горизонтальной скорости в вертикальную при залазании на стену
minWallClimbSpeed = 9f, // Минимальная горизонтальная скорость для начала залазания на стену
wallDetectionAngleThreshold = 80f; // Максимальный угол (в градусах) от вертикали, при котором поверхность считается "стеной"
// Наклон при залазании на стены // Наклон при залазании на стены
public float public float
maxWallLeanAngle = 45f, // Максимальный угол наклона от стены (в градусах) leanAngle = 25f,
wallLeanSharpness = 50f, // Скорость наклона/выпрямления (чем выше — тем резче) leanThreshold = 0.3f,
wallLeanHeightScale = 0.1f; // Множитель: на сколько метров подъёма = 1 единица "наклона" leanSmoothSpeed = 10f;
// Гравитация // Гравитация
public Vector3 gravity = new(0f, -30f, 0f); // Вектор, отвечающий за применение гравитации public Vector3 gravity = new(0f, -30f, 0f); // Вектор, отвечающий за применение гравитации
@@ -0,0 +1,29 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace Code.Configs
{
[CreateAssetMenu(menuName = "Config/Enemies/Cockroach", fileName = "CockroachConfig")]
public class CockroachConfig : ScriptableObject
{
public AssetReference cockroachPrefab;
public int damage = 2;
public float attackCooldown = 1.5f;
public float detectionRange = 20f;
public float attackRange = 1.3f;
public float speed = 6f;
public bool isStunnable;
public float stunDuration;
public bool usesIdleMovement = true;
public float idleWanderRadius = 3f;
public float idleMoveDurationMin = 0.3f;
public float idleMoveDurationMax = 1f;
public float idlePauseDurationMin = 0.1f;
public float idlePauseDurationMax = 0.8f;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7bc162d3127949aa950d0738beee99a4
timeCreated: 1763571220
@@ -0,0 +1,29 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace Code.Configs
{
[CreateAssetMenu(menuName = "Config/Enemies/DrunkenGrandpa", fileName = "DrunkenGrandpaConfig")]
public class DrunkenGrandpaConfig : ScriptableObject
{
public AssetReference drunkenGrandpaPrefab;
public int damage = 2;
public float attackCooldown = 5f;
public float detectionRange = 10f;
public float attackRange = 1.5f;
public float speed = 3f;
public bool isStunnable = true;
public float stunDuration = 3f;
public bool usesIdleMovement = true;
public float idleWanderRadius = 5f;
public float idleMoveDurationMin = 1f;
public float idleMoveDurationMax = 5f;
public float idlePauseDurationMin = 3f;
public float idlePauseDurationMax = 5f;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4d157e8891074b90957a7434cff0ae71
timeCreated: 1763569096
@@ -0,0 +1,29 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace Code.Configs
{
[CreateAssetMenu(menuName = "Config/Enemies/Fog", fileName = "FogConfig")]
public class FogConfig : ScriptableObject
{
public AssetReference fogPrefab;
public int damage = int.MaxValue;
public float attackCooldown;
public float detectionRange;
public float attackRange = 5f;
public float speed = 1.5f;
public bool isStunnable;
public float stunDuration;
public bool usesIdleMovement;
public float idleWanderRadius;
public float idleMoveDurationMin;
public float idleMoveDurationMax;
public float idlePauseDurationMin;
public float idlePauseDurationMax;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aa4dd2a5cce24902a2918488b5751f2f
timeCreated: 1763581410
@@ -10,9 +10,9 @@ namespace Code.Gameplay.Camera
*/ */
public enum CameraMode public enum CameraMode
{ {
Bogdan, None,
Static, Static,
UltraStatic, MainMenu,
FirstPerson, FirstPerson,
ThirdPerson, ThirdPerson,
Cinematic Cinematic
@@ -9,12 +9,13 @@ namespace Code.Gameplay.Camera
// Текущий режим работы камеры // Текущий режим работы камеры
public override void Initialize() public override void Initialize()
{ {
States.Add(CameraMode.None, new NoneState(StateMachineContext));
States.Add(CameraMode.Static, new StaticState(StateMachineContext)); States.Add(CameraMode.Static, new StaticState(StateMachineContext));
States.Add(CameraMode.UltraStatic, new UltraStaticState(StateMachineContext)); States.Add(CameraMode.MainMenu, new MainMenuState(StateMachineContext));
States.Add(CameraMode.FirstPerson, new FirstPersonState(StateMachineContext)); States.Add(CameraMode.FirstPerson, new FirstPersonState(StateMachineContext));
States.Add(CameraMode.ThirdPerson, new ThirdPersonState(StateMachineContext)); States.Add(CameraMode.ThirdPerson, new ThirdPersonState(StateMachineContext));
CurrentBaseState = States[CameraMode.UltraStatic]; CurrentBaseState = States[CameraMode.None];
} }
public UnityEngine.Camera Camera => StateMachineContext.Camera; public UnityEngine.Camera Camera => StateMachineContext.Camera;
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using Code.Configs; using Code.Configs;
using Code.Input; using Code.Input;
using Reflex.Attributes; using Reflex.Attributes;
@@ -9,5 +10,6 @@ namespace Code.Gameplay.Camera
[Inject] public UnityEngine.Camera Camera; [Inject] public UnityEngine.Camera Camera;
[Inject] public CameraConfig Config; [Inject] public CameraConfig Config;
[Inject] public IInputService InputService; [Inject] public IInputService InputService;
[Inject] public Dictionary<CameraMode,CameraSettingsConfig> CameraSettingsConfigs;
} }
} }
@@ -14,6 +14,8 @@ namespace Code.Gameplay.Camera.CameraStates
public override void EnterState() public override void EnterState()
{ {
base.EnterState();
// В FirstPerson режиме дистанция всегда равна 0 // В FirstPerson режиме дистанция всегда равна 0
MinDistance = 0f; MinDistance = 0f;
MaxDistance = 0f; MaxDistance = 0f;
@@ -21,7 +23,7 @@ namespace Code.Gameplay.Camera.CameraStates
TargetDistance = 0f; TargetDistance = 0f;
CurrentDistance = 0f; CurrentDistance = 0f;
base.EnterState(); Camera.fieldOfView = Context.CameraSettingsConfigs[CameraMode.FirstPerson].fov;
} }
protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation) protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation)
@@ -2,14 +2,15 @@ using UnityEngine;
namespace Code.Gameplay.Camera.CameraStates namespace Code.Gameplay.Camera.CameraStates
{ {
public class UltraStaticState : BaseCameraState, ICameraState public class MainMenuState : BaseCameraState, ICameraState
{ {
public UltraStaticState(CameraStateMachineContext context) : base(context) { } public MainMenuState(CameraStateMachineContext context) : base(context) { }
public override void EnterState() public override void EnterState()
{ {
Debug.Log("Ultra static State Enter");
base.EnterState(); base.EnterState();
Camera.fieldOfView = Context.CameraSettingsConfigs[CameraMode.MainMenu].fov;
} }
protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation) protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation)
@@ -17,7 +18,7 @@ namespace Code.Gameplay.Camera.CameraStates
Camera.transform.position = FollowTransform.position; Camera.transform.position = FollowTransform.position;
} }
public CameraMode GetCameraMode() => CameraMode.UltraStatic; public CameraMode GetCameraMode() => CameraMode.MainMenu;
protected override void HandleRotationInput(float deltaTime, Vector3 rotationInput, protected override void HandleRotationInput(float deltaTime, Vector3 rotationInput,
out Quaternion targetRotation) out Quaternion targetRotation)
@@ -0,0 +1,21 @@
using UnityEngine;
namespace Code.Gameplay.Camera.CameraStates
{
public class NoneState : BaseCameraState, ICameraState
{
public NoneState(CameraStateMachineContext context) : base(context) { }
protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation)
{
}
public CameraMode GetCameraMode() => CameraMode.None;
protected override void HandleRotationInput(float deltaTime, Vector3 rotationInput,
out Quaternion targetRotation)
{
targetRotation = Quaternion.identity;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d8fa6119113f4f12a35365fd27387124
timeCreated: 1763847889
@@ -9,8 +9,9 @@ namespace Code.Gameplay.Camera.CameraStates
public override void EnterState() public override void EnterState()
{ {
Debug.Log("Static State Enter");
base.EnterState(); base.EnterState();
Camera.fieldOfView = Context.CameraSettingsConfigs[CameraMode.Static].fov;
} }
protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation) protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation)
@@ -14,13 +14,13 @@ namespace Code.Gameplay.Camera.CameraStates
public override void EnterState() public override void EnterState()
{ {
base.EnterState();
// Восстанавливаем исходные значения дистанции // Восстанавливаем исходные значения дистанции
MinDistance = OriginalMinDistance; MinDistance = OriginalMinDistance;
MaxDistance = OriginalMaxDistance; MaxDistance = OriginalMaxDistance;
DefaultDistance = OriginalDefaultDistance; DefaultDistance = OriginalDefaultDistance;
TargetDistance = DefaultDistance; TargetDistance = DefaultDistance;
base.EnterState();
} }
protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation) protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation)
@@ -1,147 +1,78 @@
using UnityEngine; using UnityEngine;
using KinematicCharacterController;
namespace Code.Gameplay.Character.Controller.Abilities namespace Code.Gameplay.Character.Controller.Abilities
{ {
/*
* Механика наклона персонажа при контакте со стеной
* Управляет визуальным наклоном модели в зависимости от высоты подъёма по стене
*/
public class WallLeanMechanic public class WallLeanMechanic
{ {
private readonly float _maxLeanAngle; private readonly float _leanAngle;
private readonly float _leanSharpness; private readonly float _threshold;
private readonly float _heightScale; private readonly float _smoothSpeed;
private Transform _characterTransform; private Transform _model;
private Vector3 _lastWallNormal;
private float _lastWallContactHeight;
private float _leanFactor;
private float _leanSign;
private Vector3 _leanDirection;
private Vector3 _lastWallContactPoint; public WallLeanMechanic(
private bool _isLeaningOnWall; float leanAngle,
private const float WallProximityThreshold = 0.6f; float threshold,
float smoothSpeed)
public WallLeanMechanic(float maxLeanAngle, float leanSharpness, float heightScale)
{ {
_maxLeanAngle = maxLeanAngle; _leanAngle = leanAngle;
_leanSharpness = leanSharpness; _threshold = threshold;
_heightScale = heightScale; _smoothSpeed = smoothSpeed;
} }
// Назначаем трансформ визуальной модели один раз public void SetModel(Transform model) => _model = model;
public void SetCharacterTransform(Transform transform)
{
_characterTransform = transform;
}
// Вызывается из OnMovementHit public void Update(bool isWallRunning, Vector3 wallNormal, Vector3 characterForward)
public void OnWallContact(KinematicCharacterMotor motor, Vector3 wallNormal, float contactHeight)
{ {
// Только если это действительно стена (не пол/потолок) if (!_model)
var dotWithUp = Vector3.Dot(wallNormal, motor.CharacterUp);
if (Mathf.Abs(dotWithUp) > 0.7f) // ~45°, можно настроить
return;
_lastWallNormal = wallNormal;
_lastWallContactHeight = contactHeight;
_lastWallContactPoint = motor.TransientPosition;
// Пересчитываем направление наклона
_leanDirection = -_lastWallNormal;
_leanDirection = Vector3.ProjectOnPlane(_leanDirection, motor.CharacterUp).normalized;
_leanSign = 1;
// what the fuck
if (_leanDirection.x != 0)
{ {
switch (_leanDirection.x) _model.localRotation = Quaternion.identity;
{
case -1 or 1 when motor.CharacterRight.x > 0.1f:
_leanSign = -1;
break;
case -1 or 1 when Mathf.Abs(motor.CharacterRight.x) < 0.1f:
_leanDirection = new Vector3(0, 0, -1);
break;
}
}
else
{
switch (_leanDirection.z)
{
case 1 when Mathf.Abs(motor.CharacterRight.z) < 0.1f:
_leanSign = -1;
break;
case -1 when motor.CharacterRight.z < -0.1f:
case 1 when motor.CharacterRight.z > 0.1f:
_leanDirection = new Vector3(-1, 0, 0);
break;
case -1 when motor.CharacterRight.z > 0.1f:
case 1 when motor.CharacterRight.z < -0.1f:
_leanDirection = new Vector3(1, 0, 0);
break;
}
}
_isLeaningOnWall = true;
}
// Обновление каждый кадр
public void UpdateLean(KinematicCharacterMotor motor, float deltaTime)
{
if (!_characterTransform) return;
var currentPosition = motor.TransientPosition;
var isStillNearWall = false;
if (_isLeaningOnWall && _lastWallNormal != Vector3.zero)
{
var toWall = currentPosition - _lastWallContactPoint;
var distanceToWallPlane = Mathf.Abs(Vector3.Dot(toWall, _lastWallNormal));
isStillNearWall = distanceToWallPlane <= WallProximityThreshold;
}
if (_isLeaningOnWall)
{
var isBelowContactHeight = currentPosition.y < _lastWallContactHeight;
if (!isStillNearWall || isBelowContactHeight)
{
_isLeaningOnWall = false;
}
else
{
var heightDelta = currentPosition.y - _lastWallContactHeight;
var targetLeanFactor = Mathf.Clamp01(heightDelta * _heightScale);
_leanFactor = Mathf.Lerp(_leanFactor, targetLeanFactor, 1f - Mathf.Exp(-_leanSharpness * deltaTime));
}
}
if (!_isLeaningOnWall)
{
_leanFactor = Mathf.Lerp(_leanFactor, 0f, 1f - Mathf.Exp(-_leanSharpness * deltaTime));
}
ApplyLeanRotation(motor);
}
private void ApplyLeanRotation(KinematicCharacterMotor motor)
{
if (_leanFactor <= 0.01f)
{
_characterTransform.localRotation = Quaternion.identity;
return; return;
} }
if (_leanDirection.sqrMagnitude < 0.01f) return; var targetRotation = Quaternion.identity;
var leanAngle = _leanSign * _leanFactor * _maxLeanAngle; if (isWallRunning)
var leanAxis = Vector3.Cross(motor.CharacterUp, _leanDirection); {
var leanRotation = Quaternion.AngleAxis(leanAngle, leanAxis); var n = new Vector3(wallNormal.x, 0f, wallNormal.z).normalized;
_characterTransform.localRotation = leanRotation; if (n.sqrMagnitude < 0.001f) n = Vector3.forward;
var bodyRight = Vector3.Cross(Vector3.up, characterForward).normalized;
var side = Vector3.Dot(n, bodyRight);
var front = Vector3.Dot(n, characterForward);
var axis = Vector3.right;
var angle = 0f;
if (side > _threshold)
{
axis = Vector3.forward;
angle = -_leanAngle;
}
else if (side < -_threshold)
{
axis = Vector3.forward;
angle = _leanAngle;
}
else if (front > _threshold)
{
axis = Vector3.right;
angle = -_leanAngle;
}
else if (front < -_threshold)
{
axis = Vector3.right;
angle = _leanAngle;
}
targetRotation = Quaternion.AngleAxis(angle, axis);
}
_model.localRotation = Quaternion.Slerp(
_model.localRotation,
targetRotation,
1f - Mathf.Exp(-_smoothSpeed * Time.deltaTime)
);
} }
} }
} }
@@ -0,0 +1,256 @@
using Code.Gameplay.Character.Controller.Input;
using Code.Gameplay.Character.Core;
using KinematicCharacterController;
using UnityEngine;
namespace Code.Gameplay.Character.Controller.Abilities
{
public class WallRunningMechanic
{
public float MaxWallRunTime = 1.75f;
public float WallRunSpeed = 8.0f;
public float WallRunAcceleration = 25.0f;
public float WallRunGravityScale = 0.25f;
public float WallDetachImpulse = 7.0f;
public float WallContactAngleThreshold = 45.0f;
private bool _isWallRunning;
private float _wallRunTimer;
private Vector3 _wallNormal;
private Vector3 _wallTangentRight;
private Vector3 _wallTangentUp;
private Vector3 _wallContactPoint;
private Vector3 _lastMoveInputInWallSpace;
private float _surfaceAngleCos;
private bool _canWallRun;
public float WallContactHeight { get; private set; }
public Vector3 WallRunStartForward { get; private set; }
private KinematicCharacterMotor _motor;
private CharacterInputHandler _inputHandler;
public bool IsWallRunning => _isWallRunning;
public void Initialize(KinematicCharacterMotor motor, CharacterInputHandler inputHandler)
{
_motor = motor;
_inputHandler = inputHandler;
_surfaceAngleCos = Mathf.Cos(Mathf.Deg2Rad * WallContactAngleThreshold);
}
public void Reset()
{
_isWallRunning = false;
_wallRunTimer = 0f;
_wallNormal = Vector3.zero;
}
public void Tick(float deltaTime)
{
if (_isWallRunning)
{
_wallRunTimer += deltaTime;
if (_wallRunTimer > MaxWallRunTime)
{
DetachFromWall(false);
}
}
}
public bool TryStartWallRun(Vector3 hitNormal, Vector3 hitPoint)
{
if (_isWallRunning) return false;
float dotWithUp = Vector3.Dot(hitNormal, _motor.CharacterUp);
bool isWall = Mathf.Abs(dotWithUp) < _surfaceAngleCos;
if (!isWall) return false;
if (_motor.GroundingStatus.IsStableOnGround) return false;
float velocityIntoWall = Vector3.Dot(_motor.Velocity, -hitNormal);
if (velocityIntoWall < 1f) return false;
StartWallRun(hitNormal, hitPoint);
return true;
}
private void StartWallRun(Vector3 wallNormal, Vector3 contactPoint)
{
_isWallRunning = true;
_wallRunTimer = 0f;
_wallNormal = wallNormal.normalized;
_wallContactPoint = contactPoint;
WallContactHeight = contactPoint.y;
//_wallTangentUp = Vector3.ProjectOnPlane(_motor.CharacterUp, _wallNormal).normalized;
if (_wallTangentUp.sqrMagnitude < 0.01f) _wallTangentUp = Vector3.up;
_wallTangentRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized;
_wallTangentUp = Vector3.Cross(_wallTangentRight, _wallNormal).normalized;
if (Vector3.Dot(_wallTangentUp, Vector3.up) < 0)
_wallTangentUp = -_wallTangentUp;
WallRunStartForward = _motor.CharacterForward;
_motor.ForceUnground();
}
public void HandleWallRun(ref Vector3 currentVelocity, CharacterInputs inputs, float deltaTime)
{
if (!_isWallRunning) return;
if (!IsWallStillPresent()) { DetachFromWall(false); return; }
Vector3 moveInputWorld = _inputHandler.MoveInputVector;
Vector3 moveInWallPlane = Vector3.ProjectOnPlane(moveInputWorld, _wallNormal);
Vector3 wsMove = Vector3.Project(moveInWallPlane, _wallTangentRight);
float ad = inputs.MoveAxisRight;
float sideDot = Vector3.Dot(_wallNormal, _motor.CharacterRight);
if (sideDot < 0)
ad = -ad;
Vector3 adMove = _wallTangentUp * (-ad);
Vector3 finalMove = wsMove + adMove;
finalMove = finalMove.normalized * moveInputWorld.magnitude;
Vector3 targetVelocity = finalMove * WallRunSpeed;
currentVelocity = Vector3.Lerp(
currentVelocity,
targetVelocity,
1f - Mathf.Exp(-WallRunAcceleration * deltaTime)
);
currentVelocity += Physics.gravity * (WallRunGravityScale * deltaTime);
float distanceToWall = Vector3.Dot(_motor.TransientPosition - _wallContactPoint, _wallNormal);
if (Mathf.Abs(distanceToWall) > 0.8f)
{
DetachFromWall(false);
return;
}
if (inputs.JumpPressed)
{
DetachFromWall(true);
}
}
private bool IsWallStillPresent()
{
if (_motor == null) return false;
Vector3 characterCenter = _motor.TransientPosition;
Vector3 characterForward = _motor.CharacterForward;
Vector3 characterRight = _motor.CharacterRight;
Vector3 characterUp = _motor.CharacterUp;
float checkDistance = 0.45f;
Vector3 rayOrigin = characterCenter + characterUp * 0.2f;
Vector3 rayDir = -_wallNormal;
Vector3[] offsets = {
Vector3.zero,
characterUp * 0.3f,
-characterUp * 0.3f,
characterRight * 0.2f,
-characterRight * 0.2f
};
foreach (var offset in offsets)
{
if (Physics.Raycast(rayOrigin + offset, rayDir, out RaycastHit hit, checkDistance,
_motor.CollidableLayers, QueryTriggerInteraction.Ignore))
{
float dotWithUp = Mathf.Abs(Vector3.Dot(hit.normal, characterUp));
if (dotWithUp < _surfaceAngleCos)
{
_wallNormal = hit.normal.normalized;
_wallContactPoint = hit.point;
/*_wallTangentUp = Vector3.ProjectOnPlane(_motor.CharacterUp, _wallNormal).normalized;
if (_wallTangentUp.sqrMagnitude < 0.01f) _wallTangentUp = Vector3.up;
_wallTangentRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized;
*/
return true;
}
}
}
return false;
}
public void DetachFromWall(bool withImpulse)
{
if (!_isWallRunning) return;
_isWallRunning = false;
if (withImpulse && _motor != null)
{
Vector3 detachImpulse = -_wallNormal * WallDetachImpulse;
_motor.BaseVelocity += detachImpulse;
}
}
public Vector3 GetWallNormal() => _wallNormal;
public bool TryGetWallTangents(out Vector3 up, out Vector3 right)
{
if (!_isWallRunning)
{
up = right = Vector3.zero;
return false;
}
up = _wallTangentUp;
right = _wallTangentRight;
return true;
}
public bool CanWallRun()
{
return !_isWallRunning && !_motor.GroundingStatus.IsStableOnGround;
}
public void PerformWallJump(CharacterInputs inputs, Quaternion cameraRotation)
{
if (!_isWallRunning || _motor == null) return;
_isWallRunning = false;
Vector3 wallAway = _wallNormal;
Vector3 wallUp = _wallTangentUp;
Vector3 playerForward = cameraRotation * Vector3.forward;
Vector3 forwardInWallPlane = Vector3.ProjectOnPlane(playerForward, _wallNormal).normalized;
if (forwardInWallPlane.sqrMagnitude < 0.01f) forwardInWallPlane = _wallTangentRight;
Vector3 jumpDirection =
forwardInWallPlane * 0.2f +
wallAway * 0.4f +
wallUp * 0.4f;
jumpDirection = jumpDirection.normalized;
float wallJumpForce = WallDetachImpulse * 2f;
Vector3 jumpImpulse = jumpDirection * wallJumpForce;
_motor.BaseVelocity += jumpImpulse;
_motor.SetPosition(_motor.TransientPosition + wallAway * 0.07f);
if (_motor.CharacterController is CustomCharacterController controller)
{
controller.BlockAbilities(0.12f);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4bef67aa9cf14753a63ea07eecc5ff67
timeCreated: 1764263348
@@ -28,44 +28,23 @@ namespace Code.Gameplay.Character.Controller
// Подсистемы // Подсистемы
private CharacterInputHandler _inputHandler; private CharacterInputHandler _inputHandler;
private SlideMechanic _slideMechanic; private SlideMechanic _slideMechanic;
private WallClimbMechanic _wallClimbMechanic; // private WallClimbMechanic _wallClimbMechanic;
private WallRunningMechanic _wallRunningMechanic;
private WallLeanMechanic _wallLeanMechanic; private WallLeanMechanic _wallLeanMechanic;
private GroundMovementSystem _groundMovement; private GroundMovementSystem _groundMovement;
private AirMovementSystem _airMovement; private AirMovementSystem _airMovement;
public bool IsSliding => _slideMechanic.IsSliding;
public bool IsWallRunning => _wallRunningMechanic.IsWallRunning;
private float abilityBlockTimer;
private bool isAbilitiesBlocked;
private CharacterInputs _lastInputs;
public void SetPosition(Vector3 position) => motor.SetPosition(position); public void SetPosition(Vector3 position) => motor.SetPosition(position);
public void SetRotation(Quaternion rotation) => motor.SetRotation(rotation); public void SetRotation(Quaternion rotation) => motor.SetRotation(rotation);
// Инициализация контроллера
// private void Awake()
// {
// // Инициализация подсистем с параметрами из конфига
// _inputHandler = new CharacterInputHandler(
// _characterConfig.sprintAngleThreshold
// );
// _slideMechanic = new SlideMechanic(
// _characterConfig.slideSpeed,
// _characterConfig.slideDuration,
// _characterConfig.slideCooldown
// );
// _wallClimbMechanic = new WallClimbMechanic(
// _characterConfig.wallClimbSpeedFactor,
// _characterConfig.minWallClimbSpeed,
// _characterConfig.wallDetectionAngleThreshold
// );
// _wallLeanMechanic = new WallLeanMechanic(
// _characterConfig.maxWallLeanAngle,
// _characterConfig.wallLeanSharpness,
// _characterConfig.wallLeanHeightScale
// );
// _groundMovement = new GroundMovementSystem(
// _characterConfig.maxStableMoveSpeed,
// _characterConfig.sprintMoveSpeed,
// _characterConfig.stableMovementSharpness,
// _characterConfig.jumpSpeed
// );
// _airMovement = new AirMovementSystem(_characterConfig.gravity);
// }
[Inject] [Inject]
private void Construct(CharacterConfig characterConfig) private void Construct(CharacterConfig characterConfig)
{ {
@@ -79,15 +58,24 @@ namespace Code.Gameplay.Character.Controller
_characterConfig.slideDuration, _characterConfig.slideDuration,
_characterConfig.slideCooldown _characterConfig.slideCooldown
); );
_wallClimbMechanic = new WallClimbMechanic( // _wallClimbMechanic = new WallClimbMechanic(
_characterConfig.wallClimbSpeedFactor, // _characterConfig.wallClimbSpeedFactor,
_characterConfig.minWallClimbSpeed, // _characterConfig.minWallClimbSpeed,
_characterConfig.wallDetectionAngleThreshold // _characterConfig.wallDetectionAngleThreshold
); // );
_wallRunningMechanic = new WallRunningMechanic();
// Настройка параметров WallRunningMechanic (можно вынести в конфиг позже)
_wallRunningMechanic.MaxWallRunTime = 5f;
_wallRunningMechanic.WallRunSpeed = 15.0f;
_wallRunningMechanic.WallRunAcceleration = 25.0f;
_wallRunningMechanic.WallRunGravityScale = 0.25f;
_wallRunningMechanic.WallDetachImpulse = 7.0f;
_wallRunningMechanic.WallContactAngleThreshold = 45.0f;
_wallLeanMechanic = new WallLeanMechanic( _wallLeanMechanic = new WallLeanMechanic(
_characterConfig.maxWallLeanAngle, _characterConfig.leanAngle,
_characterConfig.wallLeanSharpness, _characterConfig.leanThreshold,
_characterConfig.wallLeanHeightScale _characterConfig.leanSmoothSpeed
); );
_groundMovement = new GroundMovementSystem( _groundMovement = new GroundMovementSystem(
_characterConfig.maxStableMoveSpeed, _characterConfig.maxStableMoveSpeed,
@@ -101,31 +89,24 @@ namespace Code.Gameplay.Character.Controller
motor.CharacterController = this; // Регистрация этого контроллера в движке motor.CharacterController = this; // Регистрация этого контроллера в движке
_inputHandler.FirstPersonCameraRotation = _inputHandler.FirstPersonCameraRotation =
motor.Transform.rotation; // Начальное вращение совпадает с персонажемс motor.Transform.rotation; // Начальное вращение совпадает с персонажемс
_wallLeanMechanic.SetCharacterTransform(characterTransform); _wallLeanMechanic.SetModel(characterTransform);
_wallRunningMechanic.Initialize(motor, _inputHandler);
} }
// Текущий режим камеры для адаптации поведения // Текущий режим камеры для адаптации поведения
private CameraMode _currentCameraMode = CameraMode.FirstPerson; private CameraMode _currentCameraMode = CameraMode.FirstPerson;
// Инициализация контроллера
// private void Start()
// {
// motor.CharacterController = this; // Регистрация этого контроллера в движке
// _inputHandler.FirstPersonCameraRotation =
// motor.Transform.rotation; // Начальное вращение совпадает с персонажемс
// _wallLeanMechanic.SetCharacterTransform(characterTransform);
// }
// Обработка и преобразование входных данных от персонажа
public void SetInputs(ref CharacterInputs inputs) public void SetInputs(ref CharacterInputs inputs)
{ {
// Обновляем таймер перезарядки слайда _lastInputs = inputs;
// Обновляем таймеры механик
_slideMechanic.TickCooldown(Time.deltaTime); _slideMechanic.TickCooldown(Time.deltaTime);
_wallClimbMechanic.TickCooldown(Time.deltaTime); // _wallClimbMechanic.TickCooldown(Time.deltaTime);
_wallRunningMechanic.Tick(Time.deltaTime);
// Если активен слайд - игнорируем весь остальной ввод // Если активен слайд - игнорируем весь остальной ввод
if (_slideMechanic.IsSliding) if (_slideMechanic.IsSliding)
{ {
// Во время слайда не обрабатываем движение, прыжки и спринт
_inputHandler.MoveInputVector = Vector3.zero; _inputHandler.MoveInputVector = Vector3.zero;
_inputHandler.LookInputVector = Vector3.one; _inputHandler.LookInputVector = Vector3.one;
_inputHandler.IsSprinting = false; _inputHandler.IsSprinting = false;
@@ -133,149 +114,160 @@ namespace Code.Gameplay.Character.Controller
return; return;
} }
// Сохраняем текущий режим камеры
_currentCameraMode = inputs.CameraMode; _currentCameraMode = inputs.CameraMode;
if (isAbilitiesBlocked)
{
abilityBlockTimer -= Time.deltaTime;
if (abilityBlockTimer <= 0)
{
isAbilitiesBlocked = false;
}
else
{
inputs.SprintPressed = false;
inputs.SlidePressed = false;
}
}
// Обработка слайда // Обработка слайда
_slideMechanic.RequestSlide(inputs.SlidePressed); _slideMechanic.RequestSlide(inputs.SlidePressed);
// Обработка остального ввода // Обработка остального ввода
_inputHandler.ProcessInputs(ref inputs, motor); _inputHandler.ProcessInputs(ref inputs, motor);
// Передаём запрос прыжка напрямую в систему движения по земле // Передаём запрос прыжка
_groundMovement.RequestJump(inputs.JumpPressed); _groundMovement.RequestJump(inputs.JumpPressed);
} }
// Обновление вращения персонажа на основе входных данных
public void UpdateRotation(ref Quaternion currentRotation, float deltaTime) public void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{ {
if (_slideMechanic.IsSliding || _wallClimbMechanic.IsClimbingActive) return; if (_slideMechanic.IsSliding) return;
switch (_currentCameraMode) switch (_currentCameraMode)
{ {
// В FirstPerson режиме применяем вращение камеры напрямую к персонажу (только горизонталь)
case CameraMode.FirstPerson when _inputHandler.ShouldApplyCameraRotation: case CameraMode.FirstPerson when _inputHandler.ShouldApplyCameraRotation:
// Немедленно применяем горизонтальное вращение камеры к персонажу
currentRotation = _inputHandler.FirstPersonCameraRotation; currentRotation = _inputHandler.FirstPersonCameraRotation;
break; break;
// В ThirdPerson режиме плавно поворачиваем персонаж в направлении движения
case CameraMode.ThirdPerson: case CameraMode.ThirdPerson:
{ {
// Вращение применяется только если есть направление движения и разрешена резкость вращения
if (_inputHandler.LookInputVector.sqrMagnitude > 0 && _characterConfig.orientationSharpness > 0) if (_inputHandler.LookInputVector.sqrMagnitude > 0 && _characterConfig.orientationSharpness > 0)
{ {
// Плавная интерполяция текущего направления к целевому с экспоненциальным сглаживанием
var smoothedLookInputDirection = Vector3.Slerp( var smoothedLookInputDirection = Vector3.Slerp(
motor.CharacterForward, motor.CharacterForward,
_inputHandler.LookInputVector, _inputHandler.LookInputVector,
1f - Mathf.Exp(-_characterConfig.orientationSharpness * deltaTime) 1f - Mathf.Exp(-_characterConfig.orientationSharpness * deltaTime)
).normalized; ).normalized;
// Установка нового вращения смотря в сглаженном направлении
currentRotation = Quaternion.LookRotation(smoothedLookInputDirection, motor.CharacterUp); currentRotation = Quaternion.LookRotation(smoothedLookInputDirection, motor.CharacterUp);
} }
break; break;
} }
// Заготовка на будущее (если понадобится)
case CameraMode.Static: case CameraMode.Static:
case CameraMode.Cinematic: case CameraMode.Cinematic:
case CameraMode.Bogdan: // Скорее всего юзлес, но пускай будет
default: default:
break; break;
} }
} }
// Обновление скорости движения персонажа
public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime) public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{ {
// Если во время слайда запрошен прыжок — прерываем слайд
if (_slideMechanic.IsSliding && _groundMovement.JumpRequested) if (_slideMechanic.IsSliding && _groundMovement.JumpRequested)
{ {
_slideMechanic.EndSlide(); _slideMechanic.EndSlide();
} }
// Проверяем запрос слайда и запускаем его если можно
if (!_slideMechanic.IsSliding) if (!_slideMechanic.IsSliding)
{ {
_slideMechanic.TryStartSlide(motor, _currentCameraMode, _inputHandler.FirstPersonCameraRotation, _slideMechanic.TryStartSlide(motor, _currentCameraMode, _inputHandler.FirstPersonCameraRotation,
_inputHandler.LookInputVector); _inputHandler.LookInputVector);
} }
// Если активен слайд - обрабатываем отдельно
else else
{ {
_slideMechanic.HandleSlide(ref currentVelocity, deltaTime, motor); _slideMechanic.HandleSlide(ref currentVelocity, deltaTime, motor);
return; return;
} }
// === WALL RUNNING: обработка состояния ===
if (_wallRunningMechanic.IsWallRunning)
{
// 🔄 Передаём актуальные данные для walljump
if (_groundMovement.JumpRequested) // или _lastInputs.JumpPressed — зависит от логики
{
_wallRunningMechanic.PerformWallJump(_lastInputs, _inputHandler.FirstPersonCameraRotation);
_groundMovement.CancelJumpRequest(); // сброс, чтобы не сработало дважды
}
else
{
_wallRunningMechanic.HandleWallRun(ref currentVelocity, _lastInputs, deltaTime);
}
return;
}
// Если персонаж находится на земле // Если персонаж находится на земле
if (motor.GroundingStatus.IsStableOnGround) if (motor.GroundingStatus.IsStableOnGround)
{ {
_groundMovement.UpdateVelocity(ref currentVelocity, _inputHandler, motor, deltaTime); _groundMovement.UpdateVelocity(ref currentVelocity, _inputHandler, motor, deltaTime);
} }
// Если персонаж не находится на земле // Если персонаж не на земле — стандартное воздушное движение
else else
{ {
_airMovement.UpdateVelocity(ref currentVelocity, deltaTime); _airMovement.UpdateVelocity(ref currentVelocity, deltaTime);
// Отменяем прыжок (фикс проблемы с двойным прыжком после приземления)
_groundMovement.CancelJumpRequest(); _groundMovement.CancelJumpRequest();
} }
} }
// Метод вызывается перед обновлением персонажа (можно использовать для подготовки) public void BlockAbilities(float duration)
{
isAbilitiesBlocked = true;
abilityBlockTimer = duration;
}
public void BeforeCharacterUpdate(float deltaTime) public void BeforeCharacterUpdate(float deltaTime)
{ {
} }
// Метод вызывается после обработки столкновения с землей
public void PostGroundingUpdate(float deltaTime) public void PostGroundingUpdate(float deltaTime)
{ {
// Если во время слайда персонаж оторвался от земли - заканчиваем слайд
if (_slideMechanic.IsSliding && !motor.GroundingStatus.IsStableOnGround) if (_slideMechanic.IsSliding && !motor.GroundingStatus.IsStableOnGround)
{ {
_slideMechanic.EndSlide(); _slideMechanic.EndSlide();
} }
if (motor.GroundingStatus.IsStableOnGround)
{
_wallClimbMechanic.ResetWallClimbSession();
}
} }
// Метод вызывается после обновления персонажа
public void AfterCharacterUpdate(float deltaTime) public void AfterCharacterUpdate(float deltaTime)
{ {
_wallLeanMechanic.UpdateLean(motor, deltaTime); _wallLeanMechanic.Update(
_wallRunningMechanic.IsWallRunning,
_wallRunningMechanic.GetWallNormal(),
motor.CharacterForward
);
} }
// Проверка валидности коллайдера для обработки столкновений
public bool IsColliderValidForCollisions(Collider coll) public bool IsColliderValidForCollisions(Collider coll)
{ {
return true; // Все коллайдеры считаются валидными для столкновений return true;
} }
// Обработка столкновения с землей (вызывается при приземлении)
public void OnGroundHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, public void OnGroundHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint,
ref HitStabilityReport hitStabilityReport) ref HitStabilityReport hitStabilityReport)
{ {
} }
// Обработка столкновения при движении (вызывается при ударе о препятствие)
public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint,
ref HitStabilityReport hitStabilityReport) ref HitStabilityReport hitStabilityReport)
{ {
if (_wallClimbMechanic.TryClimbWall(motor, hitNormal)) if (_wallRunningMechanic.TryStartWallRun(hitNormal, hitPoint))
_wallLeanMechanic.OnWallContact(motor, hitNormal, motor.TransientPosition.y); {
}
} }
// Обработка отчета о стабильности столкновения
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint,
Vector3 atCharacterPosition, Vector3 atCharacterPosition,
Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport) Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
{ {
} }
// Обработка дискретных столкновений (для триггеров и специальных коллайдеров)
public void OnDiscreteCollisionDetected(Collider hitCollider) public void OnDiscreteCollisionDetected(Collider hitCollider)
{ {
} }
@@ -5,19 +5,44 @@ namespace Code.Gameplay.Door
{ {
public class CinematicDoorOpening public class CinematicDoorOpening
{ {
public DamnDoor DamnDoor; private MainMenuDoor _mainMenuDoor;
public Light DoorLight; private Light _doorLight;
public void PreOpen(bool start) public CinematicDoorOpening(MainMenuDoor mainMenuDoor, Light doorLight)
{ {
DoorLight.DOColor(start ? Color.yellow : Color.red, 0.2f); _mainMenuDoor = mainMenuDoor;
_doorLight = doorLight;
}
public void PreOpen(int type)
{
switch (type)
{
case 0:
_doorLight.DOColor(Color.green, 0.2f);
break;
case 1:
_doorLight.DOColor(Color.yellow, 0.2f);
break;
case 2:
_doorLight.DOColor(Color.red, 0.2f);
break;
}
DamnDoor.Petlya.DORotate(new Vector3(0, -40, 0), 1f); _mainMenuDoor.Petlya.DORotate(new Vector3(0, -10, 0), 1f);
} }
public void Close() public void Close()
{ {
DamnDoor.Petlya.DORotate(new Vector3(0, 0, 0), 1f); _mainMenuDoor.Petlya.DORotate(new Vector3(0, 0, 0), 1f);
} }
} }
[System.Serializable]
public class MainMenuUIVariables
{
public MainMenuDoor MainMenuDoor;
public Light DoorLight;
public Color ButtonDefaultColor;
public Color ButtonHoverColor;
}
} }
@@ -2,7 +2,7 @@ using UnityEngine;
namespace Code.Gameplay.Door namespace Code.Gameplay.Door
{ {
public class DamnDoor : MonoBehaviour public class MainMenuDoor : MonoBehaviour
{ {
[SerializeField] Rigidbody rigidbody; [SerializeField] Rigidbody rigidbody;
[SerializeField] private Transform petlya; [SerializeField] private Transform petlya;
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3b36108558824d748620db29707cd2b1
timeCreated: 1763228467
@@ -0,0 +1,203 @@
using UnityEngine;
using UnityEngine.AI;
using Code.Gameplay.Player;
using Code.Gameplay.Character.Controller;
using Code.Gameplay.Character.Core;
using Reflex.Attributes;
using Reflex.Core;
using Reflex.Injectors;
namespace Code.Gameplay.Enemies
{
public abstract class BaseEnemy : MonoBehaviour
{
[SerializeField] protected NavMeshAgent agent;
[SerializeField] protected Collider enemyCollider;
protected int Damage;
protected float AttackCooldown;
protected float DetectionRange;
protected float AttackRange;
protected float Speed;
protected bool IsStunnable;
protected float StunDuration;
protected bool UsesIdleMovement;
protected float IdleWanderRadius;
protected float IdleMoveDurationMin;
protected float IdleMoveDurationMax;
protected float IdlePauseDurationMin;
protected float IdlePauseDurationMax;
private bool _isStunned;
private float _currentStunTimer;
private bool _canAttack = true;
private float _attackCooldownTimer;
private enum IdleState { Moving, Paused }
private IdleState _currentIdleState = IdleState.Paused;
private float _idleTimer;
private Vector3 _idleTargetPosition;
[Inject] private IPlayerProvider _playerProvider;
protected CharacterProvider Player => _playerProvider?.Character;
protected PlayerHealth PlayerHealth => _playerProvider?.Health;
protected CustomCharacterController PlayerController => Player?.CustomCharacterController;
protected virtual void Awake()
{
if (_playerProvider == null)
{
AttributeInjector.Inject(this, Container.ProjectContainer);
}
}
protected virtual void Update()
{
if (!Player) return;
HandleStun();
HandleAttackCooldown();
if (_isStunned) return;
HandleMovement();
HandleAttack();
}
protected virtual void HandleMovement()
{
if (!Player || !agent) return;
var distanceToPlayer = Vector3.Distance(transform.position, Player.transform.position);
if (distanceToPlayer <= DetectionRange)
{
agent.isStopped = false;
agent.SetDestination(Player.transform.position);
}
else if (UsesIdleMovement)
{
HandleIdleMovement();
}
else
{
agent.isStopped = true;
}
}
protected virtual void HandleIdleMovement()
{
_idleTimer -= Time.deltaTime;
switch (_currentIdleState)
{
case IdleState.Paused:
if (_idleTimer <= 0f)
{
// Start moving to a new random point
_currentIdleState = IdleState.Moving;
SetNewIdleDestination();
_idleTimer = Random.Range(IdleMoveDurationMin, IdleMoveDurationMax);
agent.isStopped = false;
}
break;
case IdleState.Moving:
if (_idleTimer <= 0f || HasReachedIdleDestination())
{
// Stop and pause
_currentIdleState = IdleState.Paused;
_idleTimer = Random.Range(IdlePauseDurationMin, IdlePauseDurationMax);
agent.isStopped = true;
}
break;
}
}
private void SetNewIdleDestination()
{
Vector3 randomPoint = transform.position + Random.insideUnitSphere * IdleWanderRadius;
NavMeshHit hit;
if (NavMesh.SamplePosition(randomPoint, out hit, 3f, agent.areaMask))
{
_idleTargetPosition = hit.position;
agent.SetDestination(_idleTargetPosition);
}
else
{
// Fallback: stay
_idleTargetPosition = transform.position;
}
}
private bool HasReachedIdleDestination()
{
return agent.remainingDistance <= agent.stoppingDistance;
}
protected virtual void HandleAttack() { }
protected virtual void HandleStun()
{
if (!_isStunned || !IsStunnable) return;
_currentStunTimer -= Time.deltaTime;
if (!(_currentStunTimer <= 0)) return;
_isStunned = false;
agent.isStopped = false;
enemyCollider.enabled = true;
}
protected virtual void ApplyStun()
{
_isStunned = true;
_currentStunTimer = StunDuration;
agent.isStopped = true;
enemyCollider.enabled = false;
}
protected virtual void HandleAttackCooldown()
{
if (!_canAttack)
{
_attackCooldownTimer -= Time.deltaTime;
if (_attackCooldownTimer <= 0)
{
_canAttack = true;
}
}
}
protected virtual void DealDamage()
{
if (!_canAttack || PlayerHealth == null) return;
PlayerHealth.GetDamage(Damage);
ApplyEffectsToPlayer();
_canAttack = false;
_attackCooldownTimer = AttackCooldown;
}
protected virtual void ApplyEffectsToPlayer()
{
if (PlayerController)
PlayerController.BlockAbilities(3f);
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(transform.position, DetectionRange);
Gizmos.color = new Color(0.3f, 0.3f, 0.8f, 0.4f);
Gizmos.DrawSphere(transform.position, AttackRange);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 691006ba0f69448787db8f350ecf32f4
timeCreated: 1763228480
@@ -0,0 +1,55 @@
using Code.Configs;
using Reflex.Attributes;
using UnityEngine;
using UnityEngine.AI;
namespace Code.Gameplay.Enemies
{
public class Cockroach : BaseEnemy
{
[Inject]
private void Construct(CockroachConfig cockroachConfig)
{
Damage = cockroachConfig.damage;
AttackCooldown = cockroachConfig.attackCooldown;
DetectionRange = cockroachConfig.detectionRange;
AttackRange = cockroachConfig.attackRange;
Speed = cockroachConfig.speed;
IsStunnable = cockroachConfig.isStunnable;
StunDuration = cockroachConfig.stunDuration;
UsesIdleMovement = cockroachConfig.usesIdleMovement;
IdleWanderRadius = cockroachConfig.idleWanderRadius;
IdleMoveDurationMin = cockroachConfig.idleMoveDurationMin;
IdleMoveDurationMax = cockroachConfig.idleMoveDurationMax;
IdleMoveDurationMin = cockroachConfig.idleMoveDurationMin;
IdleMoveDurationMax = cockroachConfig.idleMoveDurationMax;
IdlePauseDurationMin = cockroachConfig.idlePauseDurationMin;
IdlePauseDurationMax = cockroachConfig.idlePauseDurationMax;
}
protected override void Awake()
{
base.Awake();
if (agent == null) return;
var walkableArea = NavMesh.GetAreaFromName("Cockroach");
agent.areaMask = 1 << walkableArea;
}
protected override void HandleAttack()
{
if (!Player) return;
var distance = Vector3.Distance(transform.position, Player.transform.position);
if (!(distance <= AttackRange)) return;
DealDamage();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 119c0b675c944ed7ada6cdc8db4b04aa
timeCreated: 1763228933
@@ -0,0 +1,58 @@
using Code.Configs;
using Reflex.Attributes;
using UnityEngine;
using UnityEngine.AI;
namespace Code.Gameplay.Enemies
{
public class DrunkenGrandpa : BaseEnemy
{
[Inject]
private void Construct(DrunkenGrandpaConfig drunkenGrandpaConfig)
{
Damage = drunkenGrandpaConfig.damage;
AttackCooldown = drunkenGrandpaConfig.attackCooldown;
DetectionRange = drunkenGrandpaConfig.detectionRange;
AttackRange = drunkenGrandpaConfig.attackRange;
Speed = drunkenGrandpaConfig.speed;
IsStunnable = drunkenGrandpaConfig.isStunnable;
StunDuration = drunkenGrandpaConfig.stunDuration;
UsesIdleMovement = drunkenGrandpaConfig.usesIdleMovement;
IdleWanderRadius = drunkenGrandpaConfig.idleWanderRadius;
IdleMoveDurationMin = drunkenGrandpaConfig.idleMoveDurationMin;
IdleMoveDurationMax = drunkenGrandpaConfig.idleMoveDurationMax;
IdleMoveDurationMin = drunkenGrandpaConfig.idleMoveDurationMin;
IdleMoveDurationMax = drunkenGrandpaConfig.idleMoveDurationMax;
IdlePauseDurationMin = drunkenGrandpaConfig.idlePauseDurationMin;
IdlePauseDurationMax = drunkenGrandpaConfig.idlePauseDurationMax;
}
protected override void Awake()
{
base.Awake();
if (agent == null) return;
var walkableArea = NavMesh.GetAreaFromName("Walkable");
agent.areaMask = 1 << walkableArea;
}
protected override void HandleAttack()
{
if (!Player) return;
var distance = Vector3.Distance(transform.position, Player.transform.position);
if (!(distance <= AttackRange)) return;
if (Player.CustomCharacterController.IsSliding)
ApplyStun();
else
DealDamage();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e77164596e249bca15e09cdfc9ddac2
timeCreated: 1763228910
@@ -0,0 +1,55 @@
using System;
using Code.Configs;
using Cysharp.Threading.Tasks;
using Reflex.Attributes;
using Reflex.Core;
using Reflex.Injectors;
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace Code.Gameplay.Enemies
{
public class EnemyFactory : IEnemyFactory
{
[Inject] private FogConfig _fogConfig;
[Inject] private DrunkenGrandpaConfig _drunkenGrandpaConfig;
[Inject] private CockroachConfig _cockroachConfig;
public async UniTask<BaseEnemy> CreateEnemy(EnemyType type, Vector3? position = null, Quaternion? rotation = null)
{
var prefabRef = GetEnemyPrefab(type);
var enemyObj = await Addressables
.InstantiateAsync(
prefabRef,
position: position ?? Vector3.zero,
rotation: rotation ?? Quaternion.identity
)
.ToUniTask();
AttributeInjector.Inject(enemyObj, Container.ProjectContainer);
return enemyObj.GetComponent<BaseEnemy>();
}
public void ReleaseEnemy(BaseEnemy enemy)
{
if (enemy == null) return;
enemy.gameObject.SetActive(false);
Addressables.ReleaseInstance(enemy.gameObject);
}
private AssetReference GetEnemyPrefab(EnemyType type)
{
return type switch
{
EnemyType.Fog => _fogConfig.fogPrefab,
EnemyType.DrunkenGrandpa => _drunkenGrandpaConfig.drunkenGrandpaPrefab,
EnemyType.Cockroach => _cockroachConfig.cockroachPrefab,
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2f9328c54d334b0c85ce44b0aa7add44
timeCreated: 1764168610
@@ -0,0 +1,84 @@
using Code.Gameplay.Player;
using Cysharp.Threading.Tasks;
using Reflex.Attributes;
using Reflex.Core;
using Reflex.Injectors;
using UnityEngine;
namespace Code.Gameplay.Enemies
{
public class EnemySpawner : MonoBehaviour
{
[SerializeField] private EnemyType spawnerType;
[SerializeField] private float spawnRadius = 40f;
[SerializeField] private float despawnRadius = 50f;
[Inject] private IPlayerProvider _playerProvider;
[Inject] private IEnemyFactory _enemyFactory;
private BaseEnemy _spawnedEnemy;
private bool _isSpawning;
private bool _isPlayerInRange;
private void Awake()
{
if (_playerProvider == null || _enemyFactory == null)
{
AttributeInjector.Inject(this, Container.ProjectContainer);
}
}
private void Update()
{
if (!_playerProvider?.Character)
return;
var distanceToPlayer = Vector3.Distance(transform.position, _playerProvider.Character.transform.position);
var wasInRange = _isPlayerInRange;
_isPlayerInRange = distanceToPlayer <= spawnRadius;
switch (_isPlayerInRange)
{
case true when !_spawnedEnemy && !_isSpawning && !wasInRange:
Spawn().Forget();
break;
case false when _spawnedEnemy && distanceToPlayer > despawnRadius:
Despawn();
break;
}
}
private async UniTask Spawn()
{
if (_spawnedEnemy || _isSpawning) return;
_isSpawning = true;
var enemy = await _enemyFactory.CreateEnemy(
spawnerType,
transform.position,
transform.rotation
);
_spawnedEnemy = enemy;
_isSpawning = false;
}
private void Despawn()
{
if (_spawnedEnemy == null) return;
_enemyFactory.ReleaseEnemy(_spawnedEnemy);
_spawnedEnemy = null;
}
private void OnDrawGizmosSelected()
{
Gizmos.color = new Color(0, 1, 0, 0.5f);
Gizmos.DrawSphere(transform.position, spawnRadius);
Gizmos.color = new Color(1, 0, 0, 0.2f);
Gizmos.DrawSphere(transform.position, despawnRadius);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e87d9529de45409781ec5bfc356518c7
timeCreated: 1764225519
@@ -0,0 +1,9 @@
namespace Code.Gameplay.Enemies
{
public enum EnemyType
{
Fog,
DrunkenGrandpa,
Cockroach
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5430cad1a8c44516944259a428de641e
timeCreated: 1764168839
@@ -0,0 +1,68 @@
using Code.Configs;
using Reflex.Attributes;
using UnityEngine;
namespace Code.Gameplay.Enemies
{
public class Fog : BaseEnemy
{
[Inject]
private void Construct(FogConfig fogConfig)
{
Damage = fogConfig.damage;
AttackCooldown = fogConfig.attackCooldown;
DetectionRange = fogConfig.detectionRange;
AttackRange = fogConfig.attackRange;
Speed = fogConfig.speed;
IsStunnable = fogConfig.isStunnable;
StunDuration = fogConfig.stunDuration;
UsesIdleMovement = fogConfig.usesIdleMovement;
IdleWanderRadius = fogConfig.idleWanderRadius;
IdleMoveDurationMin = fogConfig.idleMoveDurationMin;
IdleMoveDurationMax = fogConfig.idleMoveDurationMax;
IdleMoveDurationMin = fogConfig.idleMoveDurationMin;
IdleMoveDurationMax = fogConfig.idleMoveDurationMax;
IdlePauseDurationMin = fogConfig.idlePauseDurationMin;
IdlePauseDurationMax = fogConfig.idlePauseDurationMax;
}
protected override void Awake()
{
base.Awake();
if (agent == null) return;
agent.enabled = false;
Destroy(agent);
}
protected override void HandleMovement()
{
if (!Player) return;
var direction = (Player.transform.position - transform.position).normalized;
transform.position += direction * (Speed * Time.deltaTime);
}
protected override void HandleAttack()
{
if (!Player || PlayerHealth == null) return;
var distance = Vector3.Distance(transform.position, Player.transform.position);
if (!(distance < AttackRange)) return;
PlayerHealth.GetDamage(PlayerHealth.MaxHealthPoints);
base.ApplyEffectsToPlayer();
}
private void OnDrawGizmosSelected()
{
Gizmos.color = new Color(0.3f, 0.3f, 0.8f, 0.4f);
Gizmos.DrawSphere(transform.position, AttackRange);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4c70370426e4457aa9a56c77689158ff
timeCreated: 1763228955
@@ -0,0 +1,11 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace Code.Gameplay.Enemies
{
public interface IEnemyFactory
{
UniTask<BaseEnemy> CreateEnemy(EnemyType type, Vector3? position, Quaternion? rotation);
void ReleaseEnemy(BaseEnemy enemy);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 68eb3027a07042b991aa1893ddd14368
timeCreated: 1764168497
@@ -2,19 +2,24 @@ namespace Code.Gameplay.Orders.Order
{ {
public interface IOrderService public interface IOrderService
{ {
delegate void OrderFailureDelegate();
delegate void OrderProgressDelegate(float time); delegate void OrderProgressDelegate(float time);
event OrderProgressDelegate OnOrderGiven; event OrderProgressDelegate OnOrderGiven;
event OrderProgressDelegate OnOrderRecieved; event OrderProgressDelegate OnOrderRecieved;
event OrderFailureDelegate OnOrderFailure;
delegate void OrderDeliveredDelegate(int score, float time); delegate void OrderDeliveredDelegate(int score, float time);
event OrderDeliveredDelegate OnOrderDelivered; event OrderDeliveredDelegate OnOrderDelivered;
OrderState OrderState { get; } OrderState OrderState { get; }
float DeliveryTimer { get; }
void RoomPassed(); void RoomPassed();
float DecreaseTimer(float delta); void DecreaseTimer(float delta);
void OrderGive(); void OrderGive();
void OrderRecieved(); void OrderRecieved();
void OrderDelivered(); void OrderDelivered();
void OrderFailed();
} }
} }
@@ -1,9 +0,0 @@
namespace Code.Gameplay.Orders.Order
{
public interface IScoreService
{
int Score { get; }
void IncreaseScore(int amount);
void DecreaseScore(int amount);
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 64668c9ef4c441af84c635365cfbb2de
timeCreated: 1763559523
@@ -11,7 +11,7 @@ namespace Code.Gameplay.Orders.Order
{ {
[Inject] private IOrderService _orderService; [Inject] private IOrderService _orderService;
public Rigidbody rigidbody; public Rigidbody rigidbody;
[SerializeField] private bool _pickupMode; private bool _pickupMode;
// [SerializeField] private AnimationCurve debugCurve; // [SerializeField] private AnimationCurve debugCurve;
//DEBUG //DEBUG
@@ -41,9 +41,7 @@ namespace Code.Gameplay.Orders.Order
public async UniTaskVoid SwitchMode() public async UniTaskVoid SwitchMode()
{ {
Debug.LogWarning("Activating");
await UniTask.WaitForSeconds(_activationTime); await UniTask.WaitForSeconds(_activationTime);
Debug.LogWarning("Available");
_pickupMode = true; _pickupMode = true;
rigidbody.isKinematic = true; rigidbody.isKinematic = true;
_capturedPosition = transform.position; _capturedPosition = transform.position;
@@ -62,7 +60,6 @@ namespace Code.Gameplay.Orders.Order
if (_timeForMoving > _curveLength) _timeForMoving = 0; if (_timeForMoving > _curveLength) _timeForMoving = 0;
transform.position = _capturedPosition + new Vector3(0, _floatingCurve.Evaluate(_timeForMoving), 0); transform.position = _capturedPosition + new Vector3(0, _floatingCurve.Evaluate(_timeForMoving), 0);
Debug.Log(transform.position);
} }
} }
} }
@@ -1,6 +1,6 @@
using Code.Audio;
using Code.Configs; using Code.Configs;
using UnityEngine; using UnityEngine;
using Random = UnityEngine.Random;
namespace Code.Gameplay.Orders.Order namespace Code.Gameplay.Orders.Order
{ {
@@ -9,11 +9,13 @@ namespace Code.Gameplay.Orders.Order
public event IOrderService.OrderProgressDelegate OnOrderGiven; public event IOrderService.OrderProgressDelegate OnOrderGiven;
public event IOrderService.OrderProgressDelegate OnOrderRecieved; public event IOrderService.OrderProgressDelegate OnOrderRecieved;
public event IOrderService.OrderDeliveredDelegate OnOrderDelivered; public event IOrderService.OrderDeliveredDelegate OnOrderDelivered;
public event IOrderService.OrderFailureDelegate OnOrderFailure;
private OrderState _orderState; private OrderState _orderState;
public OrderState OrderState => _orderState; public OrderState OrderState => _orderState;
private float _deliveryTimer = 0.0f; private float _deliveryTimer;
public float DeliveryTimer => _deliveryTimer;
private Vector2 _timeBounds; private Vector2 _timeBounds;
private float _koefRooms, _koefOvertime; private float _koefRooms, _koefOvertime;
@@ -30,11 +32,9 @@ namespace Code.Gameplay.Orders.Order
} }
public void RoomPassed() => _roomsPassed++; public void RoomPassed() => _roomsPassed++;
public float DecreaseTimer(float delta) public void DecreaseTimer(float delta) => _deliveryTimer -= delta;
{
return _deliveryTimer -= delta;
}
// Вызываются триггерами
public void OrderGive() public void OrderGive()
{ {
_orderState = OrderState.Given; _orderState = OrderState.Given;
@@ -49,9 +49,16 @@ namespace Code.Gameplay.Orders.Order
_orderState = OrderState.Recieved; _orderState = OrderState.Recieved;
OnOrderRecieved?.Invoke(_deliveryTimer); OnOrderRecieved?.Invoke(_deliveryTimer);
} }
public void OrderFailed() => OnOrderFailure?.Invoke();
public void OrderDelivered() public void OrderDelivered()
{ {
if (_orderState != OrderState.Recieved)
{
OnOrderFailure?.Invoke();
return;
}
_orderState = OrderState.Delivered; _orderState = OrderState.Delivered;
int score = (int)(_koefRooms * _roomsPassed + _deliveryTimer * _koefOvertime); int score = (int)(_koefRooms * _roomsPassed + _deliveryTimer * _koefOvertime);
@@ -1,11 +0,0 @@
namespace Code.Gameplay.Orders.Order
{
public class ScoreService : IScoreService
{
private int _score;
public int Score => _score;
public void IncreaseScore(int amount) => _score += amount;
public void DecreaseScore(int amount) => _score -= amount;
}
}
@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 619f2425460843aaa1f0f21e302158ce
timeCreated: 1763559528
@@ -8,7 +8,6 @@ namespace Code.Gameplay.Player
public interface IPlayerFactory public interface IPlayerFactory
{ {
PlayerHealth CreatePlayerHealth(int count = -1); PlayerHealth CreatePlayerHealth(int count = -1);
PlayerMoney CreatePlayerMoney(int count = 0);
UniTask<CharacterProvider> CreatePlayer(); UniTask<CharacterProvider> CreatePlayer();
void ReleasePlayer(CharacterProvider characterProvider); void ReleasePlayer(CharacterProvider characterProvider);
UniTask<ArmsMeshProvider> CreateArms(Transform papochka); UniTask<ArmsMeshProvider> CreateArms(Transform papochka);
@@ -0,0 +1,11 @@
using Code.Gameplay.Character.Core;
namespace Code.Gameplay.Player
{
public interface IPlayerProvider
{
CharacterProvider Character { get; }
PlayerHealth Health { get; }
bool IsSpawned { get; }
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8fdfaa773e8645d5a2d400c0317b9894
timeCreated: 1763237368
@@ -0,0 +1,10 @@
namespace Code.Gameplay.Player
{
public interface IPlayerScore
{
event PlayerScore.OnScoreGetDelegate OnScoreGet;
event PlayerScore.OnScoreLooseDelegate OnScoreLoose;
int Score { get; }
void UpdateScore(int amount);
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5084b2568ebd4cddaef2154e5ad1f870
timeCreated: 1763890486
@@ -14,6 +14,7 @@ namespace Code.Gameplay.Player
public class PlayerFactory : IPlayerFactory public class PlayerFactory : IPlayerFactory
{ {
[Inject] PlayerConfig _playerConfig; [Inject] PlayerConfig _playerConfig;
[Inject] private IPlayerProvider _playerProvider;
public PlayerHealth CreatePlayerHealth(int count = -1) public PlayerHealth CreatePlayerHealth(int count = -1)
{ {
@@ -21,18 +22,23 @@ namespace Code.Gameplay.Player
return new(count); return new(count);
} }
public PlayerMoney CreatePlayerMoney(int count = 0) => new(count);
public async UniTask<CharacterProvider> CreatePlayer() public async UniTask<CharacterProvider> CreatePlayer()
{ {
CharacterProvider characterProvider = (await Addressables.InstantiateAsync(_playerConfig.characterPrefab).ToUniTask()).GetComponent<CharacterProvider>(); var go = await Addressables.InstantiateAsync(_playerConfig.characterPrefab).ToUniTask();
AttributeInjector.Inject(characterProvider, Container.ProjectContainer); var provider = go.GetComponent<CharacterProvider>();
AttributeInjector.Inject(characterProvider.CustomCharacterController, Container.ProjectContainer);
return characterProvider; AttributeInjector.Inject(provider, Container.ProjectContainer);
AttributeInjector.Inject(provider.CustomCharacterController, Container.ProjectContainer);
((PlayerProvider)_playerProvider).SetPlayer(provider, _playerConfig);
return provider;
} }
public void ReleasePlayer(CharacterProvider characterProvider) public void ReleasePlayer(CharacterProvider characterProvider)
{ {
((PlayerProvider)_playerProvider).ClearPlayer();
Addressables.ReleaseInstance(characterProvider.gameObject); Addressables.ReleaseInstance(characterProvider.gameObject);
} }
@@ -28,5 +28,11 @@ namespace Code.Gameplay.Player
_healthPoints = maxHealthPoints; _healthPoints = maxHealthPoints;
MaxHealthPoints = maxHealthPoints; MaxHealthPoints = maxHealthPoints;
} }
public void InstaKill()
{
_healthPoints = 0;
OnDeath?.Invoke();
}
} }
} }
@@ -1,26 +0,0 @@
namespace Code.Gameplay.Player
{
public class PlayerMoney
{
public delegate void OnMoneyGetDelegate(int count, int money);
public event OnMoneyGetDelegate OnMoneyGet;
public delegate void OnMoneyLooseDelegate(int count, int money);
public event OnMoneyLooseDelegate OnMoneyLoose;
private int _money;
public int Money => _money;
public void GiveMoney(int amount)
{
_money += amount;
OnMoneyGet?.Invoke(amount, _money);
}
public void LooseMoney(int amount)
{
_money -= amount;
OnMoneyLoose?.Invoke(amount, _money);
}
public PlayerMoney(int money) => _money = money;
}
}
@@ -0,0 +1,26 @@
using Code.Configs;
using Code.Gameplay.Character.Core;
namespace Code.Gameplay.Player
{
public class PlayerProvider : IPlayerProvider
{
public CharacterProvider Character { get; private set; }
public PlayerHealth Health { get; private set; }
public bool IsSpawned => Character != null;
internal void SetPlayer(CharacterProvider character, PlayerConfig config)
{
Character = character;
Health = new PlayerHealth(config.maxHealth);
}
internal void ClearPlayer()
{
Character = null;
Health = null;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0dbe29c5f0e9431db8c8da25eeb8fa95
timeCreated: 1763237388
@@ -0,0 +1,24 @@
namespace Code.Gameplay.Player
{
public class PlayerScore : IPlayerScore
{
public delegate void OnScoreGetDelegate(int count, int score);
public event OnScoreGetDelegate OnScoreGet;
public delegate void OnScoreLooseDelegate(int count, int score);
public event OnScoreLooseDelegate OnScoreLoose;
private int _score;
public int Score => _score;
public void UpdateScore(int amount)
{
_score += amount;
if (amount > 0) OnScoreGet?.Invoke(amount, _score);
else OnScoreLoose?.Invoke(amount, _score);
}
}
}
@@ -2,6 +2,6 @@ namespace Code.Input
{ {
public enum UIActions public enum UIActions
{ {
Continue
} }
} }
@@ -1,6 +1,8 @@
using Code.Gameplay.Door; using Code.Gameplay.Door;
using Code.UI.Panels.Etc;
using UnityEngine; using UnityEngine;
using UnityEngine.Experimental.GlobalIllumination; using UnityEngine.Experimental.GlobalIllumination;
using UnityEngine.Serialization;
namespace Code.Scenes.SceneEnvironments namespace Code.Scenes.SceneEnvironments
{ {
@@ -8,14 +10,20 @@ namespace Code.Scenes.SceneEnvironments
public class MainMenuEnvironment : CurrentSceneEnvironment public class MainMenuEnvironment : CurrentSceneEnvironment
{ {
public Transform cameraPosition; public Transform cameraPosition;
public DamnDoor damnDoor; public MainMenuUIVariables uiVariables;
public Light doorLight; public MainMenuButton startButton;
public MainMenuButton settingButton;
public MainMenuButton exitButton;
public Canvas menuButtonsCanvas;
public void TransitVariables(MainMenuEnvironment env) public void TransitVariables(MainMenuEnvironment env)
{ {
cameraPosition = env.cameraPosition; cameraPosition = env.cameraPosition;
damnDoor = env.damnDoor; uiVariables = env.uiVariables;
doorLight = env.doorLight; startButton = env.startButton;
settingButton = env.settingButton;
exitButton = env.exitButton;
menuButtonsCanvas = env.menuButtonsCanvas;
} }
} }
} }
@@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Code.Audio;
using Code.Configs; using Code.Configs;
using Code.Gameplay.Camera;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets;
@@ -21,8 +23,13 @@ namespace Code.StaticData
SFXListConfig SFXListConfig { get; } SFXListConfig SFXListConfig { get; }
DispatcherSoundListConfig DispatcherSoundListConfig { get; } DispatcherSoundListConfig DispatcherSoundListConfig { get; }
EnemiesSoundListConfig EnemiesSoundListConfig { get; } EnemiesSoundListConfig EnemiesSoundListConfig { get; }
DrunkenGrandpaConfig DrunkenGrandpaConfig { get; }
CockroachConfig CockroachConfig { get; }
FogConfig FogConfig { get; }
OrderServiceConfig OrderServiceConfig { get; } OrderServiceConfig OrderServiceConfig { get; }
OrderBoxConfig OrderBoxConfig { get; } OrderBoxConfig OrderBoxConfig { get; }
Dictionary<CameraMode, CameraSettingsConfig> CameraSettingsConfigs { get; }
Dictionary<AudioSourceType, AudioSourceSettingsConfig> AudioSourceSettingsConfigs { get; }
void Load(); void Load();
Task<T> InstantiateAsset<T>(AssetReference assetReference) where T : Behaviour; Task<T> InstantiateAsset<T>(AssetReference assetReference) where T : Behaviour;
} }
@@ -1,6 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Code.Audio;
using Code.Configs; using Code.Configs;
using Code.Constants; using Code.Constants;
using Code.Gameplay.Camera;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using UnityEngine; using UnityEngine;
using UnityEngine.AddressableAssets; using UnityEngine.AddressableAssets;
@@ -20,21 +24,34 @@ namespace Code.StaticData
public SFXListConfig SFXListConfig { get; private set; } public SFXListConfig SFXListConfig { get; private set; }
public DispatcherSoundListConfig DispatcherSoundListConfig { get; private set; } public DispatcherSoundListConfig DispatcherSoundListConfig { get; private set; }
public EnemiesSoundListConfig EnemiesSoundListConfig { get; private set; } public EnemiesSoundListConfig EnemiesSoundListConfig { get; private set; }
public DrunkenGrandpaConfig DrunkenGrandpaConfig {get; private set;}
public CockroachConfig CockroachConfig { get; private set; }
public FogConfig FogConfig { get; private set; }
public OrderServiceConfig OrderServiceConfig { get; private set; } public OrderServiceConfig OrderServiceConfig { get; private set; }
public OrderBoxConfig OrderBoxConfig { get; private set; } public OrderBoxConfig OrderBoxConfig { get; private set; }
public Dictionary<CameraMode, CameraSettingsConfig> CameraSettingsConfigs { get; private set; } = new();
public Dictionary<AudioSourceType, AudioSourceSettingsConfig> AudioSourceSettingsConfigs { get; private set; } = new();
public void Load() public void Load()
{ {
InputConfig = Resources.Load<InputConfig>(string.Format(StringPaths.ConfigSingle, "App", "InputConfig")); InputConfig = Resources.Load<InputConfig>(string.Format(StringPaths.ConfigSingle, "App", "InputConfig"));
AppConfig = Resources.Load<AppConfig>(string.Format(StringPaths.ConfigSingle, "App", "AppConfig")); AppConfig = Resources.Load<AppConfig>(string.Format(StringPaths.ConfigSingle, "App", "AppConfig"));
CameraConfig = Resources.Load<CameraConfig>(string.Format(StringPaths.ConfigSingle, "App", "CameraConfig"));
CharacterConfig = Resources.Load<CharacterConfig>(string.Format(StringPaths.ConfigSingle, "App", "CharacterConfig")); CharacterConfig = Resources.Load<CharacterConfig>(string.Format(StringPaths.ConfigSingle, "App", "CharacterConfig"));
UIConfig = Resources.Load<UIConfig>(string.Format(StringPaths.ConfigSingle, "App", "UIConfig")); UIConfig = Resources.Load<UIConfig>(string.Format(StringPaths.ConfigSingle, "App", "UIConfig"));
PlayerConfig = Resources.Load<PlayerConfig>(string.Format(StringPaths.ConfigSingle, "App", "PlayerConfig")); PlayerConfig = Resources.Load<PlayerConfig>(string.Format(StringPaths.ConfigSingle, "App", "PlayerConfig"));
OrderServiceConfig = Resources.Load<OrderServiceConfig>(string.Format(StringPaths.ConfigSingle, "Gameplay", "OrderServiceConfig")); OrderServiceConfig = Resources.Load<OrderServiceConfig>(string.Format(StringPaths.ConfigSingle, "Gameplay", "OrderServiceConfig"));
OrderBoxConfig = Resources.Load<OrderBoxConfig>(string.Format(StringPaths.ConfigSingle, "Gameplay", "OrderBoxConfig")); OrderBoxConfig = Resources.Load<OrderBoxConfig>(string.Format(StringPaths.ConfigSingle, "Gameplay", "OrderBoxConfig"));
CameraConfig = Resources.Load<CameraConfig>(string.Format(StringPaths.ConfigSingle, "Camera", "CameraConfig"));
CameraSettingsConfigs = Resources
.LoadAll<CameraSettingsConfig>(string.Format(StringPaths.ConfigMultiple, "Camera"))
.ToDictionary(x => x.cameraMode);
LoadAudioConfigs(); LoadAudioConfigs();
LoadEnemiesConfigs();
} }
private void LoadAudioConfigs() private void LoadAudioConfigs()
@@ -44,6 +61,16 @@ namespace Code.StaticData
SFXListConfig = Resources.Load<SFXListConfig>(string.Format(StringPaths.ConfigSingle, "Audio", "SFXListConfig")); SFXListConfig = Resources.Load<SFXListConfig>(string.Format(StringPaths.ConfigSingle, "Audio", "SFXListConfig"));
DispatcherSoundListConfig = Resources.Load<DispatcherSoundListConfig>(string.Format(StringPaths.ConfigSingle, "Audio", "DispatcherSoundListConfig")); DispatcherSoundListConfig = Resources.Load<DispatcherSoundListConfig>(string.Format(StringPaths.ConfigSingle, "Audio", "DispatcherSoundListConfig"));
EnemiesSoundListConfig = Resources.Load<EnemiesSoundListConfig>(string.Format(StringPaths.ConfigSingle, "Audio", "EnemiesSoundListConfig")); EnemiesSoundListConfig = Resources.Load<EnemiesSoundListConfig>(string.Format(StringPaths.ConfigSingle, "Audio", "EnemiesSoundListConfig"));
AudioSourceSettingsConfigs = Resources
.LoadAll<AudioSourceSettingsConfig>(string.Format(StringPaths.ConfigMultiple, "Audio"))
.ToDictionary(x => x.sourceType);
}
private void LoadEnemiesConfigs()
{
DrunkenGrandpaConfig = Resources.Load<DrunkenGrandpaConfig>(string.Format(StringPaths.ConfigSingle, "Enemies", "DrunkenGrandpaConfig"));
CockroachConfig = Resources.Load<CockroachConfig>(string.Format(StringPaths.ConfigSingle, "Enemies", "CockroachConfig"));
FogConfig = Resources.Load<FogConfig>(string.Format(StringPaths.ConfigSingle, "Enemies", "FogConfig"));
} }
public async Task<T> InstantiateAsset<T>(AssetReference assetReference) where T : Behaviour public async Task<T> InstantiateAsset<T>(AssetReference assetReference) where T : Behaviour
@@ -10,7 +10,7 @@ namespace Code.UI.Panels.Etc
public Button button; public Button button;
public Action OnPointerEnterEvent, OnPointerExitEvent; public Action OnPointerEnterEvent, OnPointerExitEvent;
public void OnPointerEnter(PointerEventData eventData) => OnPointerEnterEvent?.Invoke(); public virtual void OnPointerEnter(PointerEventData eventData) => OnPointerEnterEvent?.Invoke();
public void OnPointerExit(PointerEventData eventData) => OnPointerExitEvent?.Invoke(); public virtual void OnPointerExit(PointerEventData eventData) => OnPointerExitEvent?.Invoke();
} }
} }
@@ -0,0 +1,26 @@
using DG.Tweening;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
namespace Code.UI.Panels.Etc
{
public class MainMenuButton : HoverButton
{
public TMP_Text text;
public Color defaultColor;
public Color hoverColor;
public override void OnPointerEnter(PointerEventData eventData)
{
text.DOColor(hoverColor, 0.2f);
OnPointerEnterEvent?.Invoke();
}
public override void OnPointerExit(PointerEventData eventData)
{
text.DOColor(defaultColor, 0.2f);
OnPointerExitEvent?.Invoke();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b0e3f905e0af4d1ab624b1dfe8eaa1df
timeCreated: 1763762376
@@ -1,3 +1,4 @@
using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
@@ -5,15 +6,25 @@ namespace Code.UI
{ {
public class GameOverPanel : UIElement public class GameOverPanel : UIElement
{ {
[SerializeField] private TMP_Text scoreText;
[SerializeField] private TMP_Text gameOverText;
[SerializeField] private Image _background; [SerializeField] private Image _background;
public void UpdateScoreText(int score) => scoreText.text = score.ToString();
public override void Hide() public override void Hide()
{ {
_background.enabled = false; _background.enabled = false;
scoreText.enabled = false;
gameOverText.enabled = false;
base.Hide();
} }
public override void Show() public override void Show()
{ {
_background.enabled = true; _background.enabled = true;
scoreText.enabled = true;
gameOverText.enabled = true;
base.Show();
} }
} }
} }
@@ -1,3 +1,4 @@
using Code.Gameplay.Orders.Order;
using Code.Gameplay.Player; using Code.Gameplay.Player;
using DG.Tweening; using DG.Tweening;
using TMPro; using TMPro;
@@ -15,13 +16,17 @@ namespace Code.UI.Panels
[SerializeField] private float dispatcherTransitionTime = 0.5f; [SerializeField] private float dispatcherTransitionTime = 0.5f;
[Header("Timer")] [Header("Timer")]
[SerializeField] private TMP_Text timer; [SerializeField] private TMP_Text timer;
[SerializeField] private Image timerBackGround;
public TMP_Text Timer => timer; public TMP_Text Timer => timer;
[SerializeField] private RectTransform timerHiddenPosition;
[SerializeField] private RectTransform timerShownPosition;
[SerializeField] private float timerTransitionTime = 0.5f;
[Header("HealthBar")] [Header("HealthBar")]
[SerializeField] private Image healthBar; [SerializeField] private Image healthBar;
[Header("MoneyBar")] [Header("MoneyBar")]
[SerializeField] private TMP_Text moneyText; [SerializeField] private TMP_Text moneyText;
private void MoneyUpdate(int count, int money) private void ScoreUpdate(int count, int money)
{ {
// TODO Анимация получения\потери // TODO Анимация получения\потери
moneyText.text = money.ToString(); moneyText.text = money.ToString();
@@ -32,30 +37,47 @@ namespace Code.UI.Panels
float fillAmount = (float)health.HealthPoints / health.MaxHealthPoints; float fillAmount = (float)health.HealthPoints / health.MaxHealthPoints;
healthBar.fillAmount = fillAmount; healthBar.fillAmount = fillAmount;
} }
public void Initialize(PlayerMoney money, PlayerHealth health) private IOrderService _orderService;
public void Initialize(IPlayerScore score, PlayerHealth health, IOrderService orderService)
{ {
money.OnMoneyGet += MoneyUpdate; score.OnScoreGet += ScoreUpdate;
money.OnMoneyLoose += MoneyUpdate; score.OnScoreLoose += ScoreUpdate;
MoneyUpdate(0,money.Money); ScoreUpdate(0,score.Score);
health.OnDamage += HealthUpdate; health.OnDamage += HealthUpdate;
HealthUpdate(0,health); HealthUpdate(0,health);
_orderService = orderService;
} }
private void Update()
{
if (_orderService != null) timer.text = ((int)_orderService.DeliveryTimer).ToString();
}
private int _dispatcherCalls;
public void SummonDispatcher() public void SummonDispatcher()
{ {
// TODO // TODO
dispatcher.transform.DOMoveX(dispatcherShownPosition.position.x, dispatcherTransitionTime); _dispatcherCalls++;
dispatcher.transform.DOMoveY(dispatcherShownPosition.position.y, dispatcherTransitionTime);
} }
public void CloseDispatcher() public void CloseDispatcher()
{ {
dispatcher.transform.DOMoveX(dispatcherHiddenPosition.position.x, dispatcherTransitionTime); _dispatcherCalls--;
if (_dispatcherCalls > 0) return;
dispatcher.transform.DOMoveY(dispatcherHiddenPosition.position.y, dispatcherTransitionTime);
// TODO // TODO
} }
public override void Hide() public void ShowTimer()
{ {
// TODO
timerBackGround.transform.DOMoveX(timerShownPosition.position.x, timerTransitionTime);
} }
public override void Show() public void HideTimer()
{ {
timerBackGround.transform.DOMoveX(timerHiddenPosition.position.x, timerTransitionTime);
// TODO
} }
} }
} }
@@ -8,15 +8,20 @@ namespace Code.UI.Panels
{ {
[SerializeField] private Image background; [SerializeField] private Image background;
[SerializeField] private TMP_Text loadingTitle; [SerializeField] private TMP_Text loadingTitle;
[SerializeField] private TMP_Text vyapWatermark;
public override void Hide() public override void Hide()
{ {
Shown = false;
background.enabled = false; background.enabled = false;
loadingTitle.enabled = false; loadingTitle.enabled = false;
vyapWatermark.enabled = false;
} }
public override void Show() public override void Show()
{ {
Shown = true;
background.enabled = true; background.enabled = true;
loadingTitle.enabled = true; loadingTitle.enabled = true;
vyapWatermark.enabled = true;
} }
} }
} }
@@ -5,37 +5,46 @@ namespace Code.UI.Panels
{ {
public class MainMenuPanel : UIElement public class MainMenuPanel : UIElement
{ {
public HoverButton startButton; public MainMenuButton startButton;
public HoverButton exitButton; public MainMenuButton settingButton;
public CinematicDoorOpening CinematicDoorOpening; public MainMenuButton exitButton;
public void Start() private CinematicDoorOpening _cinematicDoorOpening;
public void Initialize(MainMenuUIVariables mainMenuUIVariables,
MainMenuButton startButton,
MainMenuButton settingButton,
MainMenuButton exitButton)
{ {
this.startButton = startButton;
this.settingButton = settingButton;
this.exitButton = exitButton;
startButton.defaultColor = mainMenuUIVariables.ButtonDefaultColor;
settingButton.defaultColor = mainMenuUIVariables.ButtonDefaultColor;
exitButton.defaultColor = mainMenuUIVariables.ButtonDefaultColor;
startButton.hoverColor = mainMenuUIVariables.ButtonHoverColor;
settingButton.hoverColor = mainMenuUIVariables.ButtonHoverColor;
exitButton.hoverColor = mainMenuUIVariables.ButtonHoverColor;
startButton.OnPointerEnterEvent = OpenStart; startButton.OnPointerEnterEvent = OpenStart;
settingButton.OnPointerEnterEvent = OpenSettings;
exitButton.OnPointerEnterEvent = OpenExit; exitButton.OnPointerEnterEvent = OpenExit;
startButton.OnPointerExitEvent = CloseDoor; startButton.OnPointerExitEvent = CloseDoor;
settingButton.OnPointerExitEvent = CloseDoor;
exitButton.OnPointerExitEvent = CloseDoor; exitButton.OnPointerExitEvent = CloseDoor;
_cinematicDoorOpening = new(mainMenuUIVariables.MainMenuDoor, mainMenuUIVariables.DoorLight);
} }
public override void Show() private void OpenStart() => _cinematicDoorOpening.PreOpen(0);
{
}
public override void Hide() private void OpenSettings() => _cinematicDoorOpening.PreOpen(1);
{
}
private void OpenExit() private void OpenExit() => _cinematicDoorOpening.PreOpen(2);
{
CinematicDoorOpening.PreOpen(false); private void CloseDoor() => _cinematicDoorOpening.Close();
}
private void OpenStart()
{
CinematicDoorOpening.PreOpen(true);
}
private void CloseDoor()
{
CinematicDoorOpening.Close();
}
} }
} }
@@ -0,0 +1,30 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
namespace Code.UI
{
public class SettingsPanel : UIElement
{
[SerializeField] private TMP_Text scoreText;
[SerializeField] private TMP_Text gameOverText;
[SerializeField] private Image _background;
public void UpdateScoreText(int score) => scoreText.text = score.ToString();
public override void Hide()
{
_background.enabled = false;
scoreText.enabled = false;
gameOverText.enabled = false;
base.Hide();
}
public override void Show()
{
_background.enabled = true;
scoreText.enabled = true;
gameOverText.enabled = true;
base.Show();
}
}
}
Loaded 100 of 228 files, more files were not shown because too many files have changed in this diff. Show more