Развитие гоночек

This commit is contained in:
ThomasCharester committed 2026-02-06 10:30:50 +03:00
1 parent ec60c47652
commit b54083521f
532 files changed
+16091 -28231

No files matched your search

@@ -1,5 +1,6 @@
using Code.Infrastructure.Audio;
using Code.Infrastructure.Camera;
using Code.Infrastructure.Input;
using Code.Infrastructure.UI;
using Reflex.Attributes;
@@ -19,6 +20,7 @@ namespace Code.Infrastructure.App
[Inject] public IUIService UIService { get; set; }
[Inject] public IAudioFactory AudioFactory { get; set; }
[Inject] public IAudioService AudioService { get; set; }
[Inject] public IInputService InputService { get; set; }
}
[Generators.StateMachine(typeof(AppStateMachineContext),typeof(AppStates))]
@@ -1,3 +1,4 @@
using Code.Debug;
using Code.Infrastructure.Audio;
using Code.Infrastructure.Camera;
using Code.Infrastructure.Configs;
@@ -114,7 +115,7 @@ namespace Code.Infrastructure.App
{
_thatRightContainer.Resolve<CameraStateMachineContext>().cameraLoadedData = data;
_thatRightContainer.Resolve<CameraStateMachine>().SwitchState(CameraMode.None)
.AllowStateTransition(CameraMode.FirstPerson);
.AllowStateTransition(CameraMode.ThirdPerson);
data.Camera.GetUniversalAdditionalCameraData().renderPostProcessing = true;
@@ -128,6 +129,7 @@ namespace Code.Infrastructure.App
_initialLoading.DoProgress();
_thatRightContainer.Resolve<IUIService>().TryLoadPanel(UIPanelType.Logo);
_thatRightContainer.Resolve<IUIService>().TryLoadPanel(UIPanelType.Loading);
}
public void UILoaded(UIPanelType uiPanelType)
@@ -1,19 +1,34 @@
using Code.Debug;
using Code.Example.Race;
using Code.Example.Race.Racer;
using Code.Example.Race.Track;
using Code.Infrastructure.SceneLoader;
using Reflex.Core;
using Reflex.Enums;
using UnityEngine;
using Resolution = Reflex.Enums.Resolution;
namespace Code.Infrastructure.App.SceneInstallers
{
public class GameplaySceneInstaller : MonoBehaviour, IInstaller
{
[SerializeField] private RaceTrackData _raceTrackData;
public void InstallBindings(ContainerBuilder containerBuilder)
{
{
containerBuilder.RegisterType(typeof(RacerFactory), new[] { typeof(IRacerFactory) }, Lifetime.Scoped,
Resolution.Lazy);
containerBuilder.RegisterType(typeof(RaceScores), Lifetime.Scoped,
Resolution.Lazy);
containerBuilder.RegisterValue(_raceTrackData);
containerBuilder.OnContainerBuilt += PostInstall;
}
private void PostInstall(Container container)
{
{
container.Resolve<EventBus.EventBus>()
.RaiseEvent<IOnGameplaySceneLoadedHandler>(x => x.OnGameplaySceneInitialized(container));
}
}
}
@@ -1,8 +1,12 @@
using System.Threading;
using Code.Example.Race.Racer;
using Code.Example.Race.Track;
using Code.Infrastructure.Audio;
using Code.Infrastructure.Generators;
using Code.Infrastructure.SceneLoader;
using Code.Infrastructure.UI;
using Reflex.Core;
using UnityEngine.SceneManagement;
namespace Code.Infrastructure.App.States
{
@@ -29,7 +33,7 @@ namespace Code.Infrastructure.App.States
protected override void OnExitState()
{
Context.AudioFactory.ReleaseAudios(AudioNames.UISounds);
// Context.AudioFactory.ReleaseAudios(AudioNames.UISounds);
Context.UIService.TryRemovePanel(UIPanelType.DebugSceneChooser);
_eventBus.Unsubscribe(this);
}
@@ -40,10 +44,10 @@ namespace Code.Infrastructure.App.States
Context.UIService.OpenPanel(uiPanelType);
}
public void OnReadyToPlay(AudioNames groupName)
{
Context.AudioService.PlayRepeatedly(AudioNames.UISounds,0,new CancellationTokenSource());
}
}
}
@@ -1,31 +1,102 @@
using Code.Example.Race;
using Code.Example.Race.Racer;
using Code.Example.Race.Track;
using Code.Infrastructure.Camera;
using Code.Infrastructure.Camera.CameraStates;
using Code.Infrastructure.Generators;
using Code.Infrastructure.SceneLoader;
using UnityEngine;
using Code.Infrastructure.Input;
using Code.Infrastructure.Loadings;
using Code.Infrastructure.SceneLoader;
using Code.Infrastructure.UI;
using Reflex.Core;
using Reflex.Injectors;
using UnityEngine.SceneManagement;
namespace Code.Infrastructure.App.States
{
[State(typeof(AppStates),typeof(AppStateMachineContext))]
[StateRaisingEvent]
public partial class GameplayState : IOnGameplaySceneLoadedHandler
[State(typeof(AppStates), typeof(AppStateMachineContext))]
[StateRaisingEvent]
public partial class GameplayState : IOnGameplaySceneLoadedHandler, IRaceGameInitializedHandle, IRacerFactoryInitializedHandle, IUIPanelLoaded
{
public GameplayState(AppStateMachineContext context, EventBus.EventBus eventBus) : base(context, eventBus)
{
}
public override AppStates myState => AppStates.Gameplay;
private Loading _gameplayLoading;
protected override void OnEnterState()
{
EventBus.Subscribe(this);
Context.UIService.OpenPanel(UIPanelType.Loading);
// Scene
// RaceGame
// Factory
// PlayerCounter
_gameplayLoading = new Loading(4, GameplayLoadingCompleted);
Context.UIService.TryLoadPanel(UIPanelType.PlayerScoreCounter);
Context.SceneLoader.LoadSceneAsync(SceneId.Gameplay, LoadSceneMode.Single).Forget();
Context.InputService.EnableMap(ActionMappings.Gameplay);
}
private void GameplayLoadingCompleted()
{
_raceGame.PlaceRacers();
Context.Camera.SwitchState(CameraMode.ThirdPerson).AllowStateTransition(CameraMode.None);
((ThirdPersonState)Context.Camera.CurrentBaseState).SetFollowTarget(_raceGame.PlacedRacers[0].gameObject.transform, _raceGame.PlacedRacers[0].CarRigidbody);
Context.UIService.ClosePanel(UIPanelType.Loading);
_raceGame.StartRacers();
}
protected override void OnExitState()
{
Context.InputService.DisableMap(ActionMappings.Gameplay);
Context.UIService.TryRemovePanel(UIPanelType.PlayerScoreCounter);
EventBus.Unsubscribe(this);
}
public void OnGameplaySceneLoaded()
{
}
private RaceTrackData _raceTrackData;
private RaceGame _raceGame;
private Container UNTILTHEYFIXCONTAINERS;
public void OnGameplaySceneInitialized(Container container)
{
_raceTrackData = container.Resolve<RaceTrackData>();
Context.Camera.Camera.transform.position = _raceTrackData.CameraPosition.transform.position;
Context.Camera.Camera.transform.rotation = _raceTrackData.CameraPosition.transform.rotation;
UNTILTHEYFIXCONTAINERS = container;
_gameplayLoading.DoProgress();
container.Resolve<IRacerFactory>();
}
public void RaceGameReady()
{
_gameplayLoading.DoProgress();
}
public void RacerFactoryInitialized()
{
_raceGame = (RaceGame)ConstructorInjector.Construct(typeof(RaceGame), UNTILTHEYFIXCONTAINERS);
_gameplayLoading.DoProgress();
}
public void UILoaded(UIPanelType uiPanelType)
{
if (uiPanelType != UIPanelType.PlayerScoreCounter) return;
Context.UIService.OpenPanel(UIPanelType.PlayerScoreCounter);
_gameplayLoading.DoProgress();
}
}
}
@@ -11,10 +11,9 @@ namespace Code.Infrastructure.Audio
[ToConfig] public int id;
[ToConfig] public AssetReference fileReference;
}
[Config("Configs/Audio/AudioServiceSettings")]
public class AudioServiceSettings
[Config("Configs/Audio/AudioSourceSettings")]
public class AudioSourcePrefab
{
[ToConfig] public AssetReference sourcePrefab;
[ToConfig] public AssetReference mixerPrefab;
}
}
@@ -18,6 +18,8 @@ namespace Code.Infrastructure.Audio
UniTaskVoid PlayRepeatedly(AudioNames groupName, int id,
CancellationTokenSource cancellationTokenSource, AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null);
bool CheckClip(AudioNames groupName, int id);
}
public partial class AudioService : IAudioClipsEventHandle, IAudioService
@@ -40,7 +42,7 @@ namespace Code.Infrastructure.Audio
for (int i = 0; i < typeof(AudioNames).GetEnumValues().Length; i++)
_audioClips.Add((AudioNames)i, new Dictionary<int, AudioClip>());
InitializeMixer();
//InitializeMixer();
}
private partial void InitializeMixer();
@@ -53,6 +55,8 @@ namespace Code.Infrastructure.Audio
AudioSourceType sourceType = AudioSourceType.NoBlend,
AudioSource audioSource = null)
{
if (!CheckClip(groupName, id)) return;
if (!audioSource) audioSource = _pool.GetSource(sourceType);
audioSource.transform.position = position;
@@ -67,6 +71,8 @@ namespace Code.Infrastructure.Audio
public async UniTaskVoid PlayRepeatedly(AudioNames groupName, int id,
CancellationTokenSource cancellationTokenSource, AudioSourceType sourceType = AudioSourceType.NoBlend, AudioSource audioSource = null)
{
if (!CheckClip(groupName, id)) return;
if (!audioSource) audioSource = _pool.GetSource(sourceType);
if(!audioSource.resource) audioSource.resource = GetClip(groupName, id);
@@ -91,6 +97,7 @@ namespace Code.Infrastructure.Audio
}
public AudioClip GetClip(AudioNames groupName, int id) => _audioClips[groupName][id];
public bool CheckClip(AudioNames groupName, int id) => _audioClips[groupName].ContainsKey(id);
public void AudioClipsLoaded(AudioNames groupname, Dictionary<int, AudioClip> clips)
{
@@ -100,6 +107,7 @@ namespace Code.Infrastructure.Audio
public void AudioClipsUnLoaded(AudioNames groupName)
{
_audioClips[groupName].Clear();
_audioClips[groupName] = null;
}
}
@@ -33,7 +33,7 @@ namespace Code.Infrastructure.Audio
private string _configsLabel;
private AsyncOperationHandle<IList<AudioSourceSettingsConfig>> _handles;
private AssetReference _sourcePrefabConfigReference;
private AsyncOperationHandle<AudioServiceSettingsConfig> _prefabConfigHandle;
private AsyncOperationHandle<AudioSourcePrefabConfig> _prefabConfigHandle;
private AsyncOperationHandle<GameObject> _prefabHandle;
private EventBus.EventBus _eventBus;
@@ -42,14 +42,14 @@ namespace Code.Infrastructure.Audio
_eventBus = eventBus;
_configsLabel = configLocator.TryGetManyConfigAssetReferences<AudioSourceSettingsConfig>();
_sourcePrefabConfigReference = configLocator.TryGetConfigAssetReference<AudioServiceSettingsConfig>();
_sourcePrefabConfigReference = configLocator.TryGetConfigAssetReference<AudioSourcePrefabConfig>();
LoadConfigs().Forget();
}
private async UniTaskVoid LoadConfigs()
{
_prefabConfigHandle = Addressables.LoadAssetAsync<AudioServiceSettingsConfig>(_sourcePrefabConfigReference);
_prefabConfigHandle = Addressables.LoadAssetAsync<AudioSourcePrefabConfig>(_sourcePrefabConfigReference);
_handles = Addressables.LoadAssetsAsync<AudioSourceSettingsConfig>(_configsLabel);
await UniTask.WhenAll(
@@ -102,58 +102,7 @@ namespace Code.Infrastructure.Audio
{
Object.Destroy(source.gameObject);
}
//
// public async UniTaskVoid PlayOneShot(AudioClip clip, Vector3 position, AudioMixerGroup audioMixerGroup, AudioSourceType audioSourceType = AudioSourceType.NoBlend, Action onComplete = null)
// {
// AudioSource source = _pool.Get();
// source.PlayOneShot(clip);
// source.spatialBlend = _settings[audioSourceType].spatialBlend;
// source.transform.position = position;
// source.outputAudioMixerGroup = audioMixerGroup;
//
// await UniTask.WaitUntilValueChanged(source, x => x.isPlaying);
// onComplete?.Invoke();
// _pool.Release(source);
// }
// public async UniTaskVoid PlayOneShot(AudioClip clip, AudioMixerGroup audioMixerGroup, AudioSourceType audioSourceType = AudioSourceType.NoBlend, Action onComplete = null)
// {
// AudioSource source = _pool.Get();
// source.PlayOneShot(clip);
// source.spatialBlend = _settings[audioSourceType].spatialBlend;
// source.outputAudioMixerGroup = audioMixerGroup;
//
// 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);
//
// if(!isCanceled) onComplete?.Invoke();
// else cancellationToken.Dispose();
// }
public AudioSource GetSource(AudioSourceType sourceType)
{
var source = _pool.Get();
@@ -10,9 +10,7 @@ namespace Code.Infrastructure.Camera
public enum CameraMode
{
None,
FirstPerson,
Static,
Cinematic
ThirdPerson
}
[Config("Configs/Camera/CameraConfig")]
@@ -0,0 +1,174 @@
using UnityEngine;
public class RacingCamera : MonoBehaviour
{
public enum CameraMode
{
Follow,
Orbit,
Hood,
Bumper
}
[Header("Target")]
public Transform carTarget;
public Rigidbody carRigidbody;
[Header("Camera Mode")]
public CameraMode currentMode = CameraMode.Follow;
public KeyCode switchKey = KeyCode.C;
[Header("Follow Mode Settings")]
public Vector3 followOffset = new Vector3(0, 3, -8);
public float followSmoothTime = 0.1f;
[Header("Orbit Mode Settings")]
public float orbitDistance = 10f;
public float orbitHeight = 3f;
public float orbitSpeed = 2f;
private float orbitAngle = 0f;
[Header("First Person Settings")]
public Transform hoodCameraPosition;
public Transform bumperCameraPosition;
[Header("Look Settings")]
public float lookAheadDistance = 5f;
public Vector3 lookAtOffset = new Vector3(0, 1.5f, 0);
private Vector3 velocity = Vector3.zero;
private Camera cam;
void Start()
{
cam = GetComponent<Camera>();
if (carRigidbody == null && carTarget != null)
carRigidbody = carTarget.GetComponent<Rigidbody>();
}
void Update()
{
// Переключение режимов камеры
if (Input.GetKeyDown(switchKey))
{
SwitchCameraMode();
}
}
void LateUpdate()
{
if (carTarget == null) return;
switch (currentMode)
{
case CameraMode.Follow:
UpdateFollowCamera();
break;
case CameraMode.Orbit:
UpdateOrbitCamera();
break;
case CameraMode.Hood:
UpdateFirstPersonCamera(hoodCameraPosition);
break;
case CameraMode.Bumper:
UpdateFirstPersonCamera(bumperCameraPosition);
break;
}
}
void SwitchCameraMode()
{
currentMode = (CameraMode)(((int)currentMode + 1) % 4);
Debug.Log("Camera mode: " + currentMode);
}
void UpdateFollowCamera()
{
// Рассчитываем позицию с учетом скорости
Vector3 targetPosition = CalculateFollowPosition();
// Плавное перемещение
transform.position = Vector3.SmoothDamp(
transform.position,
targetPosition,
ref velocity,
followSmoothTime
);
// Направление взгляда с опережением
Vector3 lookTarget = carTarget.position +
carTarget.forward * lookAheadDistance +
lookAtOffset;
transform.LookAt(lookTarget);
}
Vector3 CalculateFollowPosition()
{
Vector3 basePosition = carTarget.position +
carTarget.TransformDirection(followOffset);
// Динамическое смещение на основе скорости
if (carRigidbody != null)
{
float speedFactor = Mathf.Clamp01(carRigidbody.linearVelocity.magnitude / 50f);
Vector3 speedOffset = carTarget.forward * speedFactor * -3f;
basePosition += speedOffset;
}
return basePosition;
}
void UpdateOrbitCamera()
{
// Автоматическое вращение вокруг машины
orbitAngle += orbitSpeed * Time.deltaTime;
// Рассчитываем позицию по окружности
float x = Mathf.Sin(orbitAngle) * orbitDistance;
float z = Mathf.Cos(orbitAngle) * orbitDistance;
Vector3 orbitPosition = carTarget.position +
new Vector3(x, orbitHeight, z);
transform.position = orbitPosition;
transform.LookAt(carTarget.position + lookAtOffset);
}
void UpdateFirstPersonCamera(Transform cameraPosition)
{
if (cameraPosition == null)
{
// Резервная позиция
transform.position = carTarget.position +
carTarget.forward * 1f +
Vector3.up * 1.5f;
transform.rotation = carTarget.rotation;
return;
}
transform.position = cameraPosition.position;
transform.rotation = cameraPosition.rotation;
// Добавляем небольшую тряску на скорости
if (carRigidbody != null)
{
float shakeAmount = carRigidbody.linearVelocity.magnitude * 0.001f;
transform.position += Random.insideUnitSphere * shakeAmount;
}
}
// Визуализация в редакторе
void OnDrawGizmosSelected()
{
if (carTarget != null)
{
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(carTarget.position + lookAtOffset, 0.5f);
Gizmos.DrawLine(transform.position, carTarget.position + lookAtOffset);
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1a6cd725a6ce4cc0b50b8a04be749bd1
timeCreated: 1770228499
@@ -18,7 +18,7 @@ namespace Code.Infrastructure.Camera.CameraStates
protected override void OnExitState()
{
}
protected override void HandlePosition(float deltaTime, object targetRotation)
{
throw new System.NotImplementedException();
@@ -0,0 +1,92 @@
using Code.Infrastructure.Generators;
using DG.Tweening;
using UnityEngine;
namespace Code.Infrastructure.Camera.CameraStates
{
[State(typeof(CameraMode),typeof(CameraStateMachineContext), true)]
public partial class ThirdPersonState : BaseCameraState
{
public ThirdPersonState(CameraStateMachineContext context, EventBus.EventBus eventBus) : base(context, eventBus)
{
}
public override CameraMode myState => CameraMode.ThirdPerson;
protected override void OnEnterState()
{
}
protected override void OnExitState()
{
}
protected override void HandleRotationInput(float deltaTime, Vector3 rotationInput, out Quaternion q)
{
throw new System.NotImplementedException();
}
private Transform _carTarget;
private Rigidbody _carRigidbody;
public void SetFollowTarget(Transform carTarget, Rigidbody carRigidbody)
{
_carTarget = carTarget;
_carRigidbody = carRigidbody;
}
private Vector3 _lookAtOffset = new Vector3(0, 1.5f, 0);
protected override void HandlePosition(float deltaTime, object targetRotation)
{
Fuck();
}
private void Fuck()
{
// Автоматическое вращение вокруг машины
Vector3 targetPosition = CalculateFollowPosition();
// Плавное перемещение
Vector3 velocity = _carRigidbody ? _carRigidbody.linearVelocity : new Vector3();
// Context.cameraLoadedData.Camera.transform.DOMove(targetPosition,
// Context.cameraLoadedData.SettingsConfigs[CameraMode.ThirdPerson].followSharpness);
Context.cameraLoadedData.Camera.transform.position = Vector3.SmoothDamp(
Context.cameraLoadedData.Camera.transform.position,
targetPosition,
ref velocity,
Context.cameraLoadedData.SettingsConfigs[CameraMode.ThirdPerson].followSharpness
);
// Направление взгляда с опережением
Vector3 lookTarget = _carTarget.position +
_carTarget.forward * Context.cameraLoadedData.SettingsConfigs[CameraMode.ThirdPerson].defaultDistance +
_lookAtOffset;
Context.cameraLoadedData.Camera.transform.LookAt(lookTarget);
}
private Vector3 _followOffset = new Vector3(0, 3, -8);
private Vector3 CalculateFollowPosition()
{
Vector3 basePosition = _carTarget.position +
_carTarget.TransformDirection(_followOffset);
// Динамическое смещение на основе скорости
if (!_carRigidbody) return basePosition;
var speedFactor = Mathf.Clamp01(_carRigidbody.linearVelocity.magnitude / 50f);
var speedOffset = _carTarget.forward * (speedFactor * -3f);
basePosition += speedOffset;
return basePosition;
}
public override void Update()
{
}
public override void LateUpdate()
{
Fuck();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 817473133bf54c7aab1ea3e093561e8e
timeCreated: 1770227852
@@ -31,6 +31,7 @@ using Code.Infrastructure.EventBus;
public interface IOn{member.Name}SceneLoadedHandler : IGlobalSubscriber
{{
void On{member.Name}SceneLoaded();
void On{member.Name}SceneInitialized(Reflex.Core.Container container); // Until its fixed
}}
";
dictionaryCode += $@"
@@ -4,9 +4,9 @@ using Code.Infrastructure.Configs;
using Code.Infrastructure.EventBus;
using Code.Infrastructure.Generators;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
namespace Code.Infrastructure.SceneLoader
@@ -37,15 +37,19 @@ namespace Code.Infrastructure.SceneLoader
// TODO типилище
public async UniTaskVoid LoadSceneAsync(SceneId id, LoadSceneMode loadMode)
{
await Addressables.LoadSceneAsync(_sceneInfos[id].SceneAsset, loadMode);
_loadedScenes.Add(id,Addressables.LoadSceneAsync(_sceneInfos[id].SceneAsset, loadMode));
await _loadedScenes[id];
_scenesActions[id]();
}
private Dictionary<SceneId, AsyncOperationHandle<SceneInstance>> _loadedScenes = new();
public Scene GetScene(SceneId id) => _loadedScenes[id].Result.Scene;
public async UniTaskVoid UnloadSceneAsync(SceneId id)
{
// TODO НЕВОЗМОЖНО ВЫГРУЗИТЬ БЛЯДСКИЕ СЦЕНЫ
// await Addressables.UnloadSceneAsync(_sceneInfos[])
await Addressables.UnloadSceneAsync(_loadedScenes[id]);
_loadedScenes.Remove(id);
}
private AsyncOperationHandle<IList<SceneInfoConfig>> _configHandle;
@@ -5,6 +5,7 @@ namespace Code.Infrastructure.UI
Logo,
Loading,
DebugSceneChooser,
PlayerScoreCounter,
DebugMenu
}
}