85 Commits
Author SHA1 Message Date
ThomasCharester a1c2c5fd18 Музыка меня отменила 2025-12-03 20:15:34 +03:00
ThomasCharester 485d54f272 Поудалял важные компоненты 2025-12-03 20:07:36 +03:00
ThomasCharester 0d0787b887 Звуки поражения 2025-12-03 19:32:10 +03:00
ThomasCharester 5d856ae162 Получение урона теперь чуть более заметно (плейсхолдер) 2025-12-03 17:39:26 +03:00
ThomasCharester dd1399e015 Урон курьера 2025-12-03 14:13:45 +03:00
ThomasCharester 2b656412d5 Фикс звуков, только аккуратно
Звуки от 0.01 до 1 ставим, иначе +уши
2025-12-03 14:04:08 +03:00
ThomasCharester 36e6bc3bc2 Звуки шагов в моей голове 2025-12-02 21:50:33 +03:00
ThomasCharester 1e31e1e8b1 Фикс трактора 2025-12-02 19:53:02 +03:00
ThomasCharester 2897eb88bf Звуки шагов, ядерный таракан, монстр дед 2025-12-01 18:36:22 +03:00
ThomasCharester 134adda787 Недочин недопил но переполошил аудио 2025-12-01 14:52:19 +03:00
ThomasCharester 2f56a5758d Мобы на уровнях, анимации 2025-12-01 12:59:15 +03:00
ThomasCharester fb9510a6b2 Починка генерации + небольшой прикол дверей 2025-12-01 00:02:00 +03:00
ThomasCharester aa4a9e69dd Подогреваем коллизии 2025-11-29 00:41:07 +03:00
ThomasCharester 34d7fa3791 Блять 2025-11-28 15:13:03 +03:00
ThomasCharester 26804e08f7 Да, теперь нормально 2025-11-28 15:04:56 +03:00
ThomasCharester e96dcf3e0d Merge branch 'Dev' of https://notscam.serveminecraft.net/Tetramino/CourierOld into Dev 2025-11-28 15:04:25 +03:00
ThomasCharester 94f0522f89 Да, теперь нормально 2025-11-28 14:51:38 +03:00
xrust1k eefcf6eabc Merge branch 'Dev' of https://notscam.serveminecraft.net/Tetramino/CourierOld into Dev 2025-11-28 14:48:59 +03:00
xrust1k c85638fda0 Бля ебать рот этих дверей 2025-11-28 14:47:29 +03:00
ThomasCharester 1de117622e Фикс пива 2025-11-28 14:32:37 +03:00
ThomasCharester e2f8984e6f Merge remote-tracking branch 'origin/mobs-and-levels-dev' into Dev 2025-11-28 14:25:02 +03:00
ThomasCharester 32c6e0630a Солнышко 2025-11-28 14:20:51 +03:00
MrTorex 870a4743c6 дрочка лёжа на рельсах 2025-11-28 14:14:55 +03:00
MrTorex 2fc8e249f5 Merge branch 'mobs-and-levels-dev' into Dev 2025-11-28 13:56:35 +03:00
MrTorex 280eb3754c МЕНЯ ТРАХАЛИ ВСЕМ ДЕТДОМОМ 2025-11-28 13:56:02 +03:00
xrust1k 6eff3ef9a9 AAAAAAAAAAAAAAAAAAA 2025-11-28 13:51:32 +03:00
xrust1k 6b4792393a Merge branch 'Dev' of https://notscam.serveminecraft.net/Tetramino/CourierOld into Dev 2025-11-28 13:33:00 +03:00
xrust1k a547c39db7 Комнаты 2025-11-28 13:26:52 +03:00
ThomasCharester 0cab2eda74 Добавлена механика Initial Room 2025-11-28 13:21:56 +03:00
xrust1k 5d075034b5 Я со старой версии запушил снова, ломаться не должно 2025-11-28 12:10:30 +03:00
xrust1k 7f7ba0d9c5 Merge branch 'Dev' of https://notscam.serveminecraft.net/Tetramino/CourierOld into Dev 2025-11-28 11:52:49 +03:00
xrust1k ab0b41a318 Я боюсь 2025-11-28 11:52:15 +03:00
MrTorex a5ce8522ab мержим 2025-11-28 11:46:04 +03:00
MrTorex 97aea0fde1 Merge branch 'Dev' into movement-dev 2025-11-28 11:42:30 +03:00
MrTorex e9e8e091b2 письки 2025-11-28 11:42:08 +03:00
ThomasCharester 9e1c1177e0 Фикс геймплея? 2025-11-28 11:39:59 +03:00
ThomasCharester 07535982ba Dimka Bakery 2025-11-28 11:09:31 +03:00
MrTorex df0c2b3157 Merge branch 'Dev' into movement-dev 2025-11-28 10:49:11 +03:00
MrTorex d51527b7f3 это спермаггедон 2025-11-28 10:47:28 +03:00
xrust1k 13aca5a86f Merge branch 'Dev' of https://notscam.serveminecraft.net/Tetramino/CourierOld into Dev 2025-11-28 10:43:47 +03:00
xrust1k 9cc77d5c67 Уровни (Да ничего я не сделал) 2025-11-28 10:42:00 +03:00
ThomasCharester c1a0e94fe3 Merge branch 'Generator' into Dev 2025-11-28 10:28:10 +03:00
ThomasCharester acd9cc1a32 Ахуеть, я сделал генерацию уровней на основе идеи Димки 2025-11-28 10:27:49 +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
ThomasCharester b7e9a26462 Комнаты, вся хуйня 2025-11-27 23:39:57 +03:00
ThomasCharester 3782a0b568 Фикс отпуска звуков 2025-11-27 23:01:12 +03:00
ThomasCharester 000985ee9b Фикс отпуска звуков 2025-11-27 22:59:50 +03:00
ThomasCharester abde1d5ba9 Merge branch 'Dev' into Generator 2025-11-27 22:36:55 +03:00
ThomasCharester 52b18967a1 Руки игрока 2025-11-27 22:34:34 +03:00
MrTorex d10d2cdca6 прорвёмся (добавлен walljump) 2025-11-27 21:36:19 +03:00
ThomasCharester 79d03528f6 Настройки 2025-11-27 21:30:00 +03:00
MrTorex 01d3b0adec чел не липнет к воздуху 2025-11-27 21:10:23 +03:00
MrTorex b271042137 инишиал титан падать второй мувмент (но пока очень говно) 2025-11-27 20:37:16 +03:00
ThomasCharester 4a547f1348 Шаманим над меню 2025-11-27 13:06:41 +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
Pe4enega cea36c0cda Триггеры на вход в комнату 2025-11-25 20:04:36 +03:00
Pe4enega ff8ed96138 Merge branch 'Dev' into Generator 2025-11-24 18:00:18 +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
Pe4enega 01505de4c3 Проверка расстановки 2025-11-18 14:45:43 +03:00
MrTorex 7024e1daf0 темп 2025-11-18 12:27:35 +03:00
MrTorex 020934e07f Инишиал мобы 2025-11-17 14:45:51 +03:00
Pe4enega 52edef92e3 Вроде ок, осталось проверить на практике нормально, когда комнаты будут готовы 2025-11-15 21:31:40 +03:00
Pe4enega c6b097a879 Промежуточный 2025-11-15 17:25:09 +03:00
Pe4enega a243ad53d6 Вставка лестниц(не доделана) 2025-11-12 14:26:16 +03:00
Pe4enega 85a07065d9 Комнаты ставятся, но криво 2025-11-10 21:08:54 +03:00
844 changed files with 1007591 additions and 3595 deletions

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
{
"version": "1.0",
"components": [
"Microsoft.VisualStudio.Workload.ManagedGame"
]
}
@@ -15,7 +15,7 @@ MonoBehaviour:
m_DefaultGroup: d447fc60305c17a17bf20948ab295a45 m_DefaultGroup: d447fc60305c17a17bf20948ab295a45
m_currentHash: m_currentHash:
serializedVersion: 2 serializedVersion: 2
Hash: db97f0adbdaa5d76ea9341571ea44391 Hash: 1ec1ee985689145b755c859b69691cb0
m_OptimizeCatalogSize: 0 m_OptimizeCatalogSize: 0
m_BuildRemoteCatalog: 0 m_BuildRemoteCatalog: 0
m_CatalogRequestsTimeout: 0 m_CatalogRequestsTimeout: 0
@@ -15,21 +15,28 @@ 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: 1cc66c0b13a946e1c9ec37026268e144
m_Address: Audio/Enemies/pl_metal2
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,13 +52,18 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 2301400160a23924eb27c4bdbe797739 - m_GUID: 23e11d2c8a4f12f4ea9a7a43d1118464
m_Address: 'Audio/Music/Nightmargin (Oneshot OST) - The Prophecy ' m_Address: Gameplay
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 23e11d2c8a4f12f4ea9a7a43d1118464 - m_GUID: 26d4e4c556527924490b8018cd510eda
m_Address: Gameplay m_Address: Assets/Resources_moved/Audio/Enemies/bc_bite2.wav
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 28a6c7a4124141341ab7c2b7dfd3e95b
m_Address: Assets/Resources_moved/Prefabs/Levels/Level1 2.prefab
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
@@ -65,6 +77,16 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 2c6a984e4a1b42647b48e6b7bc69b158
m_Address: Prefabs/Levels/Level6
m_ReadOnly: 0
m_SerializedLabels: []
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
@@ -80,16 +102,31 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 3f8f2171cc252d953b82083cc16bd1ae
m_Address: Assets/Resources_moved/Prefabs/Levels/Level6_1.prefab
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 48aebfe8826dc0df79d047b5afbf8f5b - m_GUID: 48aebfe8826dc0df79d047b5afbf8f5b
m_Address: Canvas m_Address: Canvas
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 4b279429428a13040b6d1713122cd0de
m_Address: Assets/Resources_moved/Audio/SFX/pl_pain5.wav
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 4bd64d1fcecc9c6b09d81e78b7dcfebe - m_GUID: 4bd64d1fcecc9c6b09d81e78b7dcfebe
m_Address: Audio/Dispatcher/take_damage1 m_Address: Audio/Dispatcher/take_damage1
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 7067a5248027e74478f4f0f53ddf06a4
m_Address: Prefabs/Rooms/Save1
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 735f38402ea347fa381408293503a24e - m_GUID: 735f38402ea347fa381408293503a24e
m_Address: Audio/Dispatcher/receiving_an_order1 m_Address: Audio/Dispatcher/receiving_an_order1
m_ReadOnly: 0 m_ReadOnly: 0
@@ -115,6 +152,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
@@ -135,13 +178,23 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: 9d78f017876abed4c9cd8957f1e140cb
m_Address: Prefabs/Rooms/Level1_2
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: a306df57f348ce7cf900d0b9431739ee - m_GUID: a306df57f348ce7cf900d0b9431739ee
m_Address: launch_selectmenu m_Address: launch_selectmenu
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: b2c9f83c47783069aabb564f68cb0cbb - m_GUID: aa138b2e21adfbf4bbbfa5bf9d54b8f2
m_Address: absolutelynot m_Address: Assets/Resources_moved/Audio/UI/buttonclickrelease.wav
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: ae6ab2c88ae248ce1b6efca892f17659
m_Address: Assets/Resources_moved/Audio/Music/TestDeath.ogg
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
@@ -150,16 +203,51 @@ MonoBehaviour:
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: b33855c694de89386a25c56cbe463a0e
m_Address: Assets/Resources_moved/Prefabs/Enemies/DrunkenGrandpa.prefab
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: b37ea8c61cc322afdad529d678612d22
m_Address: Assets/Resources_moved/Audio/Enemies/zo_attack2.wav
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: bf87bc9ac43df86be8c818de81d44a54
m_Address: Assets/Resources_moved/Audio/SFX/pl_step1.wav
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: c566fc932edefbbee9e53271eac3c560 - m_GUID: c566fc932edefbbee9e53271eac3c560
m_Address: MainMenu m_Address: MainMenu
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: c5a0ad8a615841547ac2ee613af526d8
m_Address: Assets/Resources_moved/Audio/SFX/pl_pain2.wav
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: c938de3174f184741925c407a43a47f2
m_Address: Prefabs/Rooms/Level1
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: cab4b1ff78dd6b1fba61aa165aa17b4d - m_GUID: cab4b1ff78dd6b1fba61aa165aa17b4d
m_Address: Audio/Dispatcher/receiving_an_order2 m_Address: Audio/Dispatcher/receiving_an_order2
m_ReadOnly: 0 m_ReadOnly: 0
m_SerializedLabels: [] m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0 FlaggedDuringContentUpdateRestriction: 0
- m_GUID: e3a3840a7963bf841872cddb02e90151
m_Address: Prefabs/Levels/MainMenu 1
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: e84e3a3c26ccbd50d92f13627403f148
m_Address: Audio/Enemies/rch_walk
m_ReadOnly: 0
m_SerializedLabels: []
FlaggedDuringContentUpdateRestriction: 0
- m_GUID: ebe18293a5ef86704b23dad3a1786476 - m_GUID: ebe18293a5ef86704b23dad3a1786476
m_Address: Half-Life01 m_Address: Half-Life01
m_ReadOnly: 0 m_ReadOnly: 0
@@ -170,8 +258,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
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d36aab6707c518f5abfd60065165eb35
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d982316b2b4c99747b9e953ae7b9b42f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,41 +1,85 @@
using Code.App.StateMachine; using Code.App.StateMachine;
using Code.Audio;
using Code.Gameplay.Player;
using Code.Input;
using Code.Scenes; using Code.Scenes;
using Code.Scenes.SceneEnvironments;
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);
Context.AudioService.PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(MusicType.Death);
Context.AudioService.ApplyStun(5000,0.5f);
GameOver().Forget();
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;
_gameOverPanel.Show();
}
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(); Context.AudioService.GladlyAskToStopMusic(MusicType.Death, callback: Context.AudioFactory.ReleaseDeathMusic);
Context.AudioService.TransitToSnapshot(AudioSnapshotState.None);
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;
@@ -15,26 +13,24 @@ using Code.StateMachine;
using Code.UI; using Code.UI;
using Code.UI.Panels; using Code.UI.Panels;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using DG.Tweening;
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.Rendering;
using UnityEngine.SceneManagement; using UnityEngine.SceneManagement;
namespace Code.App.AppStates 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 +39,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; Context.AudioService.TransitToSnapshot(AudioSnapshotState.None);
Context.AudioService.TransitToSnapshot(AudioSnapshotState.Peaceful);
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.Debug4, TakeGamageDEBUG);
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.Debug5, Tube);
// Context.InputService.DebugBindAction((int)ActionMappings.Debug, (int)DebugActions.Debug6, PlayDebugDispatcher);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed Context.SceneLoaderService.LoadSceneAsyncAwaitable((int)SceneIds.DevGen);
WaitForSceneToLoad(); Context.LoadingManager.OnLoadingEnded += AfterSceneLoaded;
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
WaitForLoad().Forget();
} }
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.WarmDeathMusic(),
Context.AudioFactory.WarmSFX(), Context.AudioFactory.WarmSFX(),
Context.AudioFactory.WarmDispatcher(), Context.AudioFactory.WarmDispatcher(),
// TODO Context.AudioFactory.WarmEnemies(),
WaitForPlayerArmsToInitialize(), Context.AudioFactory.WarmCourier()
WaitForPlayerCharacterLoad()
); );
MusicInitialize();
Container sceneContainer = SceneManager.GetActiveScene().GetSceneContainer(); Context.LoadingManager
.UnregisterLoading(LoadingWork.LoadUI)
GameplaySceneEnvironment sceneEnvironment = SceneEnvironmentVariables(sceneContainer); .UnregisterLoading(LoadingWork.LoadAudio);
PlayerInitialize(sceneEnvironment);
UIInitialize();
CameraInitialize();
InputServiceInitialize(); InputServiceInitialize();
MusicInitialize();
}
private async void AfterSceneLoaded()
{
Context.LoadingManager
.RegisterLoading(LoadingWork.LoadModelResource);
Context.LoadingManager.OnLoadingEnded -= AfterSceneLoaded;
Context.LoadingManager.OnLoadingEnded += AfterPlayerLoaded;
Context.PlayerProvider = SceneManager.GetActiveScene().GetSceneContainer().Resolve<IPlayerProvider>();
Context.PlayerFactory = SceneManager.GetActiveScene().GetSceneContainer().Resolve<IPlayerFactory>();
await UniTask.WhenAll(
WaitForPlayerArmsToInitialize(),
WaitForPlayerCharacterLoad()
);
_playerHealth = Context.PlayerProvider.Health;
_playerHealth.OnDamage += PlayerDamageTaken;
_playerHealth.OnDeath += EndGame;
Context.LoadingManager
.UnregisterLoading(LoadingWork.LoadModelResource);
}
private void AfterPlayerLoaded()
{
Container sceneContainer = SceneManager.GetActiveScene().GetSceneContainer();
_playerScore = sceneContainer.Resolve<IPlayerScore>();
GameplaySceneEnvironment sceneEnvironment = SceneEnvironmentVariables(sceneContainer);
_volume = sceneEnvironment.volume;
PlayerInitialize(sceneEnvironment);
CameraInitialize();
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,32 +144,41 @@ 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);
Context.AudioService.TransitToSnapshot(AudioSnapshotState.Peaceful);
}
private void PlayerDamageTaken(int damage, PlayerHealth health)
{
Context.AudioService.PlayDispatcher((DispatcherSounds)Random.Range((int)DispatcherSounds.TakeDamage1,(int)DispatcherSounds.TakeDamage4));
Context.AudioService.PlayCourier((CourierSounds)Random.Range((int)CourierSounds.Damage1,(int)CourierSounds.Damage2));
DamageEffectProcess().Forget();
}
// TODO В конфиг
private async UniTaskVoid DamageEffectProcess()
{
_damagePanel.Show();
Context.AudioService.ApplyStun(200, 0.5f);
DOTween.To(() => _volume.weight, x => _volume.weight = x, 1f, 0.5f);
await UniTask.WaitForSeconds(3f);
DOTween.To(() => _volume.weight, x => _volume.weight = x, 0f, 1f);
Context.AudioService.ApplyStun(5000, 1f);
_damagePanel.Hide();
} }
// DEBUG // DEBUG
private void GiveOrderDEBUG(InputAction.CallbackContext context) private void InstaKillDEBUG(InputAction.CallbackContext context)
{ {
_orderService.OrderGive(); _playerHealth.InstaKill();
} }
private void TakeGamageDEBUG(InputAction.CallbackContext context)
private void RecieveOrderDEBUG(InputAction.CallbackContext context)
{ {
_orderService.OrderRecieved(); _playerHealth.GetDamage(1);
}
private void DeliverOrderDEBUG(InputAction.CallbackContext context)
{
_orderService.OrderDelivered();
}
private void RoomPassedDEBUG(InputAction.CallbackContext context)
{
_orderService.RoomPassed();
}
private OrderDispenser _debugTube;
private void Tube(InputAction.CallbackContext context)
{
_debugTube.ThrowOrder();
} }
// DEBUG // DEBUG
private bool _tickEnabled; private bool _tickEnabled;
@@ -145,8 +186,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 +198,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 +206,22 @@ 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, time: 0f, callback: Context.AudioFactory.ReleaseActionMusic);
Context.AudioService.GladlyAskToStopMusic(MusicType.Peaceful, callback: Context.AudioFactory.ReleasePeacefulMusic); Context.AudioService.GladlyAskToStopMusic(MusicType.Peaceful, time: 0f, callback: Context.AudioFactory.ReleasePeacefulMusic);
Context.AudioFactory.ReleaseCourier();
Context.AudioFactory.ReleaseDispatcher();
Context.AudioFactory.ReleaseEnemies();
Context.AudioFactory.ReleaseSFX();
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.Debug4, TakeGamageDEBUG);
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.Debug5, Tube);
// Context.InputService.DebugUnbindAction((int)ActionMappings.Debug, (int)DebugActions.Debug6, PlayDebugDispatcher);
base.ExitState(); base.ExitState();
} }
@@ -194,16 +238,21 @@ namespace Code.App.AppStates
private GameplayPanel _gameplayPanel; private GameplayPanel _gameplayPanel;
private ArmsMeshProvider _armsMesh; private ArmsMeshProvider _armsMesh;
private DamagePanel _damagePanel;
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;
_damagePanel = _gameplayPanel.DamagePanel;
} }
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,18 +261,19 @@ 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 Volume _volume;
private GameplaySceneEnvironment SceneEnvironmentVariables(Container sceneContainer) private GameplaySceneEnvironment SceneEnvironmentVariables(Container sceneContainer)
{ {
var sceneEnvironment = (GameplaySceneEnvironment)sceneContainer.Resolve<CurrentSceneEnvironment>(); var sceneEnvironment = (GameplaySceneEnvironment)sceneContainer.Resolve<CurrentSceneEnvironment>();
_debugTube = sceneEnvironment.debugTube;
return sceneEnvironment; return sceneEnvironment;
} }
} }
@@ -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,81 @@ 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.AudioFactory.WarmAudioTestClips()
); );
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;
mainMenuEnvironment.settingsCanvas.worldCamera = Context.CameraStateMachine.Camera;
Context.UIService.LoadingScreen.Hide();
Context.InputService.EnableMap(ActionMappings.MainMenu);
} }
public override void Tick() public override void Tick()
@@ -84,6 +122,7 @@ namespace Code.App.AppStates
Context.InputService.DisableMap(ActionMappings.MainMenu); Context.InputService.DisableMap(ActionMappings.MainMenu);
Context.AudioService.GladlyAskToStopMusic(MusicType.Menu, callback: Context.AudioFactory.ReleaseMenuMusic); Context.AudioService.GladlyAskToStopMusic(MusicType.Menu, callback: Context.AudioFactory.ReleaseMenuMusic);
Context.AudioFactory.ReleaseAudioTestClips();
base.ExitState(); base.ExitState();
} }
} }
@@ -1,10 +1,14 @@
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.Generator;
using Code.Input; using Code.Input;
using Code.Scenes; using Code.Scenes;
using Code.StaticData; using Code.StaticData;
@@ -26,10 +30,12 @@ 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));
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));
@@ -61,6 +67,7 @@ namespace Code.App
containerBuilder.AddSingleton(infrastructureUnit.AddComponent<CameraStateMachine>()); containerBuilder.AddSingleton(infrastructureUnit.AddComponent<CameraStateMachine>());
} }
// TODO Часть в сцены
private void InstallStaticData(ContainerBuilder containerBuilder, out IStaticDataService staticDataService) private void InstallStaticData(ContainerBuilder containerBuilder, out IStaticDataService staticDataService)
{ {
staticDataService = new StaticDataService(); staticDataService = new StaticDataService();
@@ -72,20 +79,32 @@ 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.AudioTestClipsConfig, typeof(AudioTestClipsConfig));
containerBuilder.AddSingleton(staticDataService.SFXListConfig, typeof(SFXListConfig)); containerBuilder.AddSingleton(staticDataService.SFXListConfig, typeof(SFXListConfig));
containerBuilder.AddSingleton(staticDataService.CourierSoundListConfig, typeof(CourierSoundListConfig));
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.DefaultAudioSettingsConfig, typeof(DefaultAudioSettingsConfig));
containerBuilder.AddSingleton(staticDataService.OrderServiceConfig, typeof(OrderServiceConfig)); containerBuilder.AddSingleton(staticDataService.OrderServiceConfig, typeof(OrderServiceConfig));
containerBuilder.AddSingleton(staticDataService.OrderBoxConfig, typeof(OrderBoxConfig)); containerBuilder.AddSingleton(staticDataService.OrderBoxConfig, typeof(OrderBoxConfig));
containerBuilder.AddSingleton(staticDataService.GeneratorConfig, typeof(GeneratorConfig));
} }
private AppStateMachine _appStateMachine; private AppStateMachine _appStateMachine;
@@ -100,20 +119,26 @@ namespace Code.App
AttributeInjector.Inject(cameraStateMachine.StateMachineContext, container); AttributeInjector.Inject(cameraStateMachine.StateMachineContext, container);
cameraStateMachine.Initialize(); cameraStateMachine.Initialize();
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed BootstrapFinish(container).Forget();
BootstrapFinish(container);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
} }
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,14 @@
using Code.App.Loading;
using Code.Gameplay.Enemies;
using Code.Gameplay.Orders.Order; using Code.Gameplay.Orders.Order;
using Code.Gameplay.Player;
using Code.Generator;
using Code.Scenes; using Code.Scenes;
using Code.Scenes.SceneEnvironments; using Code.Scenes.SceneEnvironments;
using Cysharp.Threading.Tasks;
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,17 +19,38 @@ 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.AddScoped(typeof(RoomGeneratorFactory), typeof(IRoomGeneratorFactory));
containerBuilder.AddScoped(typeof(PlayerProvider), typeof(IPlayerProvider));
containerBuilder.AddScoped(typeof(PlayerFactory), typeof(IPlayerFactory));
containerBuilder.AddScoped(typeof(EnemyFactory), typeof(IEnemyFactory));
containerBuilder.AddScoped(typeof(RoomGenerator));
containerBuilder.OnContainerBuilt += Initialize; containerBuilder.OnContainerBuilt += Initialize;
} }
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);
LoadRooms(container,loadingManager).Forget();
}
private async UniTaskVoid LoadRooms(Container container, ILoadingManager loadingManager)
{
await container.Resolve<IRoomGeneratorFactory>().PreloadRooms(container.Resolve<IOrderService>());
container.Resolve<RoomGenerator>().Initialize();
loadingManager.UnregisterLoading(LoadingWork.LoadModelResource);
} }
} }
} }
@@ -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,6 +1,9 @@
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.Generator;
using Code.Input; using Code.Input;
using Code.Scenes; using Code.Scenes;
using Code.UI; using Code.UI;
@@ -10,13 +13,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 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 IPlayerFactory PlayerFactory { get; set; }
/*[Inject]*/public IPlayerProvider PlayerProvider { get; set; }
// [Inject] public IEnemyFactory EnemyFactory { get; private set; }
} }
} }
@@ -11,14 +11,13 @@ namespace Code.Audio
} }
public enum EnemiesSounds public enum EnemiesSounds
{ {
Bastard, GrandpaAttack,
Bravo RoachAttack,
RoachStep,
GrandpaStep
} }
public enum DispatcherSounds public enum DispatcherSounds
{ {
Absolutely,
AbsolutelyNot,
AhFreeman,
Recieve1, Recieve1,
Recieve2, Recieve2,
TakeDamage1, TakeDamage1,
@@ -31,4 +30,10 @@ namespace Code.Audio
Late4, Late4,
Late5 Late5
} }
public enum CourierSounds
{
Step,
Damage1,
Damage2
}
} }
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Code.Configs; using Code.Configs;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using UnityEngine; using UnityEngine;
@@ -15,31 +16,40 @@ namespace Code.Audio
public AudioMixer AudioMixer { get; private set; } public AudioMixer AudioMixer { get; private set; }
private AssetReference _audioSourcePrefab; private AssetReference _audioSourcePrefab;
private AssetReference[] _audioTestClipsReferences;
private AssetReference[] _peacefulSoundtrack; private AssetReference[] _peacefulSoundtrack;
private AssetReference[] _actionSoundtrack; private AssetReference[] _actionSoundtrack;
private AssetReference[] _deathSoundtrack;
private AssetReference[] _menuSoundtrack; private AssetReference[] _menuSoundtrack;
private AssetReference[] _SFX; private AssetReference[] _SFX;
private AssetReference[] _enemies; private AssetReference[] _enemies;
private AssetReference[] _dispatcher; private AssetReference[] _dispatcher;
private AssetReference[] _character; private AssetReference[] _courier;
private AsyncOperationHandle<IList<AudioClip>> _audioTestClipsHandle;
private AsyncOperationHandle<IList<AudioClip>> _menuMusicHandle; private AsyncOperationHandle<IList<AudioClip>> _menuMusicHandle;
private AsyncOperationHandle<IList<AudioClip>> _deathMusicHandle;
private AsyncOperationHandle<IList<AudioClip>> _actionMusicHandle; private AsyncOperationHandle<IList<AudioClip>> _actionMusicHandle;
private AsyncOperationHandle<IList<AudioClip>> _peacefulMusicHandle; private AsyncOperationHandle<IList<AudioClip>> _peacefulMusicHandle;
private AsyncOperationHandle<IList<AudioClip>> _SFXHandle; private AsyncOperationHandle<IList<AudioClip>> _SFXHandle;
private AsyncOperationHandle<IList<AudioClip>> _enemiesHandle; private AsyncOperationHandle<IList<AudioClip>> _enemiesHandle;
private AsyncOperationHandle<IList<AudioClip>> _dispatcherHandle; private AsyncOperationHandle<IList<AudioClip>> _dispatcherHandle;
private AsyncOperationHandle<IList<AudioClip>> _courierHandle;
public Dictionary<AudioType, AudioClip> AudioTestClips { get; private set; } = new();
public Dictionary<MusicType, List<AudioClip>> MusicDictionary { get; private set; } = new(); public Dictionary<MusicType, List<AudioClip>> MusicDictionary { get; private set; } = new();
public List<AudioClip> SFXAudios { get; private set; } = new(); public List<AudioClip> SFXAudios { get; private set; } = new();
public List<AudioClip> EnemiesAudios { get; private set; } = new(); public List<AudioClip> EnemiesAudios { get; private set; } = new();
public List<AudioClip> CourierAudios { get; private set; } = new();
public List<AudioClip> DispatcherAudios { get; private set; } = new(); public List<AudioClip> DispatcherAudios { get; private set; } = new();
public AudioFactory(AudioMixerConfig audioMixerConfig, public AudioFactory(AudioMixerConfig audioMixerConfig,
SoundtrackListConfig soundtrackListConfig, SoundtrackListConfig soundtrackListConfig,
SFXListConfig sfxListConfig, SFXListConfig sfxListConfig,
EnemiesSoundListConfig enemiesSoundListConfig, EnemiesSoundListConfig enemiesSoundListConfig,
DispatcherSoundListConfig dispatcherSoundListConfig DispatcherSoundListConfig dispatcherSoundListConfig,
AudioTestClipsConfig audioTestClipsConfig,
CourierSoundListConfig courierSoundListConfig
) )
{ {
AudioMixer = audioMixerConfig.audioMixerAsset; AudioMixer = audioMixerConfig.audioMixerAsset;
@@ -47,14 +57,18 @@ namespace Code.Audio
_peacefulSoundtrack = soundtrackListConfig.peacefulSoundtrack; _peacefulSoundtrack = soundtrackListConfig.peacefulSoundtrack;
_actionSoundtrack = soundtrackListConfig.actionSoundtrack; _actionSoundtrack = soundtrackListConfig.actionSoundtrack;
_deathSoundtrack = soundtrackListConfig.deathSoundtrack;
_menuSoundtrack = soundtrackListConfig.menuSoundtrack; _menuSoundtrack = soundtrackListConfig.menuSoundtrack;
_SFX = sfxListConfig.SFX; _SFX = sfxListConfig.SFX;
_enemies = enemiesSoundListConfig.enemies; _enemies = enemiesSoundListConfig.enemies;
_dispatcher = dispatcherSoundListConfig.dispatcher; _dispatcher = dispatcherSoundListConfig.dispatcher;
_courier = courierSoundListConfig.courierSounds;
_audioTestClipsReferences = audioTestClipsConfig.audioTestClips;
MusicDictionary.Add(MusicType.Menu, new ()); MusicDictionary.Add(MusicType.Menu, new ());
MusicDictionary.Add(MusicType.Peaceful, new ()); MusicDictionary.Add(MusicType.Death, new ());
MusicDictionary.Add(MusicType.Action, new ()); MusicDictionary.Add(MusicType.Action, new ());
MusicDictionary.Add(MusicType.Peaceful, new ());
} }
public async UniTask LoadAudioSourcePrefab(Action<GameObject> callback) => public async UniTask LoadAudioSourcePrefab(Action<GameObject> callback) =>
@@ -77,12 +91,28 @@ namespace Code.Audio
MusicDictionary[MusicType.Peaceful].Clear(); MusicDictionary[MusicType.Peaceful].Clear();
_peacefulMusicHandle.Release(); _peacefulMusicHandle.Release();
} }
public async UniTask WarmAudioTestClips( )
{
// _audioTestClipCounter = 0;
_audioTestClipsHandle = Addressables.LoadAssetsAsync<AudioClip>(_audioTestClipsReferences, OnAudioTestClipLoaded, Addressables.MergeMode.Union);
// foreach (var assetReference in _menuSoundtrack) MenuMusic.Add(await assetReference.LoadAssetAsync<AudioClip>());
var clips = (await _audioTestClipsHandle.ToUniTask()).ToList();
for (int i = 0; i < clips.Count; i++)
AudioTestClips[(AudioType)i] = clips[i];
}
public void ReleaseAudioTestClips()
{
AudioTestClips.Clear();
// foreach (var assetReference in _menuSoundtrack) assetReference.ReleaseAsset();
_audioTestClipsHandle.Release();
}
public async UniTask WarmMenuMusic( ) public async UniTask WarmMenuMusic( )
{ {
_menuMusicHandle = Addressables.LoadAssetsAsync<AudioClip>(_menuSoundtrack, OnMenuTrackLoaded, Addressables.MergeMode.Union); _menuMusicHandle = Addressables.LoadAssetsAsync<AudioClip>(_menuSoundtrack, OnMenuTrackLoaded, Addressables.MergeMode.Union);
// foreach (var assetReference in _menuSoundtrack) MenuMusic.Add(await assetReference.LoadAssetAsync<AudioClip>()); // foreach (var assetReference in _menuSoundtrack) MenuMusic.Add(await assetReference.LoadAssetAsync<AudioClip>());
await _menuMusicHandle.ToUniTask(); await _menuMusicHandle.ToUniTask();
} }
public void ReleaseMenuMusic() public void ReleaseMenuMusic()
{ {
@@ -90,6 +120,16 @@ namespace Code.Audio
// foreach (var assetReference in _menuSoundtrack) assetReference.ReleaseAsset(); // foreach (var assetReference in _menuSoundtrack) assetReference.ReleaseAsset();
_menuMusicHandle.Release(); _menuMusicHandle.Release();
} }
public async UniTask WarmDeathMusic()
{
_deathMusicHandle = Addressables.LoadAssetsAsync<AudioClip>(_deathSoundtrack, OnDeathTrackLoaded, Addressables.MergeMode.Union);
await _deathMusicHandle.ToUniTask();
}
public void ReleaseDeathMusic()
{
MusicDictionary[MusicType.Death].Clear();
_deathMusicHandle.Release();
}
public async UniTask WarmSFX( ) public async UniTask WarmSFX( )
{ {
_SFXHandle = Addressables.LoadAssetsAsync<AudioClip>(_SFX, OnSFXLoaded, Addressables.MergeMode.Union); _SFXHandle = Addressables.LoadAssetsAsync<AudioClip>(_SFX, OnSFXLoaded, Addressables.MergeMode.Union);
@@ -115,6 +155,18 @@ namespace Code.Audio
EnemiesAudios.Clear(); EnemiesAudios.Clear();
_enemiesHandle.Release(); _enemiesHandle.Release();
} }
public async UniTask WarmCourier()
{
_courierHandle = Addressables.LoadAssetsAsync<AudioClip>(_courier, OnCourierLoaded, Addressables.MergeMode.Union);
await _courierHandle.ToUniTask();
}
public void ReleaseCourier()
{
CourierAudios.Clear();
_courierHandle.Release();
}
public async UniTask WarmDispatcher( ) public async UniTask WarmDispatcher( )
{ {
@@ -134,6 +186,16 @@ namespace Code.Audio
{ {
MusicDictionary[MusicType.Menu].Add(audioClip); MusicDictionary[MusicType.Menu].Add(audioClip);
} }
private void OnDeathTrackLoaded(AudioClip audioClip)
{
MusicDictionary[MusicType.Death].Add(audioClip);
}
// private int _audioTestClipCounter;
private void OnAudioTestClipLoaded(AudioClip audioClip)
{
// AudioTestClips[(AudioType)_audioTestClipCounter] = audioClip;_audioTestClipCounter++;
}
private void OnActionTrackLoaded(AudioClip audioClip) private void OnActionTrackLoaded(AudioClip audioClip)
{ {
MusicDictionary[MusicType.Action].Add(audioClip); MusicDictionary[MusicType.Action].Add(audioClip);
@@ -154,5 +216,9 @@ namespace Code.Audio
{ {
DispatcherAudios.Add(audioClip); DispatcherAudios.Add(audioClip);
} }
private void OnCourierLoaded(AudioClip audioClip)
{
CourierAudios.Add(audioClip);
}
} }
} }
+136 -48
View File
@@ -4,6 +4,7 @@ using System.Threading;
using Code.Configs; using Code.Configs;
using Code.Constants; using Code.Constants;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using DG.Tweening;
using Reflex.Attributes; using Reflex.Attributes;
using Unity.VisualScripting; using Unity.VisualScripting;
using UnityEngine; using UnityEngine;
@@ -21,62 +22,104 @@ namespace Code.Audio
private AudioSource _actionMusicSource; private AudioSource _actionMusicSource;
private AudioSource _peacefulMusicSource; private AudioSource _peacefulMusicSource;
private AudioMixerGroup _masterGroup, private Dictionary<AudioType, AudioMixerGroup> _audioMixerGroups = new();
// _musicGroup, private readonly AudioSourcePool _audioSourcePool;
// _actionMusicGroup,
// _peacefulMusicGroup,
_SFXGroup,
_creaturesGroup,
_dispatcherGroup,
_courierGroup,
_enemiesGroup;
private readonly AudioSourcePool _audioSourcePool = new(); // Юнити не может - говнокод поможет.
private Dictionary<AudioSnapshotState, Dictionary<AudioType, float>> _snapshotsVolumes = new();
private Dictionary<AudioSourceType, AudioSourceSettingsConfig> _settings;
private Dictionary<AudioSnapshotState, AudioMixerSnapshot> _snapshots = new(); public AudioService(IAudioFactory audioFactory,
Dictionary<AudioSourceType, AudioSourceSettingsConfig> settings,
public AudioService(IAudioFactory audioFactory) DefaultAudioSettingsConfig defaultAudioSettingsConfig)
{ {
_audioFactory = audioFactory; _audioFactory = audioFactory;
_audioSourcePool = new(settings);
_settings = settings;
_snapshotsVolumes.Add(AudioSnapshotState.None,defaultAudioSettingsConfig.noneVolume);
_snapshotsVolumes.Add(AudioSnapshotState.Menu,defaultAudioSettingsConfig.menuVolume);
_snapshotsVolumes.Add(AudioSnapshotState.Action,defaultAudioSettingsConfig.actionVolume);
_snapshotsVolumes.Add(AudioSnapshotState.Peaceful,defaultAudioSettingsConfig.peacefulVolume);
}
public float GetSetting(AudioType type)
{
_mixer.GetFloat(_audioMixerGroups[type].name, out float value);
value = Mathf.Pow(10f, value / 20);
return value;
}
public void ChangeSetting(AudioType type, float value)
{
float volume = Mathf.Log10(value) * 20;
switch (type)
{
case AudioType.DeathMusic:
case AudioType.Master:
_snapshotsVolumes[AudioSnapshotState.Menu][type] = value;
_snapshotsVolumes[AudioSnapshotState.Action][type] = value;
_snapshotsVolumes[AudioSnapshotState.Peaceful][type] = value;
_mixer.SetFloat(_audioMixerGroups[type].name, volume);
break;
case AudioType.ActionMusic:
_snapshotsVolumes[AudioSnapshotState.Action][type] = value;
_mixer.SetFloat(_audioMixerGroups[type].name, volume);
break;
case AudioType.PeacefulMusic:
_snapshotsVolumes[AudioSnapshotState.Peaceful][type] = value;
_mixer.SetFloat(_audioMixerGroups[type].name, volume);
break;
case AudioType.MenuMusic:
_snapshotsVolumes[AudioSnapshotState.Menu][type] = value;
_mixer.SetFloat(_audioMixerGroups[type].name, volume);
break;
case AudioType.SFX:
case AudioType.Dispatcher:
case AudioType.Courier:
case AudioType.Enemies:
_snapshotsVolumes[AudioSnapshotState.Action][type] = value;
_snapshotsVolumes[AudioSnapshotState.Peaceful][type] = value;
_mixer.SetFloat(_audioMixerGroups[type].name, volume);
break;
}
} }
public async UniTask Initialize() public async UniTask Initialize()
{ {
// _mixer = await _audioFactory.CreateMixer();
_mixer = _audioFactory.AudioMixer; _mixer = _audioFactory.AudioMixer;
_masterGroup = _mixer.FindMatchingGroups(AudioGroups.MasterGroup)[0];
// _actionMusicGroup = _mixer.FindMatchingGroups(AudioGroups.ActionMusicGroup)[0];
// _peacefulMusicGroup = _mixer.FindMatchingGroups(AudioGroups.PeacefulMusoup)[0];
// _musicGroup = _mixer.FindMatchingGroups(AudioGroups.MusicGroup)[0];
_musicAudioGroups.Add(MusicType.Menu, _mixer.FindMatchingGroups(AudioGroups.MenuMusicGroup)[0]); _musicAudioGroups.Add(MusicType.Menu, _mixer.FindMatchingGroups(AudioGroups.MenuMusicGroup)[0]);
_musicAudioGroups.Add(MusicType.Death, _mixer.FindMatchingGroups(AudioGroups.DeathMusicGroup)[0]);
_musicAudioGroups.Add(MusicType.Action, _mixer.FindMatchingGroups(AudioGroups.ActionMusicGroup)[0]); _musicAudioGroups.Add(MusicType.Action, _mixer.FindMatchingGroups(AudioGroups.ActionMusicGroup)[0]);
_musicAudioGroups.Add(MusicType.Peaceful, _mixer.FindMatchingGroups(AudioGroups.PeacefulMusicGroup)[0]); _musicAudioGroups.Add(MusicType.Peaceful, _mixer.FindMatchingGroups(AudioGroups.PeacefulMusicGroup)[0]);
_cancellationTokenSources.Add(MusicType.Menu, new()); _cancellationTokenSources.Add(MusicType.Menu, new());
_cancellationTokenSources.Add(MusicType.Death, new());
_cancellationTokenSources.Add(MusicType.Action, new()); _cancellationTokenSources.Add(MusicType.Action, new());
_cancellationTokenSources.Add(MusicType.Peaceful, new()); _cancellationTokenSources.Add(MusicType.Peaceful, new());
_musicAudioSources.Add(MusicType.Menu, null); _musicAudioSources.Add(MusicType.Menu, null);
_musicAudioSources.Add(MusicType.Death, null);
_musicAudioSources.Add(MusicType.Action, null); _musicAudioSources.Add(MusicType.Action, null);
_musicAudioSources.Add(MusicType.Peaceful, null); _musicAudioSources.Add(MusicType.Peaceful, null);
_SFXGroup = _mixer.FindMatchingGroups(AudioGroups.SFXGroup)[0]; _audioMixerGroups.Add(AudioType.Master, _mixer.FindMatchingGroups(AudioGroups.MasterGroup)[0]);
_creaturesGroup = _mixer.FindMatchingGroups(AudioGroups.CreaturesGroup)[0]; _audioMixerGroups.Add(AudioType.SFX, _mixer.FindMatchingGroups(AudioGroups.SFXGroup)[0]);
_dispatcherGroup = _mixer.FindMatchingGroups(AudioGroups.DispatcherMusicGroup)[0]; _audioMixerGroups.Add(AudioType.Dispatcher, _mixer.FindMatchingGroups(AudioGroups.DispatcherMusicGroup)[0]);
_courierGroup = _mixer.FindMatchingGroups(AudioGroups.CourierMusicGroup)[0]; _audioMixerGroups.Add(AudioType.Courier, _mixer.FindMatchingGroups(AudioGroups.CourierGroup)[0]);
_enemiesGroup = _mixer.FindMatchingGroups(AudioGroups.EnemiesMusicGroup)[0]; _audioMixerGroups.Add(AudioType.Enemies, _mixer.FindMatchingGroups(AudioGroups.EnemiesGroup)[0]);
_audioMixerGroups.Add(AudioType.MenuMusic, _musicAudioGroups[MusicType.Menu]);
_audioMixerGroups.Add(AudioType.DeathMusic, _musicAudioGroups[MusicType.Death]);
_audioMixerGroups.Add(AudioType.ActionMusic, _musicAudioGroups[MusicType.Action]);
_audioMixerGroups.Add(AudioType.PeacefulMusic, _musicAudioGroups[MusicType.Peaceful]);
_snapshots[AudioSnapshotState.None] = _mixer.FindSnapshot(AudioGroups.NoneSnapshot);
_snapshots[AudioSnapshotState.Peaceful] = _mixer.FindSnapshot(AudioGroups.PeacefulSnapshot);
_snapshots[AudioSnapshotState.Action] = _mixer.FindSnapshot(AudioGroups.ActionSnapshot);
_snapshots[AudioSnapshotState.Menu] = _mixer.FindSnapshot(AudioGroups.MenuSnapshot);
await _audioFactory.LoadAudioSourcePrefab(_audioSourcePool.Initialize); await _audioFactory.LoadAudioSourcePrefab(_audioSourcePool.Initialize);
} }
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
private Dictionary<MusicType, CancellationTokenSource> _cancellationTokenSources = new(); private Dictionary<MusicType, CancellationTokenSource> _cancellationTokenSources = new();
private Dictionary<MusicType, AudioSource> _musicAudioSources = new(); private Dictionary<MusicType, AudioSource> _musicAudioSources = new();
private Dictionary<MusicType, AudioMixerGroup> _musicAudioGroups = new(); private Dictionary<MusicType, AudioMixerGroup> _musicAudioGroups = new();
@@ -86,7 +129,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];
} }
@@ -96,19 +139,13 @@ namespace Code.Audio
_musicAudioSources[musicType].Play(); _musicAudioSources[musicType].Play();
// await UniTask.WaitUntilValueChanged(_musicAudioSources[musicType],
// _ => !_musicAudioSources[musicType].isPlaying,
// cancellationToken: _cancellationTokenSources[musicType].Token);
await UniTask.NextFrame(); await UniTask.NextFrame();
// Debug.Log($"Playing {musicType}");
var (isCanceled, _) = await UniTask.WaitUntilValueChanged(_musicAudioSources[musicType], var (isCanceled, _) = await UniTask.WaitUntilValueChanged(_musicAudioSources[musicType],
_ => !_musicAudioSources[musicType].isPlaying, _ => !_musicAudioSources[musicType].isPlaying,
cancellationToken: _cancellationTokenSources[musicType].Token).SuppressCancellationThrow(); cancellationToken: _cancellationTokenSources[musicType].Token).SuppressCancellationThrow();
// Debug.Log($"Finished {musicType}"); Debug.LogError("Music skipping");
if (isCanceled) if (isCanceled)
{ {
// Debug.Log($"Cancelled {musicType}");
_cancellationTokenSources[musicType].Dispose(); _cancellationTokenSources[musicType].Dispose();
_cancellationTokenSources[musicType] = new(); _cancellationTokenSources[musicType] = new();
@@ -118,12 +155,13 @@ namespace Code.Audio
return; return;
} }
PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(musicType); PlayRandomMusicUntilHumanityWillBeDestroyedForTheySins(musicType).Forget();
} }
public async UniTaskVoid GladlyAskToStopMusic(MusicType musicType, AudioSnapshotState audioSnapshotState = AudioSnapshotState.None, float time = 1f, Action callback = null) public async UniTaskVoid GladlyAskToStopMusic(MusicType musicType,
AudioSnapshotState audioSnapshotState = AudioSnapshotState.None, float time = 1f, Action callback = null)
{ {
TransitToSnapshot(audioSnapshotState, time); //TransitToSnapshot(audioSnapshotState, time);
await UniTask.WaitForSeconds(time); await UniTask.WaitForSeconds(time);
@@ -131,42 +169,92 @@ namespace Code.Audio
callback?.Invoke(); callback?.Invoke();
} }
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public void StopMusic() public void StopMusic()
{ {
_peacefulMusicSource.Stop(); _peacefulMusicSource.Stop();
_actionMusicSource.Stop(); _actionMusicSource.Stop();
_cancellationTokenSources[MusicType.Peaceful].Cancel();
_cancellationTokenSources[MusicType.Action].Cancel();
} }
public void GladlyAskToStopMusic(Action callback = null) public void GladlyAskToStopMusic(Action callback = null)
{ {
TransitToSnapshot(AudioSnapshotState.None); // TransitToSnapshot(AudioSnapshotState.None);
_peacefulMusicSource.Stop(); _peacefulMusicSource.Stop();
_actionMusicSource.Stop(); _actionMusicSource.Stop();
callback?.Invoke(); callback?.Invoke();
} }
public void TransitToSnapshot(AudioSnapshotState state, float time = 1f) public void TransitToSnapshot(AudioSnapshotState state, float time = 1)
{ {
_snapshots[state].TransitionTo(time); foreach (var key in _audioMixerGroups.Keys)
_mixer.DOSetFloat(_audioMixerGroups[key].name, Mathf.Log10(_snapshotsVolumes[state][key]) * 20, time);
} }
#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) public void PlaySFX(SFXSounds sfxId, Vector3 position)
{ {
_audioSourcePool.PlayOneShot(_audioFactory.SFXAudios[(int)sfxId], position, _SFXGroup); _audioSourcePool
.PlayOneShot(_audioFactory.SFXAudios[(int)sfxId], position, _audioMixerGroups[AudioType.SFX]).Forget();
}
public void ApplyStun(float value, float time)
{
_mixer.DOSetFloat("Stun", value, time);
} }
public void PlayEnemy(EnemiesSounds enemySoundId, Vector3 position) public void PlayEnemy(EnemiesSounds enemySoundId, Vector3 position)
{ {
_audioSourcePool.PlayOneShot(_audioFactory.EnemiesAudios[(int)enemySoundId], position, _enemiesGroup); _audioSourcePool.PlayOneShot(_audioFactory.EnemiesAudios[(int)enemySoundId], position,
_audioMixerGroups[AudioType.Enemies]).Forget();
} }
public AudioSource GetSourceOfGroup(AudioType group)
{
AudioSource source = _audioSourcePool.GetSource();
source.outputAudioMixerGroup = _audioMixerGroups[group];
return source;
}
public void ReleaseSourceOfGroup(AudioSource source) => _audioSourcePool.ReleaseSource(source);
// Чинить долго и дорого пусть Артёмов будет много, ведь он наш друг
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],
_audioMixerGroups[AudioType.Dispatcher],
AudioSourceType.NoBlend, () => OnDispatcherEnded?.Invoke()).Forget();
}
public void PlayCourier(CourierSounds courierSounds)
{
_audioSourcePool.PlayOneShot(_audioFactory.CourierAudios[(int)courierSounds],
_audioMixerGroups[AudioType.Courier],
AudioSourceType.NoBlend).Forget();
}
private CancellationTokenSource _testClipCancellationTokenSource = new();
private void ResetCurrentTestAudioSource() => _currentTestClipAudioType = AudioType.Master;
private AudioType _currentTestClipAudioType;
// TODO Засунуть в словарь
public void PlayTestClip(AudioType audioType)
{
if (_currentTestClipAudioType == audioType) return;
_testClipCancellationTokenSource.Cancel();
_testClipCancellationTokenSource = new();
_currentTestClipAudioType = audioType;
_audioSourcePool.PlayOneShot(_audioFactory.AudioTestClips[audioType],
_audioMixerGroups[audioType], _testClipCancellationTokenSource,
AudioSourceType.NoBlend, ResetCurrentTestAudioSource).Forget();
} }
} }
} }
@@ -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,12 @@
using System;
using System.Collections.Generic;
using System.Threading;
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 +16,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,28 +64,56 @@ 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);
}
public async UniTaskVoid PlayOneShot(AudioSource audioSource, AudioClip clip, AudioMixerGroup audioMixerGroup, AudioSourceType audioSourceType = AudioSourceType.NoBlend, Action onComplete = null)
{
audioSource.PlayOneShot(clip);
audioSource.spatialBlend = _settings[audioSourceType].spatialBlend;
audioSource.outputAudioMixerGroup = audioMixerGroup;
// TODO Потом сделать. Можно с cancellation, но сейчас лень. Ну правда
await UniTask.WaitUntilValueChanged(audioSource, x => x.isPlaying);
if (!audioSource) return;
_pool.Release(audioSource);
onComplete?.Invoke();
}
public async UniTaskVoid PlayOneShot(AudioClip clip, AudioMixerGroup audioMixerGroup, CancellationTokenSource cancellationToken, AudioSourceType audioSourceType = AudioSourceType.NoBlend, Action onComplete = null)
{
AudioSource source = _pool.Get();
source.PlayOneShot(clip);
source.spatialBlend = _settings[audioSourceType].spatialBlend;
source.outputAudioMixerGroup = audioMixerGroup;
var (isCanceled, _) = await UniTask.WaitUntilValueChanged(source, x => x.isPlaying, cancellationToken: cancellationToken.Token).SuppressCancellationThrow();
_pool.Release(source); _pool.Release(source);
if(!isCanceled) onComplete?.Invoke();
else cancellationToken.Dispose();
} }
public AudioSource GetSource() => _pool.Get(); public AudioSource GetSource() => _pool.Get();
public void ReleaseSource(AudioSource source) => _pool.Release(source); public void ReleaseSource(AudioSource source) => _pool.Release(source);
@@ -0,0 +1,8 @@
namespace Code.Audio
{
public enum AudioSourceType
{
Full3D,
NoBlend
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 260bbb19b55049928b9ab4d55b32fc3f
timeCreated: 1763849966
@@ -8,14 +8,19 @@ namespace Code.Audio
{ {
public interface IAudioFactory public interface IAudioFactory
{ {
AudioMixer AudioMixer { get; }
Dictionary<AudioType, AudioClip> AudioTestClips { get; }
Dictionary<MusicType, List<AudioClip>> MusicDictionary { get; }
List<AudioClip> SFXAudios { get; } List<AudioClip> SFXAudios { get; }
List<AudioClip> EnemiesAudios { get; } List<AudioClip> EnemiesAudios { get; }
List<AudioClip> DispatcherAudios { get; } List<AudioClip> DispatcherAudios { get; }
AudioMixer AudioMixer { get; } List<AudioClip> CourierAudios { get; }
Dictionary<MusicType, List<AudioClip>> MusicDictionary { get; }
UniTask LoadAudioSourcePrefab(Action<GameObject> callback); UniTask LoadAudioSourcePrefab(Action<GameObject> callback);
UniTask WarmGameplayMusic( ); UniTask WarmGameplayMusic( );
void ReleaseActionMusic(); void ReleaseActionMusic();
void ReleasePeacefulMusic();
UniTask WarmAudioTestClips( );
void ReleaseAudioTestClips();
UniTask WarmMenuMusic( ); UniTask WarmMenuMusic( );
void ReleaseMenuMusic(); void ReleaseMenuMusic();
UniTask WarmSFX( ); UniTask WarmSFX( );
@@ -24,6 +29,9 @@ namespace Code.Audio
void ReleaseEnemies(); void ReleaseEnemies();
UniTask WarmDispatcher( ); UniTask WarmDispatcher( );
void ReleaseDispatcher(); void ReleaseDispatcher();
void ReleasePeacefulMusic(); UniTask WarmCourier();
void ReleaseCourier();
UniTask WarmDeathMusic( );
void ReleaseDeathMusic();
} }
} }
@@ -14,6 +14,18 @@ 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);
void ChangeSetting(AudioType type, float value);
float GetSetting(AudioType type);
void PlayTestClip(AudioType audioType);
AudioSource GetSourceOfGroup(AudioType group);
void ReleaseSourceOfGroup(AudioSource source);
void PlayCourier(CourierSounds range);
void ApplyStun(float value, float time);
} }
} }
+14 -1
View File
@@ -4,6 +4,19 @@ namespace Code.Audio
{ {
Menu, Menu,
Peaceful, Peaceful,
Action Action,
Death
}
public enum AudioType
{
Master,
MenuMusic,
Dispatcher,
SFX,
Courier,
Enemies,
ActionMusic,
PeacefulMusic,
DeathMusic
} }
} }
@@ -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,11 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace Code.Configs
{
[CreateAssetMenu(menuName = "Config/Audio/AudioTestClips", fileName = "AudioTestClipsConfig")]
public class AudioTestClipsConfig : ScriptableObject
{
public AssetReference[] audioTestClips;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f2f1c08a20a54a708336caf8ca6499e1
timeCreated: 1764263243
@@ -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); // Вектор, отвечающий за применение гравитации
@@ -1,11 +0,0 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace Code.Configs
{
[CreateAssetMenu(menuName = "Config/Audio/CharacterSoundList", fileName = "CharacterSoundListConfig")]
public class CharacterSoundListConfig : ScriptableObject
{
public AssetReference[] character;
}
}
@@ -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,11 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
namespace Code.Configs
{
[CreateAssetMenu(menuName = "Config/Audio/CourierSoundListConfig", fileName = "CourierSoundListConfig")]
public class CourierSoundListConfig : ScriptableObject
{
public AssetReference[] courierSounds;
}
}
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using AYellowpaper.SerializedCollections;
using UnityEngine;
using AudioType = Code.Audio.AudioType;
namespace Code.Configs
{
[CreateAssetMenu(fileName = "DefaultAudioSettingsConfig", menuName = "Audio/DefaultAudioSettings")]
public class DefaultAudioSettingsConfig : ScriptableObject
{
[Header("Menu")]
[SerializedDictionary("AudioType", "Volume")]
public SerializedDictionary<Audio.AudioType, float> menuVolume = new();
[Header("Action")]
[SerializedDictionary("AudioType", "Volume")]
public SerializedDictionary<Audio.AudioType, float> actionVolume = new();
[Header("Peaceful")]
[SerializedDictionary("AudioType", "Volume")]
public SerializedDictionary<Audio.AudioType, float> peacefulVolume = new();
[Header("None")]
[SerializedDictionary("AudioType", "Volume")]
public SerializedDictionary<Audio.AudioType, float> noneVolume = new();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 36d35c52933d4d4ba836f75b05c4810f
timeCreated: 1764754933
@@ -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
@@ -0,0 +1,10 @@
using UnityEngine;
namespace Code.Configs
{
[CreateAssetMenu(menuName = "Config/Audio/MusicPlayer", fileName = "MusicPlayerConfig")]
public class MusicPlayerConfig : ScriptableObject
{
public float threshold = 0.5f;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8708e3c129d241b08563f1a170bd3a44
timeCreated: 1764585060
@@ -8,6 +8,7 @@ namespace Code.Configs
{ {
public AssetReference[] peacefulSoundtrack; public AssetReference[] peacefulSoundtrack;
public AssetReference[] actionSoundtrack; public AssetReference[] actionSoundtrack;
public AssetReference[] deathSoundtrack;
public AssetReference[] menuSoundtrack; public AssetReference[] menuSoundtrack;
} }
} }
@@ -19,13 +19,14 @@ namespace Code.Constants
public const string PeacefulMusicGroup = "Peaceful"; public const string PeacefulMusicGroup = "Peaceful";
public const string ActionMusicGroup = "Action"; public const string ActionMusicGroup = "Action";
public const string DispatcherMusicGroup = "Dispatcher"; public const string DispatcherMusicGroup = "Dispatcher";
public const string CourierMusicGroup = "Courier"; public const string CourierGroup = "Courier";
public const string EnemiesMusicGroup = "Enemies"; public const string EnemiesGroup = "Enemies";
public const string PeacefulSnapshot = "Peaceful"; public const string PeacefulSnapshot = "Peaceful";
public const string ActionSnapshot = "Action"; public const string ActionSnapshot = "Action";
public const string NoneSnapshot = "None"; public const string NoneSnapshot = "None";
public const string MenuSnapshot = "Menu"; public const string MenuSnapshot = "Menu";
public const string MenuMusicGroup = "Menu"; public const string MenuMusicGroup = "Menu";
public const string DeathMusicGroup = "Death";
} }
} }
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bb5ad0cc3a72448a93ee2adb560c35ad
timeCreated: 1764345160
@@ -0,0 +1,12 @@
using UnityEngine;
namespace Code.DebugAndTest
{
public class TriggerTest : MonoBehaviour
{
private void OnTriggerEnter(Collider other)
{
Debug.Log(gameObject.name + " found " + other.name);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6bb3c649d82049079d00b843a76aced8
timeCreated: 1764345168
@@ -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,9 @@ namespace Code.Gameplay.Camera.CameraStates
TargetDistance = 0f; TargetDistance = 0f;
CurrentDistance = 0f; CurrentDistance = 0f;
base.EnterState(); Camera.fieldOfView = Context.CameraSettingsConfigs[CameraMode.FirstPerson].fov;
Cursor.lockState = CursorLockMode.Locked;
} }
protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation) protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation)
@@ -2,14 +2,16 @@ 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;
Cursor.lockState = CursorLockMode.None;
} }
protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation) protected override void HandlePosition(float deltaTime, float zoomInput, Quaternion targetRotation)
@@ -17,7 +19,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,10 @@ 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;
Cursor.lockState = CursorLockMode.None;
} }
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 = 1.75f;
_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;
}
} }
@@ -0,0 +1,37 @@
using DG.Tweening;
using UnityEngine;
namespace Code.Gameplay.Door
{
public class Door : MonoBehaviour
{
[SerializeField] private Transform doorPolotno;
private Vector3 _doorInitialPosition;
private Quaternion _doorInitialRotation;
[SerializeField] private Transform hingeJointAnchor;
[SerializeField] private HingeJoint hingeJoint;
[SerializeField] private Rigidbody rigidbody;
public void TranslationMode(bool on)
{
if (on)
{
rigidbody.isKinematic = true;
return;
}
_doorInitialPosition = doorPolotno.position;
_doorInitialRotation = doorPolotno.rotation;
rigidbody.isKinematic = false;
hingeJoint.autoConfigureConnectedAnchor = false;
hingeJoint.connectedAnchor = hingeJointAnchor.position;
}
public void CloseTheDoor()
{
TranslationMode(true);
doorPolotno.DORotate(_doorInitialRotation.eulerAngles, 0.5f);
doorPolotno.DOMove(_doorInitialPosition, 0.5f);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bc9fc6ce2cc24b68b2b330e4fd029b80
timeCreated: 1764304430
@@ -0,0 +1,53 @@
using System;
using DG.Tweening;
using UnityEngine;
namespace Code.Gameplay.Door
{
public class DoorDebug : MonoBehaviour
{
[SerializeField] private Transform doorSelf;
[SerializeField] private Transform doorPolotno;
private Vector3 _doorInitialPosition;
private Quaternion _doorInitialRotation;
[SerializeField] private Transform hingeJointAnchor;
[SerializeField] private HingeJoint hingeJoint;
[SerializeField] private Rigidbody rigidbody;
[SerializeField] private bool debug;
[SerializeField] private bool closed;
private void Start()
{
_doorInitialPosition = doorPolotno.position;
_doorInitialRotation = doorPolotno.rotation;
}
private void Update()
{
if(debug && !closed)
{
CloseTheDoor();
}
}
public void TranslationMode(bool on)
{
if (on)
{
rigidbody.isKinematic = true;
return;
}
_doorInitialPosition = doorPolotno.position;
_doorInitialRotation = doorPolotno.rotation;
rigidbody.isKinematic = false;
hingeJoint.autoConfigureConnectedAnchor = false;
hingeJoint.connectedAnchor = hingeJointAnchor.position;
}
public void CloseTheDoor()
{
// TranslationMode(true);
doorPolotno.DORotate(_doorInitialRotation.eulerAngles, 0.5f);
doorPolotno.DOMove(_doorInitialPosition, 0.5f);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ed06ed70c7924ca9bc59f33ee7e6cdfa
timeCreated: 1764533216
@@ -0,0 +1,7 @@
namespace Code.Gameplay.Door
{
public class DoorPooler
{
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: acfad6f51f224b51abb761fc22e4356f
timeCreated: 1764521809
@@ -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,3 @@
fileFormatVersion: 2
guid: f17ace0fa75741f2a730ff21181c6e8c
timeCreated: 1764575680
@@ -0,0 +1,23 @@
using UnityEngine;
using UnityEngine.AI;
namespace Code.Gameplay.Enemies.Animators
{
public class EnemyAnimator : MonoBehaviour
{
private static readonly int Velocity = Animator.StringToHash("Velocity");
[SerializeField] private Animator animator;
[SerializeField] private NavMeshAgent agent;
private void SetVelocity(float velocity)
{
animator.SetFloat(Velocity, velocity);
}
private void Update()
{
if(agent) SetVelocity(agent.velocity.magnitude);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 936125a7ed844674b0e0586a484253ce
timeCreated: 1764575708
@@ -0,0 +1,206 @@
using Code.Audio;
using UnityEngine;
using UnityEngine.AI;
using Code.Gameplay.Player;
using Code.Gameplay.Character.Controller;
using Code.Gameplay.Character.Core;
using Code.Gameplay.Enemies.Animators;
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;
[Inject] protected IAudioService audioService;
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;
protected 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()
{
// // TODO Мешает
// 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) return;
_attackCooldownTimer -= Time.deltaTime;
if (_attackCooldownTimer <= 0)
{
_canAttack = true;
}
}
protected virtual void DealDamage()
{
if (PlayerHealth == null) return;
PlayerHealth.GetDamage(Damage);
ApplyEffectsToPlayer();
_canAttack = false;
_attackCooldownTimer = AttackCooldown;
}
protected virtual void ApplyEffectsToPlayer()
{
if (PlayerController)
PlayerController.BlockAbilities(3f); // TODO в конфиг
}
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,58 @@
using Code.Audio;
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) return;
var walkableArea = NavMesh.GetAreaFromName("Cockroach");
agent.areaMask = 1 << walkableArea;
}
protected override void HandleAttack()
{
if (!Player || !_canAttack) return;
var distance = Vector3.Distance(transform.position, Player.transform.position);
if (!(distance <= AttackRange)) return;
audioService.PlayEnemy(EnemiesSounds.RoachAttack, agent.transform.position);
DealDamage();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 119c0b675c944ed7ada6cdc8db4b04aa
timeCreated: 1763228933
@@ -0,0 +1,61 @@
using Code.Audio;
using Code.Configs;
using Code.Gameplay.Enemies.Animators;
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 || !_canAttack) return;
var distance = Vector3.Distance(transform.position, Player.transform.position);
if (!(distance <= AttackRange)) return;
audioService.PlayEnemy(EnemiesSounds.GrandpaAttack,agent.transform.position);
if (Player.CustomCharacterController.IsSliding)
ApplyStun();
else
DealDamage();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e77164596e249bca15e09cdfc9ddac2
timeCreated: 1763228910
@@ -0,0 +1,34 @@
using Code.Audio;
using Reflex.Attributes;
using UnityEngine;
namespace Code.Gameplay.Enemies
{
public class EnemyAudio : MonoBehaviour
{
private IAudioService _audioService;
private IAudioFactory _audioFactory;
[SerializeField] private AudioSource _audioSource;
[SerializeField] private EnemiesSounds stepId;
[Inject]
private void Construct(IAudioService audioService, IAudioFactory audioFactory)
{
_audioService = audioService;
_audioSource = audioService.GetSourceOfGroup(Audio.AudioType.Enemies);
_audioSource.transform.position = transform.position;
_audioSource.spatialBlend = 0;
_audioFactory = audioFactory;
}
public void PlayStep()
{
_audioSource.transform.position = transform.position;
_audioSource.PlayOneShot(_audioFactory.EnemiesAudios[(int)stepId]);
}
private void OnDestroy()
{
_audioService.ReleaseSourceOfGroup(_audioSource);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 91cb250df1b24fc4825ee791a12dda11
timeCreated: 1764695102
@@ -0,0 +1,57 @@
using System;
using Code.Configs;
using Cysharp.Threading.Tasks;
using Reflex.Attributes;
using Reflex.Core;
using Reflex.Extensions;
using Reflex.Injectors;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.SceneManagement;
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();
GameObjectInjector.InjectRecursive(enemyObj, SceneManager.GetActiveScene().GetSceneContainer());
return enemyObj.GetComponent<BaseEnemy>();
}
public void ReleaseEnemy(BaseEnemy enemy)
{
if (!enemy) 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,88 @@
using Code.Gameplay.Player;
using Cysharp.Threading.Tasks;
using Reflex.Attributes;
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;
// GameObjectSelfInjector существует
// 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 OnDestroy()
{
Despawn();
}
private void Despawn()
{
if (!_spawnedEnemy) 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
Loaded 100 of 844 files, more files were not shown because too many files have changed in this diff. Show more