This commit is contained in:
2025-05-14 10:52:53 +02:00
parent 1e190fe94b
commit f0536f4129
51 changed files with 934 additions and 381 deletions

View File

@@ -1,4 +1,5 @@
using RebootKit.Engine.Services.Console;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Console;
using RebootKitEditor.Utils;
using UnityEditor;
using UnityEngine;

View File

@@ -1,5 +1,7 @@
namespace RebootKitEditor {
static class REditorConsts {
internal const string k_EditorMenu = "Reboot Reality/";
internal const string k_OnGameRunScriptContentKey = "RebootKitEditor.OnGameRunScriptContent";
}
}

View File

@@ -27,21 +27,23 @@ namespace RebootKitEditor.RebootWindow {
public VisualElement Build() {
m_RootElement = new ScrollView();
Label servicesAmountLabel = new($"Game services: {m_EngineConfigAsset.services.Length}") {
style = {
color = new Color(0.7f, 0.9f, 0.9f),
unityFontStyleAndWeight = FontStyle.Bold
}
};
m_RootElement.Add(servicesAmountLabel);
for (int i = 0; i < m_EngineConfigAsset.services.Length; i++) {
ServiceAsset serviceAsset = m_EngineConfigAsset.services[i];
VisualElement serviceView = CreateServicesView(serviceAsset);
serviceView.style.backgroundColor = i % 2 == 0 ? new Color(0.1f, 0.1f, 0.1f) : new Color(0.2f, 0.2f, 0.2f);
m_RootElement.Add(serviceView);
}
// int servicesLength = m_EngineConfigAsset.services?.Length ?? 0;
//
// Label servicesAmountLabel = new($"Game services: {servicesLength}") {
// style = {
// color = new Color(0.7f, 0.9f, 0.9f),
// unityFontStyleAndWeight = FontStyle.Bold
// }
// };
// m_RootElement.Add(servicesAmountLabel);
//
// for (int i = 0; i < servicesLength; i++) {
// ServiceAsset serviceAsset = m_EngineConfigAsset.services[i];
//
// VisualElement serviceView = CreateServicesView(serviceAsset);
// serviceView.style.backgroundColor = i % 2 == 0 ? new Color(0.1f, 0.1f, 0.1f) : new Color(0.2f, 0.2f, 0.2f);
// m_RootElement.Add(serviceView);
// }
return m_RootElement;
}

View File

@@ -1,4 +1,7 @@
using RebootKit.Engine.UI;
using RebootKit.Engine.Main;
using RebootKit.Engine.Services.Console;
using RebootKit.Engine.UI;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
@@ -55,6 +58,67 @@ namespace RebootKitEditor.RebootWindow {
persistentPathContainer.Add(openPersistentPathButton);
rootContainer.Add(persistentPathContainer);
Label onGameRunScriptLabel = new("On Game Run Script (User):") {
style = {
fontSize = 12,
color = new Color(0.7f, 0.9f, 0.9f)
}
};
rootContainer.Add(onGameRunScriptLabel);
TextField onGameRunScriptTextField = new() {
style = {
fontSize = 12,
},
multiline = true,
value = EditorPrefs.GetString(REditorConsts.k_OnGameRunScriptContentKey, "")
};
onGameRunScriptTextField.RegisterValueChangedCallback(evt => {
EditorPrefs.SetString(REditorConsts.k_OnGameRunScriptContentKey, evt.newValue);
});
rootContainer.Add(onGameRunScriptTextField);
Label consoleCommandsLabel = new("Console Commands:") {
style = {
fontSize = 12,
color = new Color(0.7f, 0.9f, 0.9f)
}
};
rootContainer.Add(consoleCommandsLabel);
ConsoleService.ConsoleCommand[] consoleCommands = ConsoleService.GenerateCommandsToRegister();
foreach (ConsoleService.ConsoleCommand consoleCommand in consoleCommands) {
VisualElement commandContainer = new() {
style = {
flexDirection = FlexDirection.Row,
marginTop = 4,
marginBottom = 4,
paddingLeft = 4,
paddingRight = 4,
paddingTop = 4,
paddingBottom = 4,
}
};
Label commandLabel = new(consoleCommand.name) {
style = {
fontSize = 12,
color = new Color(0.7f, 0.9f, 0.9f)
}
};
commandContainer.Add(commandLabel);
Label descriptionLabel = new(consoleCommand.description) {
style = {
fontSize = 12,
color = new Color(0.5f, 0.7f, 0.7f)
}
};
commandContainer.Add(descriptionLabel);
rootContainer.Add(commandContainer);
}
return rootContainer;
}

View File

@@ -52,7 +52,7 @@ namespace RebootKitEditor.RebootWindow {
}
};
root.Add(label);
Button openButton = new(() => OpenWorldScenes(worldConfigAsset)) {
text = "Open",
style = {
@@ -76,14 +76,16 @@ namespace RebootKitEditor.RebootWindow {
return;
}
EditorSceneManager.SaveOpenScenes();
// Load first scene from build settings
string mainScenePath = SceneManager.GetSceneByBuildIndex(RConsts.k_MainSceneBuildIndex).path;
string mainScenePath = GetScenePathByBuildIndex(RConsts.k_MainSceneBuildIndex);
if (mainScenePath == null) {
return;
}
EditorSceneManager.OpenScene(mainScenePath, OpenSceneMode.Single);
// Load world scene
string worldScenePath = AssetDatabase.GetAssetPath(worldConfigAsset.Config.mainScene.editorAsset);
string worldScenePath = AssetDatabase.GUIDToAssetPath(worldConfigAsset.Config.mainScene.AssetGUID);
if (string.IsNullOrEmpty(worldScenePath)) {
Debug.LogError($"WorldConfigAsset {worldConfigAsset.name} has invalid main scene path");
return;
@@ -91,5 +93,14 @@ namespace RebootKitEditor.RebootWindow {
SceneManager.SetActiveScene(EditorSceneManager.OpenScene(worldScenePath, OpenSceneMode.Additive));
}
static string GetScenePathByBuildIndex(int buildIndex) {
if (buildIndex < 0 || buildIndex >= EditorBuildSettings.scenes.Length) {
Debug.LogError($"Build index {buildIndex} out of range. Total scenes in build: {EditorBuildSettings.scenes.Length}");
return null;
}
return EditorBuildSettings.scenes[buildIndex].path;
}
}
}

View File

@@ -1,6 +1,6 @@
# RebootKit
# Installation
# Installationa (WIP)
### nuget
@@ -22,3 +22,10 @@ Ensure you have the following dependencies in your `Packages/manifest.json` file
}
}
```
# TODO
- [ ] Save system
- [ ] Built in main menu
- [ ] Generic settings ui based on Config Variables
- [ ] Extendable FPP Controller
- [ ] UI System

View File

@@ -1,21 +0,0 @@
using System;
using RebootKit.Engine.Services.Game;
using UnityEngine.AddressableAssets;
namespace RebootKit.Engine {
[Serializable]
public class AppConfig {
public MainMenuConfig mainMenuConfig;
public GameConfig gameConfig;
}
[Serializable]
public class MainMenuConfig {
public AssetReference scene;
}
[Serializable]
public class GameConfig {
public GameModeAsset defaultGameMode;
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 75a5232723f54287b48c0294a0009869
timeCreated: 1743174115

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87d77002476b4d1cbe17b0b969e66c9d
timeCreated: 1746819904

View File

@@ -0,0 +1,39 @@
using System;
using UnityEngine;
namespace RebootKit.Engine.Components {
[Flags]
public enum TransformComponents {
None = 0,
Position = 1 << 0,
Rotation = 1 << 1,
Scale = 1 << 2,
All = Position | Rotation | Scale
}
[DefaultExecutionOrder(100)]
public class CopyTransform : MonoBehaviour {
[SerializeField] TransformComponents m_Components = TransformComponents.All;
[SerializeField] Transform m_Source;
Transform m_Transform;
void Awake() {
m_Transform = transform;
}
void LateUpdate() {
if (m_Components.HasFlag(TransformComponents.Position)) {
m_Transform.position = m_Source.position;
}
if (m_Components.HasFlag(TransformComponents.Rotation)) {
m_Transform.rotation = m_Source.rotation;
}
if (m_Components.HasFlag(TransformComponents.Scale)) {
m_Transform.localScale = m_Source.localScale;
}
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 43e47286597d44b9bca21389d3909958
timeCreated: 1746661605

View File

@@ -1,4 +1,4 @@
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Main;
using UnityEngine;
namespace RebootKit.Engine {
@@ -7,8 +7,7 @@ namespace RebootKit.Engine {
public bool initializeOnLoad = true;
public EngineCoreServicesAsset coreServices;
public ServiceAsset[] services;
public AppConfig appConfig;
public GameAsset gameAsset;
}
}

View File

@@ -1,6 +1,6 @@
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Console;
using RebootKit.Engine.Services.Game;
using RebootKit.Engine.Services.GameMode;
using RebootKit.Engine.Services.Input;
using RebootKit.Engine.Services.Simulation;
using UnityEngine;
@@ -11,6 +11,6 @@ namespace RebootKit.Engine {
public ServiceAsset<ConsoleService> consoleService;
public ServiceAsset<InputService> inputService;
public ServiceAsset<WorldService> worldService;
public ServiceAsset<GameService> gameService;
public ServiceAsset<GameModesService> gameService;
}
}

View File

@@ -32,5 +32,12 @@ namespace RebootKit.Engine.Extensions {
public static bool IsNonZero(this float3 vec, float epsilon = float.Epsilon) {
return !vec.IsZero(epsilon);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsInRange(this float3 vec, float3 pos, float range) {
float dstSquared = math.distancesq(vec, pos);
float rangeSquared = range * range;
return dstSquared <= rangeSquared;
}
}
}

View File

@@ -1,6 +1,6 @@
using System;
namespace RebootKit.Engine.Services.Console {
namespace RebootKit.Engine.Foundation {
public enum CVarValueKind {
Number, String
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using RebootKit.Engine.Main;
namespace RebootKit.Engine.Foundation {
public enum LogLevel {
@@ -19,16 +20,16 @@ namespace RebootKit.Engine.Foundation {
public void Log(LogLevel level, string message) {
switch (level) {
case LogLevel.Info:
UnityEngine.Debug.Log(FormatMessage(level, m_Name, message, true));
RR.Log(FormatMessage(level, m_Name, message, true));
break;
case LogLevel.Debug:
UnityEngine.Debug.Log(FormatMessage(level, m_Name, message, true));
RR.Log(FormatMessage(level, m_Name, message, true));
break;
case LogLevel.Warning:
UnityEngine.Debug.LogWarning(FormatMessage(level, m_Name, message, true));
RR.LogWarning(FormatMessage(level, m_Name, message, true));
break;
case LogLevel.Error:
UnityEngine.Debug.LogError(FormatMessage(level, m_Name, message, true));
RR.LogError(FormatMessage(level, m_Name, message, true));
break;
default:
throw new ArgumentOutOfRangeException(nameof(level), level, null);

View File

@@ -1,4 +1,5 @@
using UnityEngine;
using RebootKit.Engine.Main;
using UnityEngine;
using UnityEngine.Assertions;
namespace RebootKit.Engine.Foundation {
@@ -20,7 +21,7 @@ namespace RebootKit.Engine.Foundation {
DIContext context = RR.DIContext;
foreach (GameObject root in gameObject.scene.GetRootGameObjects()) {
s_logger.Info("Injecting root game object: " + root.name);
// s_logger.Info("Injecting root game object: " + root.name);
context.InjectGameObject(root);
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ce874693b1034e70a925c0043b78e7ea
timeCreated: 1746821949

View File

@@ -1,10 +1,11 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using UnityEditor;
using RebootKit.Engine.Foundation;
using UnityEngine;
using UnityEngine.SceneManagement;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Foundation {
namespace RebootKit.Engine.Main {
public static class EntryPoint {
static readonly Logger s_logger = new(nameof(EntryPoint));
@@ -16,32 +17,30 @@ namespace RebootKit.Engine.Foundation {
s_cancellationTokenSource.Cancel();
}
RR.s_Shared = null;
// unload all scenes
s_cancellationTokenSource = new CancellationTokenSource();
RunAsync(s_cancellationTokenSource.Token).Forget();
#if UNITY_EDITOR
static void OnPlayerModeState(PlayModeStateChange state) {
if (state == PlayModeStateChange.ExitingPlayMode) {
static void OnPlayerModeState(UnityEditor.PlayModeStateChange state) {
if (state == UnityEditor.PlayModeStateChange.ExitingPlayMode) {
s_cancellationTokenSource.Cancel();
}
}
EditorApplication.playModeStateChanged -= OnPlayerModeState;
EditorApplication.playModeStateChanged += OnPlayerModeState;
UnityEditor.EditorApplication.playModeStateChanged -= OnPlayerModeState;
UnityEditor.EditorApplication.playModeStateChanged += OnPlayerModeState;
#endif
}
static async UniTask RunAsync(CancellationToken cancellationToken) {
ConfigVarsContainer.Init();
s_logger.Info("Loading boot scene");
SceneManager.LoadScene(RConsts.k_BootSceneBuildIndex, LoadSceneMode.Single);
s_logger.Info("Loading engine config");
EngineConfigAsset configAsset = Resources.Load<EngineConfigAsset>(RConsts.k_EngineConfigResourcesPath);
if (configAsset == null) {
if (configAsset is null) {
s_logger.Error($"Couldn't load engine config from resources: {RConsts.k_EngineConfigResourcesPath}");
return;
}
@@ -50,19 +49,18 @@ namespace RebootKit.Engine.Foundation {
return;
}
using RR instance = new();
RR.s_Shared = instance;
s_logger.Info("Initializing RR");
await instance.Init(configAsset, cancellationToken);
await RR.InitAsync(configAsset, cancellationToken);
s_logger.Info("Loading main scene");
await SceneManager.LoadSceneAsync(RConsts.k_MainSceneBuildIndex, LoadSceneMode.Single).ToUniTask(cancellationToken: cancellationToken);
s_logger.Info("Starting RR");
instance.Run();
RR.Run();
await UniTask.WaitUntilCanceled(Application.exitCancellationToken);
RR.Shutdown();
}
}
}

View File

@@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Cysharp.Threading.Tasks;
using R3;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Console;
using RebootKit.Engine.Services.GameMode;
using RebootKit.Engine.Services.Input;
using RebootKit.Engine.Services.Simulation;
using UnityEngine;
using UnityEngine.AddressableAssets;
using Assert = UnityEngine.Assertions.Assert;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Main {
public interface IGame : IDisposable {
UniTask InitAsync(CancellationToken cancellationToken);
void Run();
}
public abstract class GameAsset : ScriptableObject {
public abstract IGame CreateGame();
}
public static class RR {
static readonly Logger s_logger = new("RR");
[ConfigVar("con.write_log", 1, "Enables writing game log to console output")]
static ConfigVar s_writeLogToConsole;
static EngineConfigAsset s_engineConfigAsset;
static DisposableBag s_disposableBag;
static DisposableBag s_servicesBag;
static DIContext s_diContext;
static ConsoleService s_consoleService;
static GameModesService s_gameModesService;
static InputService s_inputService;
static WorldService s_worldService;
public static ConsoleService Console => s_consoleService;
public static InputService Input => s_inputService;
public static WorldService World => s_worldService;
public static GameModesService GameModes => s_gameModesService;
public static DIContext DIContext => s_diContext;
static IGame s_game;
public static async UniTask InitAsync(EngineConfigAsset configAsset, CancellationToken cancellationToken) {
Assert.IsNotNull(configAsset, "Config asset is required");
Assert.IsNotNull(configAsset.gameAsset, "Game asset is required");
s_engineConfigAsset = configAsset;
s_logger.Info("Initializing");
s_servicesBag = new DisposableBag();
s_disposableBag = new DisposableBag();
s_diContext = new DIContext();
s_logger.Debug("Registering core services");
s_consoleService = CreateService(s_engineConfigAsset.coreServices.consoleService);
s_inputService = CreateService(s_engineConfigAsset.coreServices.inputService);
s_worldService = CreateService(s_engineConfigAsset.coreServices.worldService);
s_gameModesService = CreateService(s_engineConfigAsset.coreServices.gameService);
await InitializeAssetsAsync(cancellationToken);
s_logger.Debug("Creating game");
s_game = s_engineConfigAsset.gameAsset.CreateGame();
await s_game.InitAsync(cancellationToken);
}
public static void Shutdown() {
s_logger.Info("Shutting down");
s_servicesBag.Dispose();
s_disposableBag.Dispose();
}
public static void Run() {
s_game.Run();
#if UNITY_EDITOR
string scriptContent = UnityEditor.EditorPrefs.GetString("RebootKitEditor.OnGameRunScriptContent", "");
s_logger.Info($"Executing script: {scriptContent}");
if (!string.IsNullOrEmpty(scriptContent)) {
foreach (string cmd in scriptContent.Split('\n')) {
s_logger.Info($"Executing command: {cmd}");
Console.Execute(cmd);
}
}
#endif
}
// Assets API
static readonly List<GameModeAsset> s_gameModesAssets = new();
static readonly List<WorldConfigAsset> s_worldConfigsAssets = new();
public static IReadOnlyList<GameModeAsset> GameModesAssets => s_gameModesAssets;
public static IReadOnlyList<WorldConfigAsset> WorldConfigsAssets => s_worldConfigsAssets;
public static async UniTask InitializeAssetsAsync(CancellationToken cancellationToken) {
s_gameModesAssets.Clear();
s_worldConfigsAssets.Clear();
s_logger.Info("Loading game assets");
await Addressables.LoadAssetsAsync<GameModeAsset>("game_mode", asset => { s_gameModesAssets.Add(asset); }).ToUniTask(cancellationToken: cancellationToken);
s_logger.Info($"Loaded {s_gameModesAssets.Count} game modes");
await Addressables.LoadAssetsAsync<WorldConfigAsset>("world", asset => { s_worldConfigsAssets.Add(asset); }).ToUniTask(cancellationToken: cancellationToken);
}
// Game API
public static void StartGameMode(GameModeAsset gameMode, WorldConfig world) {
if (gameMode is null) {
throw new ArgumentNullException(nameof(gameMode));
}
s_logger.Info($"Starting game mode: {gameMode.name} in world: {world.name}");
s_gameModesService.Start(gameMode, world);
}
public static TGame Game<TGame>() where TGame : IGame {
if (s_game is TGame game) {
return game;
}
throw new InvalidOperationException($"Game is not of type {typeof(TGame)}");
}
// Service API
public static TService CreateService<TService>(ServiceAsset<TService> asset) where TService : class, IService {
TService service = asset.Create(s_diContext);
s_diContext.Bind(service);
s_servicesBag.Add(service);
return service;
}
public static TService CreateService<TService>() where TService : class, IService {
TService service = s_diContext.Create<TService>();
s_diContext.Bind(service);
s_servicesBag.Add(service);
return service;
}
// General API
public static void Log(string message) {
Debug.Log(message);
s_consoleService?.WriteToOutput(message);
}
public static void LogWarning(string message) {
Debug.LogWarning(message);
s_consoleService?.WriteToOutput(message);
}
public static void LogError(string message) {
Debug.LogError(message);
s_consoleService?.WriteToOutput(message);
}
// CVar API
public static ConfigVar CVarIndex(string name, int defaultValue = -1) {
ConfigVar cvar = ConfigVarsContainer.Get(name);
if (cvar != null) {
return cvar;
}
cvar = new ConfigVar(name, defaultValue);
ConfigVarsContainer.Register(cvar);
return cvar;
}
public static ConfigVar CVarNumber(string name, double defaultValue = 0) {
ConfigVar cvar = ConfigVarsContainer.Get(name);
if (cvar != null) {
return cvar;
}
cvar = new ConfigVar(name, defaultValue);
ConfigVarsContainer.Register(cvar);
return cvar;
}
public static ConfigVar CVarString(string name, string defaultValue = "") {
ConfigVar cvar = ConfigVarsContainer.Get(name);
if (cvar != null) {
return cvar;
}
cvar = new ConfigVar(name, defaultValue);
ConfigVarsContainer.Register(cvar);
return cvar;
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7a57aa60af614860ba14c07fdb43ad4e
timeCreated: 1746661579

View File

@@ -1,49 +0,0 @@
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Console;
using RebootKit.Engine.Services.Game;
using RebootKit.Engine.Services.Input;
using RebootKit.Engine.Services.Simulation;
using UnityEngine.Events;
namespace RebootKit.Engine {
public partial class RR {
public static ConsoleService Console => s_Shared.m_ConsoleService;
public static InputService Input => s_Shared.m_InputService;
public static WorldService World => s_Shared.m_WorldService;
public static GameService Game => s_Shared.m_GameService;
public static DIContext DIContext => s_Shared.m_DIContext;
public static ConfigVar CVarIndex(string name, int defaultValue = -1) {
ConfigVar cvar = ConfigVarsContainer.Get(name);
if (cvar != null) {
return cvar;
}
cvar = new ConfigVar(name, defaultValue);
ConfigVarsContainer.Register(cvar);
return cvar;
}
public static ConfigVar CVarNumber(string name, double defaultValue = 0) {
ConfigVar cvar = ConfigVarsContainer.Get(name);
if (cvar != null) {
return cvar;
}
cvar = new ConfigVar(name, defaultValue);
ConfigVarsContainer.Register(cvar);
return cvar;
}
public static ConfigVar CVarString(string name, string defaultValue = "") {
ConfigVar cvar = ConfigVarsContainer.Get(name);
if (cvar != null) {
return cvar;
}
cvar = new ConfigVar(name, defaultValue);
ConfigVarsContainer.Register(cvar);
return cvar;
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 90375a4c5db84d538d31d54f302ac022
timeCreated: 1743212750

View File

@@ -1,14 +0,0 @@
using System;
using RebootKit.Engine.Services.Game;
namespace RebootKit.Engine {
public partial class RR {
public static void StartGameMode(GameModeAsset gameMode) {
if (gameMode == null) {
throw new ArgumentNullException(nameof(gameMode));
}
s_Shared.m_GameService.Start(gameMode);
}
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: c614fae80bc84ae8b7f3ee034a42f074
timeCreated: 1743437396

View File

@@ -1,80 +0,0 @@
using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using R3;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Console;
using RebootKit.Engine.Services.Game;
using RebootKit.Engine.Services.Input;
using RebootKit.Engine.Services.Simulation;
using UnityEngine.Assertions;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine {
public partial class RR : IDisposable {
internal static RR s_Shared;
static readonly Logger s_logger = new("RR");
DIContext m_DIContext;
ConsoleService m_ConsoleService;
EngineConfigAsset m_EngineConfigAsset;
GameService m_GameService;
InputService m_InputService;
WorldService m_WorldService;
DisposableBag m_DisposableBag;
public RR() {
m_DisposableBag = new DisposableBag();
m_DIContext = new DIContext();
// m_DIContext.AddInjector(new CVarFieldInjector());
}
public void Dispose() {
s_logger.Info("Shutting down");
m_DisposableBag.Dispose();
}
public async UniTask Init(EngineConfigAsset configAsset, CancellationToken cancellationToken) {
Assert.IsNotNull(configAsset, "Config asset is required");
s_logger.Info("Initializing");
m_EngineConfigAsset = configAsset;
CreateCoreServices();
CreateServices();
await UniTask.Yield(cancellationToken);
}
public void Run() {
StartGameMode(m_EngineConfigAsset.appConfig.gameConfig.defaultGameMode);
}
TService CreateService<TService>(ServiceAsset<TService> asset) where TService : class, IService {
TService service = asset.Create(m_DIContext);
m_DIContext.Bind(service);
m_DisposableBag.Add(service);
return service;
}
void CreateCoreServices() {
s_logger.Debug("Registering core services");
m_ConsoleService = CreateService(m_EngineConfigAsset.coreServices.consoleService);
m_InputService = CreateService(m_EngineConfigAsset.coreServices.inputService);
m_WorldService = CreateService(m_EngineConfigAsset.coreServices.worldService);
m_GameService = CreateService(m_EngineConfigAsset.coreServices.gameService);
}
void CreateServices() {
foreach (ServiceAsset serviceAsset in m_EngineConfigAsset.services) {
CreateService(serviceAsset);
}
}
}
}

View File

@@ -2,40 +2,35 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Main;
using UnityEngine;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Services.Console {
public interface IConsoleCommand {
string Name { get; }
string Description { get; }
void Execute(string[] args);
}
[AttributeUsage(AttributeTargets.Method)]
public class RCCMD : Attribute {
public string name;
public string description;
public class HelpCommand : IConsoleCommand {
public string Name { get; } = "help";
public string Description { get; } = "Prints available commands/cvars and their descriptions.";
public void Execute(string[] args) {
RR.Console.PrintHelp();
}
}
public class PrintCVarsCommand : IConsoleCommand {
public string Name { get; } = "print_cvars";
public string Description { get; } = "Prints all cvars and their values.";
public void Execute(string[] args) {
RR.Console.PrintCVars();
public RCCMD(string name, string description) {
this.name = name;
this.description = description;
}
}
public class ConsoleService : IService {
static readonly Logger s_logger = new(nameof(ConsoleService));
readonly List<IConsoleCommand> m_ConsoleCommands = new();
public struct ConsoleCommand {
public string name;
public string description;
public Action<string[]> action;
}
readonly List<ConsoleCommand> m_ConsoleCommands = new();
FileStream m_LogFileStream;
TextWriter m_LogFileWriter;
@@ -60,8 +55,7 @@ namespace RebootKit.Engine.Services.Console {
Load();
RegisterCommand(new PrintCVarsCommand());
RegisterCommand(new HelpCommand());
RegisterCommands();
}
public void Dispose() {
@@ -97,37 +91,34 @@ namespace RebootKit.Engine.Services.Console {
OnOutputMessage?.Invoke(message);
}
string[] ParseCommandInputArguments(string text) {
if (text.Length == 0) {
return Array.Empty<string>();
}
return new[] {text};
string[] ParseCommandInput(string text) {
return text.Split(' ');
}
// @NOTE: Input must be in format: "command arg1 arg2 arg3", one command = one call
public void Execute(string input) {
if (input.Length == 0) {
return;
}
string commandName = input;
if (input.IndexOf(' ') != -1) {
commandName = input.Substring(0, input.IndexOf(' '));
string[] arguments = ParseCommandInput(input);
if (arguments.Length == 0) {
return;
}
string[] arguments = ParseCommandInputArguments(input.Substring(commandName.Length));
string commandName = arguments[0];
foreach (IConsoleCommand command in m_ConsoleCommands) {
if (command.Name.Equals(commandName)) {
command.Execute(arguments);
foreach (ConsoleCommand command in m_ConsoleCommands) {
if (command.name.Equals(commandName)) {
command.action(arguments);
return;
}
}
foreach (ConfigVar cvar in ConfigVarsContainer.All()) {
if (cvar.name.Equals(commandName)) {
if (arguments.Length == 1) {
cvar.ParseFromString(arguments[0]);
if (arguments.Length == 2) {
cvar.ParseFromString(arguments[1]);
}
WriteToOutput($"<b>{cvar.name}</b> - {cvar}\n");
@@ -138,25 +129,84 @@ namespace RebootKit.Engine.Services.Console {
WriteToOutput($"ERROR: Command/CVar `{commandName}` not found.");
}
public void RegisterCommand(IConsoleCommand command) {
if (m_ConsoleCommands.Any(t => t.Name.Equals(command.Name))) {
s_logger.Error($"`{command.Name}` command is already registered");
public void RegisterCommand(string name, string description, Action<string[]> action) {
if (IsCommandRegistered(name)) {
s_logger.Error($"`{name}` command is already registered");
return;
}
m_ConsoleCommands.Add(command);
s_logger.Info($"Registered command: {command.Name}");
m_ConsoleCommands.Add(new ConsoleCommand {
name = name,
description = description,
action = action
});
s_logger.Info($"Registered command: {name}");
}
public void PrintHelp() {
public static ConsoleCommand[] GenerateCommandsToRegister() {
IEnumerable<MethodInfo> methods = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.SelectMany(type => type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static))
.Where(method => method.GetCustomAttributes(typeof(RCCMD), false).Length > 0);
List<ConsoleCommand> commands = new();
foreach (MethodInfo method in methods) {
RCCMD attribute = (RCCMD)method.GetCustomAttributes(typeof(RCCMD), false)[0];
if (!method.IsStatic) {
s_logger.Error($"Command `{attribute.name}` is not static, skipping");
continue;
}
if (method.GetParameters().Length != 1) {
s_logger.Error($"Command `{attribute.name}` has invalid number of parameters, skipping");
continue;
}
if (method.GetParameters()[0].ParameterType != typeof(string[])) {
s_logger.Error($"Command `{attribute.name}` has invalid parameter type, skipping");
continue;
}
Action<string[]> action = (Action<string[]>)Delegate.CreateDelegate(typeof(Action<string[]>), method);
commands.Add(new ConsoleCommand {
name = attribute.name,
description = attribute.description,
action = action
});
}
return commands.ToArray();
}
public void RegisterCommands() {
ConsoleCommand[] commands = GenerateCommandsToRegister();
foreach (ConsoleCommand command in commands) {
RegisterCommand(command.name, command.description, command.action);
}
}
bool IsCommandRegistered(string name) {
foreach (ConsoleCommand command in m_ConsoleCommands) {
if (command.name.Equals(name)) {
return true;
}
}
return false;
}
[RCCMD("help", "Prints help message with all commands and cvars")]
public static void PrintHelpCommand(string[] args) {
StringBuilder message = new();
message.AppendLine("Available commands:");
foreach (IConsoleCommand command in m_ConsoleCommands) {
foreach (ConsoleCommand command in RR.Console.m_ConsoleCommands) {
message.Append(" ");
message.Append(command.Name);
message.Append(command.name);
message.Append(" - ");
message.Append(command.Description);
message.Append(command.description);
message.AppendLine();
}
@@ -169,17 +219,18 @@ namespace RebootKit.Engine.Services.Console {
message.AppendLine();
}
WriteToOutput(message.ToString());
RR.Console.WriteToOutput(message.ToString());
}
public void PrintCVars() {
[RCCMD("cvars", "Prints all cvars")]
public static void PrintCVars(string[] args) {
StringBuilder message = new();
foreach (ConfigVar cvar in ConfigVarsContainer.All()) {
message.AppendLine($"{cvar.name} - {cvar}");
}
WriteToOutput(message.ToString());
RR.Console.WriteToOutput(message.ToString());
}
void Save() {

View File

@@ -1,5 +1,6 @@
using System.Text;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Main;
using RebootKit.Engine.Services.Input;
using UnityEngine;
using UnityEngine.InputSystem;

View File

@@ -44,6 +44,10 @@ namespace RebootKit.Engine.Services.ConsoleUI {
public event Action ClearRequested = () => { };
public void SetMessageContent(string message) {
if (m_LabelMessage == null) {
return;
}
m_LabelMessage.text = message;
m_ScrollView.schedule.Execute(() => { m_ScrollView.scrollOffset = new Vector2(0, m_ScrollView.contentContainer.contentRect.height); }).StartingIn(16);
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3d31f3fe40394defabe1b77f6ddf8861
timeCreated: 1746665551

View File

@@ -0,0 +1,13 @@
using RebootKit.Engine.Foundation;
namespace RebootKit.Engine.Services.Crosshair {
public class CrosshairService : IService {
public CrosshairService() {
}
public void Dispose() {
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f23a52c23aae46e8bd6b77222b10e5e2
timeCreated: 1746665562

View File

@@ -1,4 +1,5 @@
using System;
using RebootKit.Engine.Main;
using RebootKit.Engine.UI;
using UnityEngine;
using UnityEngine.UIElements;
@@ -10,13 +11,18 @@ namespace RebootKit.Engine.Services.Development {
VisualElement m_RootElement;
Label m_FPSLabel;
Label m_GameModeLabel;
void Update() {
if (m_RootElement == null) {
return;
}
m_FPSLabel.text = $"fps: {Mathf.RoundToInt(1f / Time.deltaTime)} | dt: {Time.deltaTime:F4}ms | runtime: {Time.time:F4}s";
Resolution resolution = Screen.currentResolution;
m_FPSLabel.text = $"fps: {Mathf.RoundToInt(1f / Time.deltaTime)} | dt: {Time.deltaTime:F4}ms | runtime: {Time.time:F4}s | resolution: {resolution.width}x{resolution.height}@{resolution.refreshRateRatio}Hz";
m_GameModeLabel.text = RR.GameModes.ActiveGameMode.CurrentValue != null
? $"Game Mode: {RR.GameModes.ActiveGameMode.CurrentValue.GetType().Name}"
: "Game Mode: none";
}
public override VisualElement Build() {
@@ -24,7 +30,8 @@ namespace RebootKit.Engine.Services.Development {
CreateLabel($"Toggle Overlay [F3] | RebootKit | game: {Application.productName}, version: {Application.version}");
m_FPSLabel = CreateLabel($"FPS: {Application.targetFrameRate}");
m_GameModeLabel = CreateLabel("Game Mode: none");
return m_RootElement;
}

View File

@@ -1,57 +0,0 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using R3;
using RebootKit.Engine.Foundation;
namespace RebootKit.Engine.Services.Game {
public class GameService : IService {
static readonly Logger s_logger = new(nameof(GameService));
[Inject] DIContext m_DIContext;
GameModeAsset m_GameModeAsset;
CancellationTokenSource m_DestroyCancellationTokenSource = new();
DisposableBag m_ActiveGameModeDisposableBag;
IGameMode m_ActiveGameMode;
public void Dispose() {
m_DestroyCancellationTokenSource.Cancel();
m_DestroyCancellationTokenSource.Dispose();
m_ActiveGameModeDisposableBag.Dispose();
}
public void Start(GameModeAsset asset) {
if (m_ActiveGameMode != null) {
s_logger.Warning("Game is already running");
return;
}
Stop();
m_ActiveGameModeDisposableBag = new DisposableBag();
m_ActiveGameMode = asset.Create(m_DIContext);
m_ActiveGameModeDisposableBag.Add(m_ActiveGameMode);
InitializeGameModeAsync().Forget();
}
async UniTask InitializeGameModeAsync() {
await m_ActiveGameMode.OnInit(m_DestroyCancellationTokenSource.Token);
m_ActiveGameMode.OnStart();
Observable.EveryUpdate().Subscribe(_ => { m_ActiveGameMode?.OnTick(); }).AddTo(ref m_ActiveGameModeDisposableBag);
}
public void Stop() {
if (m_ActiveGameMode == null) {
return;
}
m_ActiveGameMode.OnStop();
m_ActiveGameMode = null;
m_ActiveGameModeDisposableBag.Dispose();
}
}
}

View File

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

View File

@@ -1,5 +0,0 @@
namespace RebootKit.Engine.Services.Game {
public class Player {
}
}

View File

@@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: be94d631484b4bd2a2c9825290b74b36
timeCreated: 1744413325

View File

@@ -0,0 +1,62 @@
using System.Threading;
using Cysharp.Threading.Tasks;
using R3;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Main;
using RebootKit.Engine.Services.Simulation;
namespace RebootKit.Engine.Services.GameMode {
public class GameModesService : IService {
static readonly Logger s_logger = new(nameof(GameModesService));
[Inject] DIContext m_DIContext;
GameModeAsset m_GameModeAsset;
readonly CancellationTokenSource m_DestroyCancellationTokenSource = new();
DisposableBag m_ActiveGameModeDisposableBag;
readonly ReactiveProperty<IGameMode> m_ActiveGameMode = new(null);
public ReadOnlyReactiveProperty<IGameMode> ActiveGameMode => m_ActiveGameMode;
public void Dispose() {
m_DestroyCancellationTokenSource.Cancel();
m_DestroyCancellationTokenSource.Dispose();
m_ActiveGameModeDisposableBag.Dispose();
}
public void Start(GameModeAsset asset, WorldConfig worldConfig) {
if (m_ActiveGameMode.Value != null) {
s_logger.Warning("Game is already running");
return;
}
Stop();
m_ActiveGameModeDisposableBag = new DisposableBag();
m_ActiveGameMode.Value = asset.Create(m_DIContext);
m_ActiveGameModeDisposableBag.Add(m_ActiveGameMode);
InitializeGameModeAsync(worldConfig, CancellationToken.None).Forget();
}
async UniTask InitializeGameModeAsync(WorldConfig worldConfig, CancellationToken cancellationToken) {
await m_ActiveGameMode.Value.OnInit(m_DestroyCancellationTokenSource.Token);
await RR.World.LoadAsync(worldConfig, cancellationToken);
m_ActiveGameMode.Value.OnStart();
Observable.EveryUpdate().Subscribe(_ => { m_ActiveGameMode.Value?.OnTick(); }).AddTo(ref m_ActiveGameModeDisposableBag);
}
public void Stop() {
if (m_ActiveGameMode.Value == null) {
return;
}
m_ActiveGameMode.Value.OnStop();
m_ActiveGameMode.Value = null;
m_ActiveGameModeDisposableBag.Dispose();
}
}
}

View File

@@ -0,0 +1,12 @@
using RebootKit.Engine.Foundation;
using UnityEngine;
namespace RebootKit.Engine.Services.GameMode {
[CreateAssetMenu(menuName = RConsts.k_ServiceAssetMenu + "Game")]
public class GameServiceAsset : ServiceAsset<GameModesService> {
public override GameModesService Create(DIContext context) {
GameModesService service = context.Create<GameModesService>();
return service;
}
}
}

View File

@@ -3,7 +3,7 @@ using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
namespace RebootKit.Engine.Services.Game {
namespace RebootKit.Engine.Services.GameMode {
public interface IGameMode : IDisposable {
UniTask OnInit(CancellationToken cancellationToken);

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
@@ -7,30 +8,72 @@ using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceProviders;
using UnityEngine.SceneManagement;
using Logger = RebootKit.Engine.Foundation.Logger;
namespace RebootKit.Engine.Services.Simulation {
public class WorldService : IService {
readonly List<Actor> m_Actors = new();
WorldConfig m_Config;
public void Dispose() {
KillAllActors();
static readonly Logger s_logger = new(nameof(WorldService));
enum WorldState {
Unloaded,
Loading,
Loaded
}
public async UniTask Load(WorldConfig worldConfig) {
WorldState m_WorldState = WorldState.Unloaded;
WorldConfig m_Config;
AsyncOperationHandle<SceneInstance> m_SceneInstance;
readonly List<Actor> m_Actors = new();
public void Dispose() {
Unload();
}
public async UniTask LoadAsync(WorldConfig worldConfig, CancellationToken cancellationToken) {
await UniTask.WaitWhile(() => m_WorldState == WorldState.Loading, cancellationToken: cancellationToken);
Unload();
m_WorldState = WorldState.Loading;
m_Config = worldConfig;
AsyncOperationHandle<SceneInstance> handle = worldConfig.mainScene.LoadSceneAsync(LoadSceneMode.Additive, false);
await handle.ToUniTask();
m_SceneInstance = worldConfig.mainScene.LoadSceneAsync(LoadSceneMode.Additive, false);
await m_SceneInstance.ToUniTask(cancellationToken: cancellationToken);
await handle.Result.ActivateAsync();
SceneManager.SetActiveScene(handle.Result.Scene);
await m_SceneInstance.Result.ActivateAsync();
SceneManager.SetActiveScene(m_SceneInstance.Result.Scene);
foreach (GameObject root in handle.Result.Scene.GetRootGameObjects()) {
foreach (GameObject root in m_SceneInstance.Result.Scene.GetRootGameObjects()) {
foreach (Actor actor in root.GetComponentsInChildren<Actor>()) {
RegisterActor(actor);
}
}
m_WorldState = WorldState.Loaded;
}
public async UniTask<TRequiredActor> LoadAsync<TRequiredActor>(WorldConfig worldConfig, CancellationToken cancellationToken) where TRequiredActor : Actor {
await LoadAsync(worldConfig, cancellationToken);
TRequiredActor actor = m_Actors.FirstOrDefault(t => t is TRequiredActor) as TRequiredActor;
if (actor is null) {
s_logger.Error($"Actor of type {typeof(TRequiredActor)} not found in the scene");
return null;
}
return actor;
}
public void Unload() {
KillAllActors();
if (m_SceneInstance.IsValid()) {
m_SceneInstance.Release();
m_SceneInstance = default;
}
m_WorldState = WorldState.Unloaded;
}
public async UniTask<TActor> SpawnActor<TActor>(AssetReferenceT<GameObject> asset, CancellationToken cancellationToken) where TActor : Actor {

View File

@@ -0,0 +1,218 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 19001, guid: 0000000000000000e000000000000000, type: 0}
m_Name: Teko-VariableFont_wght SDF
m_EditorClassIdentifier:
m_Version: 1.1.0
m_Material: {fileID: 1107025805838944769}
m_SourceFontFileGUID: 7c0f83ef535953a479fd66830c0386b1
m_fontAssetCreationEditorSettings:
sourceFontFileGUID: 7c0f83ef535953a479fd66830c0386b1
faceIndex: 0
pointSizeSamplingMode: 0
pointSize: 90
padding: 9
paddingMode: 2
packingMode: 0
atlasWidth: 1024
atlasHeight: 1024
characterSetSelectionMode: 7
characterSequence:
referencedFontAssetGUID:
referencedTextAssetGUID:
fontStyle: 0
fontStyleModifier: 0
renderMode: 4165
includeFontFeatures: 0
m_SourceFontFile: {fileID: 12800000, guid: 7c0f83ef535953a479fd66830c0386b1, type: 3}
m_SourceFontFilePath:
m_AtlasPopulationMode: 1
InternalDynamicOS: 0
IsEditorFont: 0
m_FaceInfo:
m_FaceIndex: 0
m_FamilyName: Teko
m_StyleName: Light
m_PointSize: 90
m_Scale: 1
m_UnitsPerEM: 1000
m_LineHeight: 128.97
m_AscentLine: 86.22
m_CapLine: 56
m_MeanLine: 44
m_Baseline: 0
m_DescentLine: -42.75
m_SuperscriptOffset: 86.22
m_SuperscriptSize: 0.5
m_SubscriptOffset: -42.75
m_SubscriptSize: 0.5
m_UnderlineOffset: -11.25
m_UnderlineThickness: 4.5
m_StrikethroughOffset: 17.6
m_StrikethroughThickness: 4.5
m_TabWidth: 12
m_GlyphTable: []
m_CharacterTable: []
m_AtlasTextures:
- {fileID: 4954975621857233213}
m_AtlasTextureIndex: 0
m_IsMultiAtlasTexturesEnabled: 0
m_GetFontFeatures: 1
m_ClearDynamicDataOnBuild: 0
m_AtlasWidth: 1024
m_AtlasHeight: 1024
m_AtlasPadding: 9
m_AtlasRenderMode: 4165
m_UsedGlyphRects: []
m_FreeGlyphRects:
- m_X: 0
m_Y: 0
m_Width: 1023
m_Height: 1023
m_FontFeatureTable:
m_MultipleSubstitutionRecords: []
m_LigatureSubstitutionRecords: []
m_GlyphPairAdjustmentRecords: []
m_MarkToBaseAdjustmentRecords: []
m_MarkToMarkAdjustmentRecords: []
m_ShouldReimportFontFeatures: 0
m_FallbackFontAssetTable: []
m_FontWeightTable:
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
m_RegularStyleWeight: 0
m_RegularStyleSpacing: 0
m_BoldStyleWeight: 0.75
m_BoldStyleSpacing: 7
m_ItalicStyleSlant: 35
m_TabMultiple: 10
--- !u!21 &1107025805838944769
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Teko-VariableFont_wght Atlas Material
m_Shader: {fileID: 19011, guid: 0000000000000000f000000000000000, type: 0}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 4954975621857233213}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _FaceDilate: 0
- _GradientScale: 10
- _OutlineSoftness: 0
- _OutlineWidth: 0
- _PerspectiveFilter: 0.875
- _ScaleRatioA: 1
- _ScaleRatioB: 1
- _ScaleRatioC: 1
- _ScaleX: 1
- _ScaleY: 1
- _ShaderFlags: 0
- _Sharpness: 0
- _TextureHeight: 1024
- _TextureWidth: 1024
- _UnderlayDilate: 0
- _UnderlayOffsetX: 0
- _UnderlayOffsetY: 0
- _UnderlaySoftness: 0
- _VertexOffsetX: 0
- _VertexOffsetY: 0
- _WeightBold: 0.75
- _WeightNormal: 0
m_Colors:
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _OutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}
m_BuildTextureStacks: []
m_AllowLocking: 1
--- !u!28 &4954975621857233213
Texture2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Teko-VariableFont_wght Atlas
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_IsAlphaChannelOptional: 0
serializedVersion: 3
m_Width: 1
m_Height: 1
m_CompleteImageSize: 1
m_MipsStripped: 0
m_TextureFormat: 1
m_MipCount: 1
m_IsReadable: 1
m_IsPreProcessed: 0
m_IgnoreMipmapLimit: 1
m_MipmapLimitGroupName:
m_StreamingMipmaps: 0
m_StreamingMipmapsPriority: 0
m_VTOnly: 0
m_AlphaIsTransparency: 0
m_ImageCount: 1
m_TextureDimension: 2
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1
m_Aniso: 1
m_MipBias: 0
m_WrapU: 0
m_WrapV: 0
m_WrapW: 0
m_LightmapFormat: 0
m_ColorSpace: 1
m_PlatformBlob:
image data: 1
_typelessdata: cd
m_StreamData:
serializedVersion: 2
offset: 0
size: 0
path:

View File

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

View File

@@ -5,12 +5,18 @@ namespace RebootKit.FPPKit {
public class FPPLocomotion : MonoBehaviour {
[SerializeField] CharacterController m_CharacterController;
public float maxStrafeSpeed = 2.0f;
public float maxMovementSpeedBackward = 2.0f;
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;
[Range(0.0f, 1.0f)]
public float airControlRatio = 0.5f;
float3 m_CurrentVelocity;
bool m_IsFalling;
@@ -20,8 +26,12 @@ namespace RebootKit.FPPKit {
float3 m_PendingInputValue;
public bool IsGrounded => m_CharacterController.isGrounded;
public bool IsMovingBackwards { get; private set; }
public bool IsSprinting => m_IsSprinting;
public bool IsStrafing { get; private set; }
public float3 Velocity => m_CurrentVelocity;
public float3 Velocity => m_CurrentVelocity;
public float3 LocalVelocity => m_CharacterController.transform.InverseTransformDirection(m_CurrentVelocity);
void Update() {
ConsumePendingInput();
@@ -42,12 +52,13 @@ namespace RebootKit.FPPKit {
}
void ConsumePendingInput() {
// if (!IsGrounded) {
// m_PendingInputValue = float3.zero;
// return;
// }
if (!IsGrounded) {
m_PendingInputValue *= airControlRatio;
}
m_PendingInputValue.y = 0.0f;
float3 localInputValue = m_CharacterController.transform.InverseTransformDirection(m_PendingInputValue);
float pendingInputMagnitude = math.length(m_PendingInputValue);
float3 direction = float3.zero;
@@ -56,7 +67,21 @@ namespace RebootKit.FPPKit {
direction = math.normalize(m_PendingInputValue);
}
float movementSpeed = m_IsSprinting ? maxSprintSpeed : maxMovementSpeed;
IsStrafing = false;
IsMovingBackwards = false;
float movementSpeed;
if (math.dot(localInputValue, Vector3.forward) <= 0.0f) {
movementSpeed = maxMovementSpeedBackward;
IsMovingBackwards = true;
} else if (m_IsSprinting) {
movementSpeed = maxSprintSpeed;
} else if (math.abs(localInputValue.x) > 0.0f && math.abs(localInputValue.z) < 0.1f) {
movementSpeed = maxStrafeSpeed;
} else {
movementSpeed = maxMovementSpeed;
}
float3 movementVelocity = m_CurrentVelocity;
movementVelocity.y = 0.0f;
movementVelocity += direction * (movementSpeed * pendingInputMagnitude);

View File

@@ -3,6 +3,7 @@ using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Main;
using RebootKit.Engine.Services.Console;
using RebootKit.Engine.Services.Input;
using Unity.Mathematics;
@@ -12,7 +13,6 @@ using UnityEngine.InputSystem;
using Object = UnityEngine.Object;
namespace RebootKit.FPPKit {
public static class FPPConfig {
[ConfigVar("fpp.mouse.sens", 0.25f, "Mouse look sensitivity")] public static ConfigVar s_MouseSensitivity;
[ConfigVar("fpp.mouse.invert.y", 0, "Invert mouse look")] public static ConfigVar s_MouseInvertY;
@@ -21,20 +21,28 @@ namespace RebootKit.FPPKit {
public static class SensitivityReader {
public static float2 Read(InputAction action) {
if (action == null || action.activeControl == null) {
return float2.zero;
}
float2 sensitivity = action.ReadValue<Vector2>();
// @TODO: differentiate between gamepad and mouse
sensitivity *= FPPConfig.s_MouseSensitivity.FloatValue;
bool isGamepad = action.activeControl.device is Gamepad;
if (FPPConfig.s_MouseInvertY.IndexValue == 1) {
sensitivity.y *= -1;
if (isGamepad) {
sensitivity *= FPPConfig.s_GamepadSensitivity.FloatValue;
} else {
sensitivity *= FPPConfig.s_MouseSensitivity.FloatValue;
if (FPPConfig.s_MouseInvertY.IndexValue == 1) {
sensitivity.y *= -1;
}
}
return sensitivity;
}
}
public class FPPPlayerController : IController {
readonly Config m_Config;
FPPActor m_FPPActor;