merci @yar
Ok second thing that is really making my eyes itching is you should do something like that from beginning:
Currently your business logic is maybe your ball manager.
Second notice also, instead of having:
Channel<Scene> separately here, it’s better to collapse together a ModelManager and Scene so you have something like SceneController class, that has Scene as a property. You see the key here is:
Entry holding MyScene should be a member of “service” basically this “service” should manage model, e.g:
// result of button press
var newBall = new Ball(...);
// reusable function that you may use somewhere else
sceneSerivice.AddBall(newBall);
Meaning:
The green should be member of class where Channel is, the red should produce a ball that fed to method call… The key here, is separation of concerns, also something that it should be reusable, e.g. if you would then add another ball you would copy the add ball function over, witch is a. error prone (you change model it’s dead), b. breaks SRP (redundant selector logic copied over)
Next stop is this:
This is not something that is going to work like that, basically we need to decide on some serializable Move format here, e.g. this is a question of playback, basically how it should be done in my opinion:
MovesRecorder is a class that hosted in runtime, when you initialize your aplication your business logic should have a “Register” method, that would register MovesRecorder as something you can call from whatever place in your app.
Basically you can think about this as:
// hook to runtime
var moveRecoder = businessLogic.MoveRecorder;
// get ball that is selected currently
var activeBall = busnisLogic.SelectedBall;
// start process
moveRecorder.StartRecording( .... ); // My ball instance? e.g. selectedBall
// then somewhere else
// get the result of process run
var recordedSequence = moveRecoder.StopRecording()
// submit to currently selected ball
var nextBall = activeBall.SetAnimation(recordedSequence):
// commit to model
stageService.SetBall(ballIndex, nextBall);
Note that recoded sequence maybe in different formats like a <TimeStamp, Vector2> so you now at witch time this is happening, or Spread in any case Ball model should include a result of recrod not the instance of recorder…
The second approach is also possible, something called “Transient”
We create a “session” with Ball and MoveRecorder, we need runtime to evaluate that session.
Like in first case we will create some sort of animation recorder, and we will ask it to produce animation for us. In second case we would need to create animation recorder per entry like per ball… This is architectural question… since MoveRecorder can be single thing but can produce multiple animations same time, the difference is going to be the way it setup in runtime like: “obviously” vs “tricky”
I guess best would be to stick with first approach where we have one move recorder and we call it with different args and methods…
Here I made you small approach how I see it..
Should maybe try this direction… Not sure it’s going to have “Runtime” (since i don’t clearly see it purpose yet) like that, but at list it seems quite composable for a smaller up.
MVApproach.vl (50.0 KB)
Thank you
so much
@antokhio , this is really extremly clear about Service Structure. This really answers my question of approaching things, damned i have so many rewriting to do !!! things begin to be less confuse for me.
ok so ViewModel is more a runtime. got it. while i m working to adapt it as “school works” :) some little questions :
If you have some time i m VERY interrested by the AddRandomBallCommand part for some detail about “This” ( not getting how all of this is working as i have not ouput so im quite a bit puzzled by it.) I dont get the connexion bewteen the model and this AddRandoMBallCommand
Just, before talking about little objects inside, trying to catch the idea of global interactions within the different parts of a model.
I let aside for the moment the balls and move recording.
I have :
- A show that contains scenes, that output a selected scene and an index of this selected scene
- A show manager to handle all the logical stuff of manipulating the model ( no rutnime)
- A runtime
- A render window
Actual trouble is the UI part, making disapear the UI if i link to the button the channeling access to the manager function.
DummyProject Service.vl (206.6 KB)
Still not sure meaning of “runtime” (prolly need to check the video you got it from), the ViewModel is kind of orchestrator, e.g. a place where you observe events (like button click) and dispatch logic (like operation call).
About this, it’s a reference of a Class to itself. E.g.:
You can kind of use pads for that, but in the regions like cache or for each channel this seems more straightforward.
Maybe you need to enable:
About your patch I have some exception when opening it:
Exception
EmitException: "C:\Users\antok\Downloads\DummyProject Service.vl.1.1.cs(529,45): error CS1061: 'UI_P' does not contain a definition for 'AddAScene' and no accessible extension method 'AddAScene' accepting a first argument of type 'UI_P' could be found (are you missing a using directive or an assembly reference?)"
StackTrace:
VL.Lang.Platforms.Roslyn.TargetCompilation+<>c__DisplayClass6_3 { internal System.ValueTuple<Microsoft.CodeAnalysis.PortableExecutableReference, VL.Core.RawAssembly> <InitializeAsync>g__Emit|5(bool includeSource, System.Threading.CancellationToken token, VL.Lang.Platforms.Roslyn.<>c__DisplayClass6_2& ) { ... } }
VL.Lang.Platforms.Roslyn.TargetCompilation+<>c__DisplayClass6_0 { internal VL.Lang.Platforms.Roslyn.ProjectCompilation <InitializeAsync>g__Build|0(VL.Lang.Platforms.Roslyn.ProjectBuildRequest projectBuildRequest) { ... } }
VL.Lang.Platforms.Roslyn.TargetCompilation+<>c__DisplayClass6_1+<<InitializeAsync>b__1>d { private virtual void MoveNext() { ... } }
System.Runtime.ExceptionServices.ExceptionDispatchInfo { public void Throw() { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void ThrowForNonSuccess(System.Threading.Tasks.Task task) { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task, System.Threading.Tasks.ConfigureAwaitOptions options) { ... } }
System.Runtime.CompilerServices.TaskAwaiter`1 { public TResult GetResult() { ... } }
VL.Lang.Platforms.Roslyn.TargetCompilation+<InitializeAsync>d__6 { private virtual void MoveNext() { ... } }
System.Runtime.ExceptionServices.ExceptionDispatchInfo { public void Throw() { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void ThrowForNonSuccess(System.Threading.Tasks.Task task) { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task, System.Threading.Tasks.ConfigureAwaitOptions options) { ... } }
System.Runtime.CompilerServices.TaskAwaiter`1 { public TResult GetResult() { ... } }
VL.Lang.Platforms.Roslyn.TargetCompilation+<CreateAsync>d__4 { private virtual void MoveNext() { ... } }
System.Runtime.ExceptionServices.ExceptionDispatchInfo { public void Throw() { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void ThrowForNonSuccess(System.Threading.Tasks.Task task) { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task, System.Threading.Tasks.ConfigureAwaitOptions options) { ... } }
System.Runtime.CompilerServices.TaskAwaiter`1 { public TResult GetResult() { ... } }
VL.Lang.Platforms.Roslyn.TargetCompilation+<WithCompilationAsync>d__43 { private virtual void MoveNext() { ... } }
System.Runtime.ExceptionServices.ExceptionDispatchInfo { public void Throw() { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void ThrowForNonSuccess(System.Threading.Tasks.Task task) { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task, System.Threading.Tasks.ConfigureAwaitOptions options) { ... } }
System.Runtime.CompilerServices.TaskAwaiter`1 { public TResult GetResult() { ... } }
VL.Lang.Platforms.Roslyn.TargetCompilation+<VL-Lang-Symbols-ITargetCompilation-WithCompilationAsync>d__44 { private virtual void MoveNext() { ... } }
System.Runtime.ExceptionServices.ExceptionDispatchInfo { public void Throw() { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void ThrowForNonSuccess(System.Threading.Tasks.Task task) { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task, System.Threading.Tasks.ConfigureAwaitOptions options) { ... } }
System.Runtime.CompilerServices.TaskAwaiter`1 { public TResult GetResult() { ... } }
VL.Model.VLSession+<CreateTargetCompilationAsync>d__150 { private virtual void MoveNext() { ... } }
System.Runtime.ExceptionServices.ExceptionDispatchInfo { public void Throw() { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void ThrowForNonSuccess(System.Threading.Tasks.Task task) { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task, System.Threading.Tasks.ConfigureAwaitOptions options) { ... } }
VL.Model.VLSession+<UpdateRuntimesAsync>d__149 { private virtual void MoveNext() { ... } }
System.Runtime.ExceptionServices.ExceptionDispatchInfo { public void Throw() { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void ThrowForNonSuccess(System.Threading.Tasks.Task task) { ... } }
System.Runtime.CompilerServices.TaskAwaiter { private static void HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task task, System.Threading.Tasks.ConfigureAwaitOptions options) { ... } }
VL.Model.VLSession+<UpdateCompilationAsync>d__173 { private virtual void MoveNext() { ... } }
Again, you mixing stuff together:
The red part is one “logical segment” the “green part” is another logical segment, they don’t really belong to the place you have them at all.
Then:
Why you create a channel in the ui, that calls an operation of another calls in the UI.
Let say you have a
- File menu
- Button in the Hotbar
- Global Hotkey
Each of this would require to go to certain place in the patch create a channel (three channels), then you need add method, you need a subscriber (ForEach Channel)
Instead you should create channel in the ViewModel class, in there you should locate scene manager call operation on it and pass this channel to ui, so UI does not know even that ShowManager exists…
Then This works only inside class itself you can’t call this on process.
The general approach is that you store channels in the View Model and do the “Routing” there and pass channels as properties
Show manager is a Service, it should be connected other way around:
Let’s move next:
What is ShowManager?
It should keep Scenes?
It’s looks like a service to me so:
Why would you want to handle side effect in the in the place you want to store scenes…
Like why don’t you call this a ScenesManager instead:
You want to make sure it’s avalible:
Then we are going to create a channel:
And use our scene manager, then we are going to add scene index you had:
Now we can:
DummyProject Service2.vl (213.4 KB)
woaw, thats really amazing ! thank you @anthokio. So a manager contains model to handle and manager ! thank you for this master piece of clarity on the structure. i will come back to u once integrating by doing in the dummyproject. (and thank you for empty node, i m fighting with this 2 years now !)
super !





















