Code-Only / Minimal Stride API Project #1253
Replies: 6 comments 21 replies
-
Would this allow the building of a headless stride server? If so I am very interested in this. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Review 3https://github.com/VaclavElias/Stride3DTutorials/tree/main/Other/Minimal Option 1 - Update in Game.csI made another working prototype with some above suggestions from @Eideren implemented. Any suggested naming convention can be changed of course. The reason that I didn't pass GameDefaults to Game/Run is because it can be actually separated in this prototype. You need to look into GameDefaults what it does and see if it makes sense to pass it to Game or Run, or keep it separately as it is. using (var game = new Game())
{
var _entity = new Entity(new Vector3(1f, 0.5f, 3f));
var _angle = 0f;
var initialPosition = _entity.Transform.Position;
// override for game.Run()
// start and update is optional, you can add any Update() through traditional script _entity.Components.Add(MyScript);
// it is using default engine Script Start and Update
game.Run(start: Start, update: Update);
void Start()
{
// adds default GraphicsCompositor, Scene, Skybox, CameraScript, Ground, Material
var defaults = new GameDefaults(game).Set3D();
// or pick up what you want
var defaults = new GameDefaults(game).Set().AddGround().AddSkybox().AddCameraScript().AddGameProfiler();
var model = new CubeProceduralModel().Generate(game.Services);
// I am getting DefaultMaterial here
model.Materials.Add(defaults.DefaultMaterial);
_entity.Components.Add(new ModelComponent(model));
_entity.Scene = game.SceneSystem.SceneInstance.RootScene;
}
void Update()
{
_angle += 5f * (float)game.UpdateTime.Elapsed.TotalSeconds;
var offset = new Vector3((float)Math.Sin(_angle), 0, (float)Math.Cos(_angle)) * 1f;
_entity.Transform.Position = initialPosition + offset;
}
} Engine Game.csAround 14 lines of code added public class Game : GameBase, ISceneRendererContext, IGameSettingsService
{
private RootScript rootScript;
protected override void Initialize()
{
...
// Add root script if exists
if (rootScript != null)
{
Script.Add(rootScript);
}
...
}
// this runs before Initialize() and we can't use Script.Add() here because Script doesn't exist yet
public void Run(Action start, Action update)
{
if (start != null || update != null)
{
rootScript = new RootScript(start, update);
}
base.Run();
}
} New class RootScript.cs public class RootScript : SyncScript
{
private readonly Action? startAction;
private readonly Action? updateAction;
public RootScript(Action? start = null, Action? update = null)
{
startAction = start;
updateAction = update;
}
public override void Start()
{
startAction?.Invoke();
}
public override void Update()
{
updateAction?.Invoke();
}
} Option 2 - Update in GameBase.csDepends where GameDefaults is (before Run() or inside Start()) affects the GameDefaults implementation. using (var game = new Game())
{
// adds default camera, camera script, skybox, ground, ..like through UI
var defaults = new GameDefaults(game).Set3D();
// only Start, which is called once the game is running
game.Run(start: Start);
void Start()
{
var model = new CubeProceduralModel().Generate(game.Services);
model.Materials.Add(defaults.DefaultMaterial);
// if you need Update(), just add your custom script, I added MotionComponentScript
var entity = new Entity(new Vector3(1f, 0.5f, -3f))
{
new ModelComponent(model),
new MotionComponentScript()
};
entity.Scene = game.SceneSystem.SceneInstance.RootScene;
}
} Engine GameBase.csAround 4 lines of code added public abstract class GameBase : ComponentBase, IGame
{
...
// maybe it can be private
public Action? StartAction;
...
// "Action? start = null" added
public void Run(GameContext gameContext = null, Action? start = null)
{
...
StartAction = start;
...
}
protected virtual void BeginRun()
{
// only this line added
StartAction?.Invoke();
} Let me know what you think. |
Beta Was this translation helpful? Give feedback.
-
Review 4Feedback from @Eideren and @manio143. Thanks for your feedback. I think your feedback can be merged together. Note again, that the naming convention can be changed to anything appropriate at the later stage. If I forgot to address anything you mentioned previously, please feel free to mention that again. Once our thoughts are more stable. I will ping all others who expressed interest in the code-only approach. Working prototypes:
Regarding the method signature which I left below as an example I have got these findings:
Engine Game.cs2 lines of code added instead of 14. public Action OnInitialize; //new line, not sure if you want to have it this way, using Action
protected override void Initialize()
{
...
OnInitialize?.Invoke(); //new line, end of the method.
} RootScript.csBased on your feedback, it might be simplified just to public class RootScript : SyncScript
{
private readonly Action? startAction;
private readonly Action<GameTime>? updateAction;
public RootScript(Action? start = null, Action<GameTime>? update = null)
{
startAction = start;
updateAction = update;
}
public override void Start()
{
startAction?.Invoke();
}
public override void Update()
{
updateAction?.Invoke(Game.UpdateTime);
}
} If we want other bits and we figure out how to get Scene public class RootScript : SyncScript
{
private readonly Action<Scene, IServiceRegistry>? startAction;
private readonly Action<Scene, IServiceRegistry, GameTime>? updateAction;
public RootScript(Action<Scene, IServiceRegistry>? start = null, Action<Scene, IServiceRegistry, GameTime>? update = null)
{
startAction = start;
updateAction = update;
}
public override void Start()
{
//create empty scene here so it can be passed
//SceneSystem.SceneInstance ??= new SceneInstance(Services) { RootScene = new Scene() };
startAction?.Invoke(SceneSystem.SceneInstance?.RootScene, Services);
}
public override void Update()
{
updateAction?.Invoke(SceneSystem.SceneInstance?.RootScene, Services, Game.UpdateTime);
}
} GameExtensions.csCode Only ExamplesExample 1 .NET 6, C# 10Nice and clean, encouraging using Components approach and using Script Components. using var game = new Game();
game.Run(start: Start, update: null);
void Start(Scene rootScene, IServiceRegistry services)
{
// adds scene, graphics comositor, camera, camera script, skybox, ground ..like through UI
game.SetupBase3DScene();
var entity = new Entity(new Vector3(1f, 0.5f, 3f))
{
new ModelComponent(new CubeProceduralModel().Generate(services)),
new MotionComponentScript()
};
entity.Scene = game.SceneSystem.SceneInstance.RootScene;
} or using var game = new Game();
game.Run(start: Start, update: null);
void Start(Scene rootScene, IServiceRegistry services)
{
// Cherry Pick
game.SetupBase(); // required, adds scene, graphics comositor, camera
//game.AddSkybox();
//game.AddGround();
game.AddMouseLookCamera();
game.AddProfiler();
var entity = new Entity(new Vector3(1f, 0.5f, 3f))
{
new ModelComponent(new CubeProceduralModel().Generate(services)),
new MotionComponentScript()
};
entity.Scene = game.SceneSystem.SceneInstance.RootScene;
} Example 2Using using (var game = new Game())
{
var entity = new Entity(new Vector3(1f, 0.5f, 3f));
var angle = 0f;
var initialPosition = entity.Transform.Position;
game.OnInitialize += () => System.Console.WriteLine("Hello, manio143");
game.Run(start: Start, update: Update);
void Start(Scene rootScene, IServiceRegistry services)
{
game.SetupBase3DScene();
var model = new CubeProceduralModel().Generate(services);
model.Materials.Add(game.NewDefaultMaterial());
entity.Components.Add(new ModelComponent(model));
entity.Scene = game.SceneSystem.SceneInstance.RootScene;
}
void Update(Scene rootScene, IServiceRegistry services, GameTime time)
{
angle += 1f * (float)time.Elapsed.TotalSeconds;
var offset = new Vector3((float)Math.Sin(_angle), 0, (float)Math.Cos(_angle)) * 1f;
entity.Transform.Position = initialPosition + offset;
}
} Let me know what you think 😀. |
Beta Was this translation helpful? Give feedback.
-
Issue opened here #1295. |
Beta Was this translation helpful? Give feedback.
-
Just adding a note. The Code Only is now supported through the Stride Community Toolkit, where all discussed above was implemented https://stride3d.github.io/stride-community-toolkit/manual/code-only/index.html |
Beta Was this translation helpful? Give feedback.
-
I have been working with ASP.NET Core intensively and was happy when they added Minimal API to simplify the setup and learning process, this got even better with .NET 6 which allows the use of global usings and file-scoped namespaces. Please be aware I am not a game developer but I like to experiment 🤣🙏.
Note: In this conversation API means Application Programming Interface.
There might be other reasons, why not to use a game editor, including:
I got inspired by multiple projects here and there from GitHub, some issues (#80, #986), our Discord discussions and help from the community and I created a working prototype for us. Ideally it should be very flexible, starting very simple but an experienced dev can still do what they want to do, and maybe eventually it could be also a hybrid and easy to hook to Stride Studio Editor to add anything else from the Editor.
https://github.com/VaclavElias/Stride3DTutorials/tree/main/Other/Minimal
Note: Any name convention can be changed as I get your feedback.
Review 1 - Concept
The main part is a new namespace Stride.Engine.Builder, which is going to be a wrapper for a boilerplate code. My prototype doesn't need to make any changes in the Game Engine but maybe some changes could make my implementation easier by exposing new event handlers or whatever you think would be better.
One NuGet package added to a new console application will make Stride to shine just with 21 lines or less 🌟🌟🌟
Note: The Add methods can't be before the .Build() because they need Game object but we can figure out a better logic here.
I have got these questions for us:
BeginRun()
in the MinimumGame class as I found that the only place this works but I don't like that I had to create a MinimalGame class. If we had a new EventHandler also for BeginRun (or wherever it is better) like we have Activated, Deactivated, Exiting, WindowCreated handlers in the GameBase, then I don't need MinimalGame class and would do the full set up in the GameApplication.cs. Do we need to update Stride GameBase class to simplify my implementation?Can anyone help/suggest how to improve the visual output, am I missing just material and lighting?..resolved-- The default could be as it is (first image)
-- And improved should look like on the second image (done in the editor as an example) or better, so anyone working with this NuGet package would be very impressed by Stride engine
What else could we add as optional? e.g. like AddSkybox, better lightning and so on...resolvedGenerated by this code-only project

Please feel free to add any comments or questions.
Thank you!
Beta Was this translation helpful? Give feedback.
All reactions