This commit is contained in:
2025-03-30 16:06:57 +02:00
parent e62bd2aa6d
commit 623ba3f621
148 changed files with 2773 additions and 1441 deletions

View File

@@ -7,45 +7,45 @@ namespace RebootKit.Engine.Services.Console {
[Serializable]
public struct CVarValue {
public CVarValueKind Kind;
public CVarValueKind kind;
public double NumberValue;
public string StringValue;
public double numberValue;
public string stringValue;
public CVarValue(int value) {
Kind = CVarValueKind.Number;
NumberValue = value;
StringValue = null;
kind = CVarValueKind.Number;
numberValue = value;
stringValue = null;
}
public CVarValue(float value) {
Kind = CVarValueKind.Number;
NumberValue = value;
StringValue = null;
kind = CVarValueKind.Number;
numberValue = value;
stringValue = null;
}
public CVarValue(double value) {
Kind = CVarValueKind.Number;
NumberValue = value;
StringValue = null;
kind = CVarValueKind.Number;
numberValue = value;
stringValue = null;
}
public CVarValue(string value) {
Kind = CVarValueKind.String;
NumberValue = 0;
StringValue = value;
kind = CVarValueKind.String;
numberValue = 0;
stringValue = value;
}
public void CopyFrom(CVarValue value) {
Kind = value.Kind;
NumberValue = value.NumberValue;
StringValue = value.StringValue;
kind = value.kind;
numberValue = value.numberValue;
stringValue = value.stringValue;
}
public override string ToString() {
return Kind switch {
CVarValueKind.Number => NumberValue.ToString(),
CVarValueKind.String => $"\"{StringValue}\"",
return kind switch {
CVarValueKind.Number => numberValue.ToString(),
CVarValueKind.String => $"\"{stringValue}\"",
_ => throw new ArgumentOutOfRangeException()
};
}
@@ -58,108 +58,98 @@ namespace RebootKit.Engine.Services.Console {
[Serializable]
public class CVar {
public CVarFlags Flags;
public string Name;
public string Description;
public CVarValue DefaultValue;
public CVarValue Value { get; private set; }
public int IndexValue => (int) Value.NumberValue;
public float FloatValue => (float) Value.NumberValue;
public double NumberValue => Value.NumberValue;
public string StringValue => Value.StringValue;
public event Action OnChanged = delegate { };
public CVarFlags flags;
public string name;
public string description;
public CVarValue defaultValue;
public CVar(CVar other) {
Flags = other.Flags;
Name = other.Name;
Description = other.Description;
DefaultValue = other.DefaultValue;
flags = other.flags;
name = other.name;
description = other.description;
defaultValue = other.defaultValue;
Value = other.Value;
}
public CVar(string name, CVarValue value, string description = "") {
Name = name;
Description = description;
DefaultValue = value;
Value = DefaultValue;
this.name = name;
this.description = description;
defaultValue = value;
Value = defaultValue;
}
public CVar(string name, int value, string description = "") {
Name = name;
Description = description;
DefaultValue = new CVarValue(value);
Value = DefaultValue;
this.name = name;
this.description = description;
defaultValue = new CVarValue(value);
Value = defaultValue;
}
public CVar(string name, float value, string description = "") {
Name = name;
Description = description;
DefaultValue = new CVarValue(value);
Value = DefaultValue;
this.name = name;
this.description = description;
defaultValue = new CVarValue(value);
Value = defaultValue;
}
public CVar(string name, double value, string description = "") {
Name = name;
Description = description;
DefaultValue = new CVarValue(value);
Value = DefaultValue;
this.name = name;
this.description = description;
defaultValue = new CVarValue(value);
Value = defaultValue;
}
public CVar(string name, string value, string description = "") {
Name = name;
Description = description;
DefaultValue = new CVarValue(value);
Value = DefaultValue;
this.name = name;
this.description = description;
defaultValue = new CVarValue(value);
Value = defaultValue;
}
public CVarValue Value { get; private set; }
public int IndexValue => (int) Value.numberValue;
public float FloatValue => (float) Value.numberValue;
public double NumberValue => Value.numberValue;
public string StringValue => Value.stringValue;
public event Action StateChanged = delegate { };
public void Set(int value) {
if (Flags.HasFlag(CVarFlags.ReadOnly)) {
return;
}
if (flags.HasFlag(CVarFlags.ReadOnly)) return;
Value = new CVarValue(value);
OnChanged?.Invoke();
StateChanged?.Invoke();
}
public void Set(float value) {
if (Flags.HasFlag(CVarFlags.ReadOnly)) {
return;
}
if (flags.HasFlag(CVarFlags.ReadOnly)) return;
Value = new CVarValue(value);
OnChanged?.Invoke();
StateChanged?.Invoke();
}
public void Set(string value) {
if (Flags.HasFlag(CVarFlags.ReadOnly)) {
return;
}
if (flags.HasFlag(CVarFlags.ReadOnly)) return;
Value = new CVarValue(value);
OnChanged?.Invoke();
StateChanged?.Invoke();
}
public void ParseFromString(string str) {
if (Flags.HasFlag(CVarFlags.ReadOnly)) {
return;
}
if (flags.HasFlag(CVarFlags.ReadOnly)) return;
if (float.TryParse(str, out float f)) {
if (float.TryParse(str, out float f))
Set(f);
} else {
else
Set(str);
}
}
public void Reset() {
if (Flags.HasFlag(CVarFlags.ReadOnly)) {
return;
}
if (flags.HasFlag(CVarFlags.ReadOnly)) return;
Value = DefaultValue;
OnChanged?.Invoke();
Value = defaultValue;
StateChanged?.Invoke();
}
public override string ToString() {

View File

@@ -1,17 +1,14 @@
using UnityEngine;
namespace RebootKit.Engine.Services.Console {
[CreateAssetMenu(menuName = RConsts.AssetMenu + "cvar", fileName = "cvar")]
[CreateAssetMenu(menuName = RConsts.k_AddComponentMenu + "cvar", fileName = "cvar")]
public class CVarAsset : ScriptableObject {
[SerializeField]
private CVar _cvar;
[SerializeField] CVar m_CVar;
public CVar Create(string cvarName = null) {
CVar cvar = new(_cvar);
CVar cvar = new(m_CVar);
if (cvarName != null) {
cvar.Name = cvarName;
}
if (cvarName != null) cvar.name = cvarName;
return cvar;
}

View File

@@ -5,9 +5,6 @@ using RebootKit.Engine.Foundation;
namespace RebootKit.Engine.Services.Console {
[AttributeUsage(AttributeTargets.Field)]
public class CVarAttribute : Attribute {
public string Name { get; }
public CVarValue Value { get; }
public CVarAttribute(string name, float defaultValue) {
Name = name;
Value = new CVarValue(defaultValue);
@@ -27,19 +24,20 @@ namespace RebootKit.Engine.Services.Console {
Name = name;
Value = new CVarValue(defaultValue);
}
public string Name { get; }
public CVarValue Value { get; }
}
public class CVarFieldInjector : DIContext.IFieldInjector {
private static readonly Logger Logger = new(nameof(CVarFieldInjector));
static readonly Logger s_logger = new(nameof(CVarFieldInjector));
public bool Inject(FieldInfo field, object target, DIContext context) {
if (!Attribute.IsDefined(field, typeof(CVarAttribute))) {
return false;
}
if (!Attribute.IsDefined(field, typeof(CVarAttribute))) return false;
ConsoleService console = context.Resolve<ConsoleService>();
if (console == null) {
Logger.Error($"Cannot inject field because cannot resolve `{nameof(ConsoleService)}`");
s_logger.Error($"Cannot inject field because cannot resolve `{nameof(ConsoleService)}`");
return false;
}

View File

@@ -2,11 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Input;
using UnityEngine.InputSystem;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Services.Console {
@@ -21,144 +17,91 @@ namespace RebootKit.Engine.Services.Console {
public string Description { get; } = "Prints available commands/cvars and their descriptions.";
public void Execute(string[] args) {
RR.Console().PrintHelp();
RR.Console.PrintHelp();
}
}
public class ConsoleService : IService {
private static readonly Logger _logger = new(nameof(ConsoleService));
static readonly Logger _logger = new(nameof(ConsoleService));
[Serializable]
public class Config {
public ConsoleUI ConsoleUIPrefab;
public ScriptableInputAction ToggleAction;
}
readonly List<IConsoleCommand> _commands = new();
readonly List<CVar> _cvars = new();
private ConsoleUI _ui;
private Config _config;
private List<IConsoleCommand> _commands = new();
private List<CVar> _cvars = new();
public bool IsVisible => _ui.IsVisible;
public ConsoleService(Config config) {
_config = config;
public ConsoleService() {
_logger.Info("Waking up");
_ui = UnityEngine.Object.Instantiate(_config.ConsoleUIPrefab);
UnityEngine.Object.DontDestroyOnLoad(_ui.gameObject);
_config.ToggleAction.Action.Enable();
_config.ToggleAction.Action.performed += OnToggleAction;
RegisterCommand(new HelpCommand());
_ui.SetVisibility(false);
_ui.Clear();
_ui.Write("Hello shelf\n");
}
public void Dispose() {
if (_ui != null) {
UnityEngine.Object.Destroy(_ui);
_ui = null;
}
}
_config.ToggleAction.Action.performed -= OnToggleAction;
public event Action<string> OnOutputMessage = _ => { };
public void WriteToOutput(string message) {
OnOutputMessage?.Invoke(message);
}
public bool CVarExists(string name) {
foreach (CVar cvar in _cvars) {
if (cvar.Name.Equals(name)) {
foreach (CVar cvar in _cvars)
if (cvar.name.Equals(name))
return true;
}
}
return false;
}
public CVar GetCVar(string name) {
foreach (CVar cvar in _cvars) {
if (cvar.Name.Equals(name)) {
foreach (CVar cvar in _cvars)
if (cvar.name.Equals(name))
return cvar;
}
}
return null;
}
public CVar ReplaceOrDefault(CVar cvar) {
CVar foundCVar = GetCVar(cvar.Name);
if (foundCVar != null) {
return foundCVar;
}
CVar foundCVar = GetCVar(cvar.name);
if (foundCVar != null) return foundCVar;
_cvars.Add(cvar);
return cvar;
}
public void Replace(CVar cvar) {
CVar foundCVar = GetCVar(cvar.Name);
if (foundCVar != null) {
_cvars.Remove(foundCVar);
}
CVar foundCVar = GetCVar(cvar.name);
if (foundCVar != null) _cvars.Remove(foundCVar);
_cvars.Add(cvar);
}
private string[] ParseCommandInputArguments(string text) {
if (text.Length == 0) {
return Array.Empty<string>();
}
string[] ParseCommandInputArguments(string text) {
if (text.Length == 0) return Array.Empty<string>();
return new string[] {text};
return new[] {text};
}
public void Execute(string input) {
if (input.Length == 0) {
return;
}
if (input.Length == 0) return;
string commandName = input;
if (input.IndexOf(' ') != -1) {
commandName = input.Substring(0, input.IndexOf(' '));
}
if (input.IndexOf(' ') != -1) commandName = input.Substring(0, input.IndexOf(' '));
string[] arguments = ParseCommandInputArguments(input.Substring(commandName.Length));
foreach (IConsoleCommand command in _commands) {
foreach (IConsoleCommand command in _commands)
if (command.Name.Equals(commandName)) {
command.Execute(arguments);
return;
}
}
foreach (CVar cvar in _cvars) {
if (cvar.Name.Equals(commandName)) {
if (arguments.Length == 1) {
cvar.ParseFromString(arguments[0]);
}
foreach (CVar cvar in _cvars)
if (cvar.name.Equals(commandName)) {
if (arguments.Length == 1) cvar.ParseFromString(arguments[0]);
_ui.Write($"<b>{cvar.Name}</b> - {cvar.ToString()}\n");
WriteToOutput($"<b>{cvar.name}</b> - {cvar}\n");
return;
}
}
_ui.Write($"ERROR: Command/CVar `{commandName}` not found.");
}
public void ToggleVisibility() {
_ui.SetVisibility(!_ui.IsVisible);
if (_ui.IsVisible) {
RR.Input().DisableControls();
RR.Input().UnlockCursor();
} else {
RR.Input().EnableControls();
RR.Input().LockCursor();
}
WriteToOutput($"ERROR: Command/CVar `{commandName}` not found.");
}
public void RegisterCommand(IConsoleCommand command) {
@@ -186,17 +129,13 @@ namespace RebootKit.Engine.Services.Console {
message.AppendLine("Available cvars:");
foreach (CVar cvar in _cvars) {
message.Append(" ");
message.Append(cvar.Name);
message.Append(cvar.name);
message.Append(" - ");
message.Append(cvar.Description);
message.Append(cvar.description);
message.AppendLine();
}
_ui.Write(message.ToString());
}
private void OnToggleAction(InputAction.CallbackContext obj) {
ToggleVisibility();
WriteToOutput(message.ToString());
}
}
}

View File

@@ -2,19 +2,14 @@
using UnityEngine;
namespace RebootKit.Engine.Services.Console {
[CreateAssetMenu(menuName = RConsts.ServiceAssetMenu + "Console")]
[CreateAssetMenu(menuName = RConsts.k_ServiceAssetMenu + "Console")]
public class ConsoleServiceAsset : ServiceAsset<ConsoleService> {
[SerializeField]
private ConsoleService.Config _config;
[SerializeField] CVar[] _initialCVars;
[SerializeField]
private CVar[] _initialCVars;
[SerializeField]
private bool _loadCVarsFromResources = true;
[SerializeField] bool _loadCVarsFromResources = true;
public override ConsoleService Create(DIContext context) {
ConsoleService service = new(_config);
ConsoleService service = new();
context.Inject(service);
foreach (CVar cvar in _initialCVars) {

View File

@@ -1,63 +0,0 @@
using System.Text;
using UnityEngine;
using UnityEngine.UIElements;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Services.Console {
public class ConsoleUI : MonoBehaviour {
private static readonly Logger Logger = new(nameof(ConsoleUI));
private StringBuilder _content = new();
[SerializeField]
private UIDocument _document;
private Label _labelMessage;
private TextField _textField;
private Button _submitButton;
public bool IsVisible { get; private set; }
private void OnEnable() {
IsVisible = _document.enabled;
VisualElement root = _document.rootVisualElement;
_labelMessage = root.Q<Label>("console-window-message");
_labelMessage.text = "SIEMA";
_textField = root.Q<TextField>("console-text-field");
_submitButton = root.Q<Button>("console-btn-submit");
_submitButton.clicked += OnSubmitButtonClicked;
}
private void OnSubmit(string input) {
RR.Console().Execute(input);
_textField.value = input;
}
public void SetVisibility(bool visible) {
_document.enabled = visible;
IsVisible = visible;
}
public void Write(string message) {
_content.Append(message);
_labelMessage.text = _content.ToString();
_labelMessage.MarkDirtyRepaint();
}
public void Clear() {
_content.Clear();
_labelMessage.text = "";
if (_textField != null) {
_textField.label = "";
}
}
private void OnSubmitButtonClicked() {
Logger.Info("Submit");
OnSubmit(_textField.value);
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0500959e68464db2abd534e30f6f5978
timeCreated: 1742422569

View File

@@ -0,0 +1,81 @@
using System.Text;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Input;
using UnityEngine;
using UnityEngine.InputSystem;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Services.ConsoleUI {
public class ConsoleUIService : ServiceMonoBehaviour {
static readonly Logger s_logger = new(nameof(ConsoleUIService));
[SerializeField] ConsoleVC m_ConsoleVC;
[SerializeField] ScriptableInputAction m_ToggleAction;
readonly StringBuilder m_Content = new();
public bool IsVisible => m_ConsoleVC.gameObject.activeSelf;
void Awake() {
SetVisibility(false);
}
void OnEnable() {
s_logger.Info("OnEnable console");
m_ToggleAction.Action.Enable();
m_ToggleAction.Action.performed += OnToggleAction;
m_ConsoleVC.InputSubmitted += OnUserInput;
m_ConsoleVC.ClearRequested += Clear;
RR.Console.OnOutputMessage += Write;
}
void OnDisable() {
m_ToggleAction.Action.performed -= OnToggleAction;
m_ConsoleVC.InputSubmitted -= OnUserInput;
m_ConsoleVC.ClearRequested -= Clear;
RR.Console.OnOutputMessage -= Write;
}
public override void Dispose() {
}
public void ToggleVisibility() {
SetVisibility(!IsVisible);
if (IsVisible) {
RR.Input.DisableControls();
RR.Input.UnlockCursor();
m_ConsoleVC.SetMessageContent(m_Content.ToString());
} else {
RR.Input.EnableControls();
RR.Input.LockCursor();
}
}
void OnUserInput(string input) {
RR.Console.Execute(input);
}
public void SetVisibility(bool visible) {
m_ConsoleVC.gameObject.SetActive(visible);
}
public void Write(string message) {
m_Content.AppendLine(message);
m_ConsoleVC.SetMessageContent(m_Content.ToString());
}
public void Clear() {
m_Content.Clear();
m_ConsoleVC.SetMessageContent(string.Empty);
}
void OnToggleAction(InputAction.CallbackContext obj) {
ToggleVisibility();
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using UnityEngine;
using UnityEngine.UIElements;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Services.ConsoleUI {
public class ConsoleVC : MonoBehaviour {
static readonly Logger s_logger = new(nameof(ConsoleVC));
[SerializeField] UIDocument m_Document;
Label m_LabelMessage;
VisualElement m_Root;
ScrollView m_ScrollView;
TextField m_TextField;
void OnEnable() {
m_Root = m_Document.rootVisualElement;
m_LabelMessage = m_Root.Q<Label>("console-window-message");
m_TextField = m_Root.Q<TextField>("console-text-field");
m_TextField.value = string.Empty;
m_TextField.RegisterCallback<KeyUpEvent>(ev => {
if (ev.keyCode == KeyCode.Return) {
Submit();
ev.StopPropagation();
}
});
Button submitButton = m_Root.Q<Button>("console-btn-submit");
submitButton.RegisterCallback<ClickEvent>(_ => { Submit(); });
Button clearButton = m_Root.Q<Button>("console-btn-clear");
clearButton.RegisterCallback<ClickEvent>(_ => { Clear(); });
m_ScrollView = m_Root.Q<ScrollView>("console-scrollview");
m_TextField.schedule.Execute(() => m_TextField.Focus()).StartingIn(1);
}
public event Action<string> InputSubmitted = _ => { };
public event Action ClearRequested = () => { };
public void SetMessageContent(string message) {
m_LabelMessage.text = message;
m_ScrollView.schedule.Execute(() => { m_ScrollView.scrollOffset = new Vector2(0, m_ScrollView.contentContainer.contentRect.height); }).StartingIn(16);
}
void Submit() {
InputSubmitted.Invoke(m_TextField.value);
m_TextField.value = string.Empty;
m_TextField.Focus();
}
void Clear() {
ClearRequested.Invoke();
m_TextField.value = string.Empty;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a6131b8af74b4894ba7b95b12286bc66
timeCreated: 1742461272

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f7a30943696f49638fd85fb1f89d5a26
timeCreated: 1743250667

View File

@@ -0,0 +1,28 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.UI;
using UnityEngine;
using UnityEngine.UIElements;
namespace RebootKit.Engine.Services.Development {
public class DebugOverlayView : MonoBehaviour, IView {
[SerializeField] UIDocument m_Document;
void Start() {
}
public async UniTask Show(CancellationToken cancellationToken) {
gameObject.SetActive(true);
await UniTask.Yield(cancellationToken);
}
public async UniTask Hide(CancellationToken cancellationToken) {
gameObject.SetActive(false);
await UniTask.Yield(cancellationToken);
}
void SetOverlayModeChanged(int mode) {
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f18f4d0e8d914fdca81f98faf0e1546f
timeCreated: 1743249301

View File

@@ -0,0 +1,46 @@
using System;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Console;
using UnityEngine;
namespace RebootKit.Engine.Services.Development {
static class DebugCVars {
public const string k_OverlayMode = "debug.mode";
}
public class DevToolsService : ServiceMonoBehaviour {
[SerializeField] DebugOverlayView m_DebugOverlayView;
[CVar(DebugCVars.k_OverlayMode, 1)] CVar m_OverlayMode;
IDisposable m_CVarChangedListener;
void OnEnable() {
m_CVarChangedListener = RR.CVarChanged.Listen(OnCVarChanged);
OnOverlayModeChanged(m_OverlayMode.IndexValue);
}
void OnDisable() {
Dispose();
}
public override void Dispose() {
m_CVarChangedListener.Dispose();
}
void OnOverlayModeChanged(int mode) {
if (mode == 1) {
m_DebugOverlayView.Show(destroyCancellationToken).Forget();
} else {
m_DebugOverlayView.Hide(destroyCancellationToken).Forget();
}
}
void OnCVarChanged(string cvarName, CVarValue value) {
if (cvarName == DebugCVars.k_OverlayMode) {
OnOverlayModeChanged((int)value.numberValue);
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0ea9757c83234daaae0d4227ac495da2
timeCreated: 1743250681

View File

@@ -4,14 +4,9 @@ using RebootKit.Engine.Foundation;
using UnityEngine;
namespace RebootKit.Engine.Services.Game {
[Serializable]
public class GameModeConfig {
public ControllerAsset[] Controllers;
}
public abstract class GameModeAsset : FactoryAsset<GameMode> {
[field: SerializeField]
public GameModeConfig GameModeConfig { get; private set; }
public GameMode.Config GameModeConfig { get; private set; }
public override GameMode Create(DIContext context) {
GameMode gameMode = new(GameModeConfig);
@@ -22,51 +17,62 @@ namespace RebootKit.Engine.Services.Game {
public abstract void ConfigureGameMode(GameMode gameMode);
}
public class GameMode : IDisposable {
private readonly GameModeConfig _config;
private readonly ControllersManager _controllersManager;
[Serializable]
public class Config {
public ControllerAsset[] controllers;
}
[Inject]
private DIContext _diContext;
readonly Config m_Config;
readonly ControllersManager<IController> m_Controllers;
private CancellationTokenSource _destroyCancellationTokenSource;
private bool _isRunning;
readonly CancellationTokenSource m_DestroyCancellationTokenSource;
public GameMode(GameModeConfig config) {
_config = config;
_isRunning = false;
[Inject] DIContext m_DIContext;
bool m_IsRunning;
_destroyCancellationTokenSource = new CancellationTokenSource();
_controllersManager = new ControllersManager(_destroyCancellationTokenSource.Token);
public GameMode(Config config) {
m_Config = config;
m_IsRunning = false;
m_DestroyCancellationTokenSource = new CancellationTokenSource();
m_Controllers = new ControllersManager<IController>(m_DestroyCancellationTokenSource.Token);
}
public void Dispose() {
_destroyCancellationTokenSource.Cancel();
_controllersManager.Dispose();
m_DestroyCancellationTokenSource.Cancel();
m_Controllers.Dispose();
}
public async Awaitable<bool> Start(CancellationToken cancellationToken) {
_controllersManager.Add(_config.Controllers, _diContext);
await _controllersManager.Start(cancellationToken);
m_Controllers.Add(m_Config.controllers, m_DIContext);
await m_Controllers.Start(cancellationToken);
_isRunning = true;
m_IsRunning = true;
return true;
}
public void Stop() {
_isRunning = false;
m_IsRunning = false;
_controllersManager.Stop();
m_Controllers.Stop();
}
public void Tick() {
_controllersManager.Tick();
m_Controllers.Tick();
}
public void AddController(IController controller) {
_controllersManager.Add(controller);
m_Controllers.Add(controller);
}
public T FindController<T>() where T : class, IController {
if (m_Controllers.TryFind<T>(out T controller)) {
return controller;
}
return null;
}
}
}

View File

@@ -5,39 +5,42 @@ using UnityEngine.Assertions;
namespace RebootKit.Engine.Services.Game {
public class GameService : IService {
private static readonly Logger Logger = new(nameof(GameService));
static readonly Logger s_logger = new(nameof(GameService));
[Inject] DIContext m_DIContext;
GameMode m_GameMode;
GameModeAsset m_GameModeAsset;
bool m_isRunning;
[Inject]
private DIContext _diContext;
private GameModeAsset _gameModeAsset;
private GameMode _gameMode;
private bool _running;
public void Dispose() {
_running = false;
_gameMode.Dispose();
m_isRunning = false;
m_GameMode.Dispose();
}
public async UniTask Start(GameModeAsset asset, CancellationToken cancellationToken) {
Assert.IsNotNull(asset);
_gameMode = asset.Create(_diContext);
await _gameMode.Start(cancellationToken);
m_GameMode = asset.Create(m_DIContext);
await m_GameMode.Start(cancellationToken);
Run(cancellationToken).Forget();
}
private async UniTask Run(CancellationToken cancellationToken) {
if (_gameMode == null) {
Logger.Error("Trying to run game without game mode");
async UniTask Run(CancellationToken cancellationToken) {
if (m_GameMode == null) {
s_logger.Error("Trying to run game without game mode");
return;
}
_running = true;
while (_running) {
m_isRunning = true;
while (m_isRunning) {
await UniTask.Yield(PlayerLoopTiming.Update, cancellationToken);
_gameMode.Tick();
m_GameMode.Tick();
if (cancellationToken.IsCancellationRequested) {
return;
}
}
}
}

View File

@@ -2,7 +2,7 @@
using UnityEngine;
namespace RebootKit.Engine.Services.Game {
[CreateAssetMenu(menuName = RConsts.ServiceAssetMenu + "Game")]
[CreateAssetMenu(menuName = RConsts.k_ServiceAssetMenu + "Game")]
public class GameServiceAsset : ServiceAsset<GameService> {
public override GameService Create(DIContext context) {
GameService service = context.Create<GameService>();

View File

@@ -1,36 +1,29 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
using UnityEngine;
using UnityEngine.InputSystem;
namespace RebootKit.Engine.Services.Input {
public class InputService : IService {
[Serializable]
public class Config {
public InputActionAsset InputAsset;
}
private Config _config;
readonly Config m_Config;
public InputService(Config config) {
_config = config;
m_Config = config;
}
public void Dispose() {
}
public void EnableControls() {
_config.InputAsset.Enable();
m_Config.inputAsset.Enable();
}
public void DisableControls() {
_config.InputAsset.Disable();
m_Config.inputAsset.Disable();
}
public InputAction FindInputAction(string path) {
return _config.InputAsset.FindAction(path);
return m_Config.inputAsset.FindAction(path);
}
public void LockCursor() {
@@ -42,5 +35,10 @@ namespace RebootKit.Engine.Services.Input {
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
[Serializable]
public class Config {
public InputActionAsset inputAsset;
}
}
}

View File

@@ -2,13 +2,12 @@
using UnityEngine;
namespace RebootKit.Engine.Services.Input {
[CreateAssetMenu(menuName = RConsts.ServiceAssetMenu + "Input")]
[CreateAssetMenu(menuName = RConsts.k_ServiceAssetMenu + "Input")]
public class InputServiceAsset : ServiceAsset<InputService> {
[SerializeField]
private InputService.Config _config;
[SerializeField] InputService.Config m_Config;
public override InputService Create(DIContext context) {
InputService instance = new(_config);
InputService instance = new(m_Config);
context.Inject(instance);
return instance;
}

View File

@@ -1,11 +1,9 @@
using UnityEngine;
using UnityEngine.InputSystem;
namespace RebootKit.Engine.Services.Input
{
[CreateAssetMenu(menuName = RConsts.AssetMenu + "Input Action", fileName = "Input Action")]
public class ScriptableInputAction : ScriptableObject
{
namespace RebootKit.Engine.Services.Input {
[CreateAssetMenu(menuName = RConsts.k_AddComponentMenu + "Input Action", fileName = "Input Action")]
public class ScriptableInputAction : ScriptableObject {
[field: SerializeField]
public InputAction Action { get; private set; }
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f5fb2de6697247618aa0dc36a659ddcc
timeCreated: 1741791621

View File

@@ -1,7 +0,0 @@
namespace RebootKit.Engine.Services.LoadigScreen
{
public class LoadingScreenService
{
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0a45f8f7ea6f45198713694a39790614
timeCreated: 1740671052

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 663488f6dc6f4f23a2fb88ab0b2a8daa
timeCreated: 1741043532

View File

@@ -1,15 +0,0 @@
using System;
namespace RebootKit.Engine.Services.Persistance {
[Serializable]
public class SaveData {
public string ProfileName;
}
public class SaveSystemService {
public void Save() {
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0fd19386b9584594bac09c3f43489a1a
timeCreated: 1741043540

View File

@@ -1,74 +1,60 @@
using UnityEngine;
namespace RebootKit.Engine.Services.Simulation.Characters
{
public class CharacterLocomotion : MonoBehaviour
{
[SerializeField]
private CharacterController _characterController;
public float MaxMovementSpeed = 4.0f;
public float MaxSprintSpeed = 15.0f;
public float JumpHeight = 1.0f;
public float Gravity = 10f;
public float MaxFallSpeed = 20f;
public float Damping = 20.0f;
namespace RebootKit.Engine.Services.Simulation.Characters {
public class CharacterLocomotion : MonoBehaviour {
[SerializeField] CharacterController m_CharacterController;
private Vector3 _pendingInputValue;
private Vector3 _currentVelocity;
public float maxMovementSpeed = 4.0f;
public float maxSprintSpeed = 15.0f;
public float jumpHeight = 1.0f;
public float gravity = 10f;
public float maxFallSpeed = 20f;
public float damping = 20.0f;
Vector3 m_CurrentVelocity;
bool m_IsFalling;
private bool _isSprinting;
private bool _jumpRequested;
private bool _isFalling;
bool m_IsSprinting;
bool m_JumpRequested;
public bool IsGrounded => _characterController.isGrounded;
Vector3 m_PendingInputValue;
public Vector3 Velocity => _currentVelocity;
public bool IsGrounded => m_CharacterController.isGrounded;
private void Update()
{
public Vector3 Velocity => m_CurrentVelocity;
void Update() {
ConsumePendingInput();
UpdateVerticalVelocity();
_characterController.Move(_currentVelocity * Time.deltaTime);
m_CharacterController.Move(m_CurrentVelocity * Time.deltaTime);
ApplyFriction();
DetectFall();
}
private void DetectFall()
{
if (_isFalling && _characterController.isGrounded)
{
_isFalling = false;
}
else if (!_characterController.isGrounded)
{
_isFalling = true;
}
void DetectFall() {
if (m_IsFalling && m_CharacterController.isGrounded)
m_IsFalling = false;
else if (!m_CharacterController.isGrounded) m_IsFalling = true;
}
private void ConsumePendingInput()
{
if (!IsGrounded)
{
_pendingInputValue = Vector3.zero;
void ConsumePendingInput() {
if (!IsGrounded) {
m_PendingInputValue = Vector3.zero;
return;
}
_pendingInputValue.y = 0.0f;
m_PendingInputValue.y = 0.0f;
float pendingInputMagnitude = _pendingInputValue.magnitude;
float pendingInputMagnitude = m_PendingInputValue.magnitude;
Vector3 direction = Vector3.zero;
if (pendingInputMagnitude > 0.0f)
{
// normalize vector, reusing magnitude to avoid multiple sqrt calls
direction = _pendingInputValue / pendingInputMagnitude;
}
direction = m_PendingInputValue / pendingInputMagnitude;
float movementSpeed = _isSprinting ? MaxSprintSpeed : MaxMovementSpeed;
Vector3 movementVelocity = _currentVelocity;
float movementSpeed = m_IsSprinting ? maxSprintSpeed : maxMovementSpeed;
Vector3 movementVelocity = m_CurrentVelocity;
movementVelocity.y = 0.0f;
movementVelocity += direction * (movementSpeed * pendingInputMagnitude);
movementVelocity.y = 0.0f;
@@ -77,84 +63,62 @@ namespace RebootKit.Engine.Services.Simulation.Characters
float movementVelocityMagnitude = movementVelocity.magnitude;
Vector3 movementVelocityDirection = movementVelocityMagnitude > 0.0f ? movementVelocity / movementVelocityMagnitude : Vector3.zero;
if (movementVelocityMagnitude > movementSpeed)
{
movementVelocityMagnitude = movementSpeed;
}
if (movementVelocityMagnitude > movementSpeed) movementVelocityMagnitude = movementSpeed;
movementVelocity = movementVelocityDirection * movementVelocityMagnitude;
_currentVelocity.x = movementVelocity.x;
_currentVelocity.z = movementVelocity.z;
m_CurrentVelocity.x = movementVelocity.x;
m_CurrentVelocity.z = movementVelocity.z;
_pendingInputValue = Vector3.zero;
m_PendingInputValue = Vector3.zero;
}
private void UpdateVerticalVelocity()
{
if (_characterController.isGrounded)
{
if (_jumpRequested)
{
_currentVelocity.y = Mathf.Sqrt(2.0f * Gravity * JumpHeight);
_jumpRequested = false;
void UpdateVerticalVelocity() {
if (m_CharacterController.isGrounded) {
if (m_JumpRequested) {
m_CurrentVelocity.y = Mathf.Sqrt(2.0f * gravity * jumpHeight);
m_JumpRequested = false;
} else {
m_CurrentVelocity.y = -1f;
}
else
{
_currentVelocity.y = -1f;
}
}
else
{
_currentVelocity.y -= Gravity * Time.deltaTime;
_currentVelocity.y = Mathf.Max(_currentVelocity.y, -MaxFallSpeed);
} else {
m_CurrentVelocity.y -= gravity * Time.deltaTime;
m_CurrentVelocity.y = Mathf.Max(m_CurrentVelocity.y, -maxFallSpeed);
}
}
private void ApplyFriction()
{
if (!IsGrounded)
{
return;
}
void ApplyFriction() {
if (!IsGrounded) return;
Vector3 movementVelocity = _currentVelocity;
Vector3 movementVelocity = m_CurrentVelocity;
movementVelocity.y = 0.0f;
movementVelocity = Vector3.MoveTowards(movementVelocity, Vector3.zero, Damping * Time.deltaTime);
movementVelocity = Vector3.MoveTowards(movementVelocity, Vector3.zero, damping * Time.deltaTime);
_currentVelocity.x = movementVelocity.x;
_currentVelocity.z = movementVelocity.z;
m_CurrentVelocity.x = movementVelocity.x;
m_CurrentVelocity.z = movementVelocity.z;
}
public void AddVelocity(Vector3 velocity)
{
_currentVelocity += velocity;
public void AddVelocity(Vector3 velocity) {
m_CurrentVelocity += velocity;
}
public void AddMovementInput(Vector3 input, float scale)
{
_pendingInputValue += input * scale;
public void AddMovementInput(Vector3 input, float scale) {
m_PendingInputValue += input * scale;
}
public void Jump()
{
if (!_characterController.isGrounded)
{
return;
}
public void Jump() {
if (!m_CharacterController.isGrounded) return;
_jumpRequested = true;
m_JumpRequested = true;
}
public void StartSprint()
{
_isSprinting = true;
public void StartSprint() {
m_IsSprinting = true;
}
public void StopSprint()
{
_isSprinting = false;
public void StopSprint() {
m_IsSprinting = false;
}
}
}

View File

@@ -1,6 +0,0 @@
using UnityEngine;
namespace RebootKit.Engine.Services.Simulation.Characters {
public class GameCharacter : MonoBehaviour {
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: c4dd42e93f8a469685be596f305ed985
timeCreated: 1741791931

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
@@ -9,22 +8,26 @@ namespace RebootKit.Engine.Services.Simulation.Interactors {
}
public interface IInteractor<in TInteractable> {
public static Type InteractableType() => typeof(TInteractable);
void Interact(TInteractable target);
}
public class GroupInteractor<TInteractable> : IInteractor<TInteractable> {
private readonly List<IInteractor<TInteractable>> _interactors = new();
public void Add(IInteractor<TInteractable> interactor) => _interactors.Add(interactor);
public void Remove(IInteractor<TInteractable> interactor) => _interactors.Remove(interactor);
public void RemoveAll() => _interactors.Clear();
readonly List<IInteractor<TInteractable>> m_Interactors = new();
public void Interact(TInteractable target) {
foreach (IInteractor<TInteractable> interactor in _interactors) {
interactor.Interact(target);
}
foreach (IInteractor<TInteractable> interactor in m_Interactors) interactor.Interact(target);
}
public void Add(IInteractor<TInteractable> interactor) {
m_Interactors.Add(interactor);
}
public void Remove(IInteractor<TInteractable> interactor) {
m_Interactors.Remove(interactor);
}
public void RemoveAll() {
m_Interactors.Clear();
}
}
}

View File

@@ -1,4 +1,5 @@
using UnityEngine;
using RebootKit.Engine.Extensions;
using UnityEngine;
namespace RebootKit.Engine.Services.Simulation {
public class PhysicsObjectDragger : MonoBehaviour {
@@ -17,24 +18,8 @@ namespace RebootKit.Engine.Services.Simulation {
public bool IsDragging => Current != null;
public void Grab(Rigidbody physicsObject) {
Current = physicsObject;
Current.linearDamping = 5.0f;
}
public void Drop() {
if (Current == null) {
return;
}
Current.linearDamping = 0.0f;
Current = null;
}
public void FixedUpdate() {
if (Current == null) {
return;
}
if (Current.OrNull() == null) return;
Vector3 direction = (TargetWorldPosition - Current.position).normalized;
float distance = Vector3.Distance(TargetWorldPosition, Current.position);
@@ -46,5 +31,17 @@ namespace RebootKit.Engine.Services.Simulation {
Current.angularVelocity = Vector3.MoveTowards(Current.angularVelocity, Vector3.zero, Time.fixedDeltaTime * AngularSlowdown);
}
public void Grab(Rigidbody physicsObject) {
Current = physicsObject;
Current.linearDamping = 5.0f;
}
public void Drop() {
if (Current == null) return;
Current.linearDamping = 0.0f;
Current = null;
}
}
}

View File

@@ -2,20 +2,15 @@
namespace RebootKit.Engine.Services.Simulation.Sensors {
public class RaycastSensor : ISensor {
public Ray Ray;
public LayerMask LayerMask;
public float MaxDistance;
public LayerMask layerMask;
public float maxDistance;
public Ray ray;
public bool HasHit { get; private set; }
public RaycastHit Hit { get; private set; }
public void Clear() {
HasHit = false;
Hit = default;
}
public GameObject Sense() {
HasHit = Physics.Raycast(Ray, out RaycastHit hit, MaxDistance, LayerMask);
HasHit = Physics.Raycast(ray, out RaycastHit hit, maxDistance, layerMask);
if (HasHit) {
Hit = hit;
@@ -25,5 +20,10 @@ namespace RebootKit.Engine.Services.Simulation.Sensors {
Hit = default;
return null;
}
public void Clear() {
HasHit = false;
Hit = default;
}
}
}

View File

@@ -5,18 +5,15 @@ using UnityEngine.AddressableAssets;
namespace RebootKit.Engine.Services.Simulation {
[Serializable]
public struct WorldConfig {
public string Name;
public string name;
// @NOTE: stays loaded during world lifetime
public AssetReference MainScene;
public AssetReference mainScene;
}
[CreateAssetMenu(menuName = RConsts.WorldAssetMenu + "World")]
[CreateAssetMenu(menuName = RConsts.k_WorldAssetMenu + "World")]
public class WorldConfigAsset : ScriptableObject {
[SerializeField]
private WorldConfig _config;
public WorldConfig Config => _config;
[field: SerializeField]
public WorldConfig Config { get; private set; }
}
}

View File

@@ -10,17 +10,17 @@ using UnityEngine.SceneManagement;
namespace RebootKit.Engine.Services.Simulation {
public class WorldService : IService {
private WorldConfig _config;
private List<Actor> _actors = new();
readonly List<Actor> m_Actors = new();
WorldConfig m_Config;
public void Dispose() {
KillAllActors();
}
public async UniTask Load(WorldConfig worldConfig) {
_config = worldConfig;
m_Config = worldConfig;
AsyncOperationHandle<SceneInstance> handle = worldConfig.MainScene.LoadSceneAsync(LoadSceneMode.Additive);
AsyncOperationHandle<SceneInstance> handle = worldConfig.mainScene.LoadSceneAsync(LoadSceneMode.Additive);
await handle.ToUniTask();
SceneManager.SetActiveScene(handle.Result.Scene);
}
@@ -32,16 +32,14 @@ namespace RebootKit.Engine.Services.Simulation {
return null;
}
if (gameObject.TryGetComponent(out TActor actor)) {
return actor;
}
if (gameObject.TryGetComponent(out TActor actor)) return actor;
asset.ReleaseInstance(gameObject);
return null;
}
public async UniTask RegisterActor(Actor actor) {
_actors.Add(actor);
m_Actors.Add(actor);
await UniTask.Yield();
}
@@ -50,11 +48,9 @@ namespace RebootKit.Engine.Services.Simulation {
}
public void KillAllActors() {
foreach (Actor actor in _actors) {
Addressables.ReleaseInstance(actor.gameObject);
}
foreach (Actor actor in m_Actors) Addressables.ReleaseInstance(actor.gameObject);
_actors.Clear();
m_Actors.Clear();
}
}
}

View File

@@ -2,7 +2,7 @@
using UnityEngine;
namespace RebootKit.Engine.Services.Simulation {
[CreateAssetMenu(menuName = RConsts.ServiceAssetMenu + "World")]
[CreateAssetMenu(menuName = RConsts.k_ServiceAssetMenu + "World")]
public class WorldServiceAsset : ServiceAsset<WorldService> {
public override WorldService Create(DIContext context) {
return context.Create<WorldService>();

View File

@@ -1,17 +0,0 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
namespace RebootKit.Engine.Services {
public class UpdateLoopService : IService {
public async UniTask OnWakeUp(CancellationToken cancellationToken) {
await UniTask.Yield(cancellationToken);
}
public void Dispose() {
}
public void Add(PlayerLoopTiming playerLoopTiming) {
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 0f0892c234a3a3a40a7f2516fa4dd1e0