I’m looking for an existing implementation of a data structure (represented by a generic class) that stores a base value, a list of delegates that can sequentially (according to priority) modify the base value, and a property from which the current value (base value after modifications) can be retrieved. When a new delegate is added or removed, the current value is recalculated.
Pseudocode Example: We have Car script with MaxSpeed that can be dynamically changed.
Processed MaxSpeed = new Processed(100f)
MaxSpeed.Value equals 100.
We add Booster component that increases speed by 20.
MaxSpeed.AddDelegate((ref float speed) => speed += 20, 0 *priority*)
MaxSpeed.Value equals 120.
Then we want to add another boost delegate that multiples speed by 2
MaxSpeed.AddDelegate((ref float speed) => speed *= 2, 1)
MaxSpeed.Value equals 240.
We changed our mind and we don’t need 1 boost anymore.
MaxSpeed.RemoveDelegate((ref float speed) => speed += 20, 0)
MaxSpeed.Value equals 200.
The problem is that it will take many days of work to get my implementation perfect, and if there is an existing implementation, I would like to use it, rather writing my own. What existing solutions can be used?
Unless I’m overlooking something the implementation is rather straightforward, almost trivial. Perhaps you are overthinking? I skimmed over your example implementation and see a lot of code that seems utterly unnecessary given your requirements. I wouldn’t use delegates but an interface.
Pseudocode:
interface IValueModifier<T>
{
T ModifyBaseValue(T);
}
class ThatThing<T>
{
public T BaseValue {get;set;}
public T ModifiedValue {get;set;}
private List<IValueModifier<T>> m_Modifiers;
public void AddModifier(IValueModifier<T> mod);
public void RemoveModifier(IValueModifier<T> mod);
}
The ModifiedValue getter would enumerate the m_Modifiers list where each gets passed a value T and returns the modified value T which is then fed into the next IValueModifier and so forth.
There may be details that I blissfully ignored, ie restricting the generic or some other generic doodads. But in essence, this is what you need to write according to your specs.
Note that you DO NOT want to recalculate the value every time a modifier is added or removed. You only need to calculate the modified value when someone requests it. At most, you can cache the most recent modified value and mark it dirty when either modifiers or base value changes.
Since I am a noob, it takes me a lot of time to figure out a sensible architecture.
I don’t like extensive use of interfaces, especially in flexible systems, because at least they limit freedom of naming. Is it problem only for me?.. Idea with storing modifiers by interfaces is clever, but I think it is not suitable for complex cases (with storing reactive subscriptions alongside delegates for example). However, I think there is a place for using interfaces to limit the developer’s scope on a finished set of game mechanics. For example, in the case with a car - to determine what and how mutators/modifications/boosters/*as you name it* can improve (The engine modifier changes the acceleration, maximum speed, maximum load capacity, etc.).
I don’t think so. Value can be requested at Update() for example. It’s not very efficient to recalculate on get. Moreover, I wrote a subclass that provides a reactive field (from R3) that copies the Value. So, it is important for us to recalculate the Value when adding/removing modifiers so that the reactive field subscribers will be notified in time.
In the near future I want to separate stock C# and reactive addition.
If there is no existing implementation of something like this, then I’m very glad that I took the time to write it myself. In any case, it was quite interesting.
Why do you think that? You can name an interface any way you want, and you can name a class/struct implementing an interface any way you want.
Since you’re a beginner you probably use these techniques incorrectly.
The interface-driven version is reactive because something from the outside calls the “calculate” method and the implementing class does not care who does so or when.
I don’t even see the need for delegates here. I mean after all, what you should be concerned with is to get the final modified value derived from the base value through modifier methods. The process of modifying that value should not invoke any events or delegates. Anything that further needs to modify the value should in itself be a modifier type.
I strongly doubt that you need the value be “reactive”. At least in games it’s preferered to have values like these not inform when they change, but rather get polled once per frame because the value shouldn’t change once it has been calculated for any given frame.
Yeah not sure why you’re concerned about naming. Clear naming is important, but it does require some creativity.
If you are concerned about performance, delegates cause lots of allocations and are slower invocation wise. Though like CodeSmile says, don’t try to prematurely optimise. But there’s easy ways to optimise this, regardless.
In either case you can honestly get the same behaviour here with either an interface or delegates. But interfaces have some notable advantages:
Better performance (namely less invisible allocations)
Can be natively serialized in Unity via [SerializeReference] (plain C# objects only, of course). Natively this can be used to view them in the inspector, and some editor tooling allows you to modify them in the inspector too.
Subjectively I’d say interfaces make it a lot more clear how an API should be used. They have more flexibility overall than a method as well.
And where’s the fun in coding if you don’t try and code things yourself?
There can be a very sizable benefit to using delegates when it comes to brevity, though.
With even just a simple example with two different value modifier implementations, the amount of code needed when using an interface is almost an order of magnitude more.
Delegate
var example = new DynamicValue<float>
(
10f,
x => x * 2f,
x => x + 5f
);
Interface
public sealed class MultiplyFloatModifier : IValueModifier<float>
{
readonly float multiplier;
public MultiplyFloatModifier(float multiplier) => this.multiplier = multiplier;
public float Apply(float input) => input * multiplier;
}
public sealed class AdditionFloatModifier : IValueModifier<float>
{
readonly float addition;
public MultiplyFloatModifier(float addition) => this.addition = addition;
public float Apply(float input) => input + addition;
}
...
var example = new DynamicValue<float>
(
10f,
new MultiplyFloatModifier(2f),
new AdditionFloatModifier(5f)
);
I’m not really sure why the OP is being pressured into introducing more complexity into their code. In my view simply using delegates makes a lot of sense - unless there’s a need to support something like [SerializeReference], for example.
It’s good to be aware that using an interface is also an option, but I don’t think either option is inherently superior. It all depends on the exact use case.
This looks neat, but it does not support the requirement of adding/removing modifiers to a value. To achieve this, you need the Add/Remove methods and the client code also needs a reference of the delegate as a variable/field because you can’t pass the lambda again since this creates a new object every time (eg won’t get found in the list of existing modifiers).
In addition to what everyone above has said, I want to highlight two things that have not been mentioned from your requirements as shown in the pseudocode:
You mention and show an example with priority:
You will probably need a sorted collection, probably the simplest solution is a SortedList, to keep the delegates in order of priority for the modification of the base value.
This doesn’t have to be a sorted collection, you can also implement a sorting algorithm to run whenever the getter is called and the boolean is dirty as mentioned.
Performance will vary depending on size and how often delegates are added/removed plus how often the getter will be called so you will have to benchmark it, if performance is important for you use case.
You mention addition/removal of delegates but in you example you show for addition:
And for removal:
This cannot be done in C#, the second delegate that you try to remove is different from the first, you will have to cache your delegates and remove them by name, lamdas are considered different delegates even if they have the same implementation.
Also, now that I had some time to think about it, maybe you can use my https://github.com/meredoth/Stat-System, it doesn’t do exactly as you ask: It doesn’t have a delegate for each modifier, but you can create modifier types, each with its own order and way of calculation (by overriding a method) and then you can create modifiers of that type.
If having each modifier perform its own different calculation is not a requirement but instead you can do with having a number of different ways of calculations(your delegates), let’s say 10-20 different modifier types and having modifiers for those types, then you can use or get inspiration from my implementation.
It’s not ironic, I am really bad at interfaces. I just don’t get it… How I should allow modifier script to modify more than one value of certain type? Like, I want to modify 2 ints in modifier script. What should I do in this case? Inherit some interfaces? Create another one? And how I would call methods to execute in this situation if methods has different names? What if my script modify a lot of things? It will be a long line of interfaces or one big interface that inherited methods from other interfaces or smth else?
Create classes like SisusCo did? It’s good approach, but I will have to create a lot of classes… Okay, maybe it’s a way.
And after all you will have an endpoint named as mentioned in interface. And I have no problem with it, when it’s wrapped in class as previously mentioned. But if we add interfaces to a class directly (without some modifier class wrapper), then it turns out that we have a bunch of methods that have names that are detached from the context. For example, in my implementation I have processor(delegate) - IsRunningOnCanRun. I immediately understand what is being modified and on what basis.
I understood that under-hood allocations of delegates are bad (I had no idea, I thought like: “It’s just a reference to a method, what possibly can be memory expensive?”). Ok, I will read about it soon.
I understand. And it was pseudocode as I mentioned. To use unnamed anonymous methods as delegates I encapsulated them in class. It make more sense then I use that class not only for storing delegates but also for subscriptions (to update target value) for reactive fields which are used in method.
Thanks, I will check it later
I almost finished my implementation with delegates in local repo. So, after that, I will think what I can do with interfaces.
You seem to be misunderstanding the intent and purpose of a modifier. A modifier would only ever manipulate a single value that it is bound to. If you need to modify two values, you add one modifier per value.
Regarding all your questions I’d simply advice to learn about interfaces as well as design patterns. Interfaces can be used like classes except that they only offer you the methods and properties defined by the interface, and the class needs to implement the interface methods. And interface is a contract, an obligation for the implementing type, a guarantee for the caller.
You need a controlling class that holds all values holding their modifiers, and this then “ticks” all the values to do their job which in turn runs the value’s modifiers. But like I said earlier, this is probably completely unnecessary in a frame-driven game loop. Each value class or struct simply has a ModifiedValue property which, upon accessing it, calculates the value using its base value and passing it along to each modifier.
Again, you are probably thinking about this entirely backwards. You expect that every change immediately has to give you a new value and inform a whole swath of objects that have an interest in the updated value. But this is absolutely unnecessary since you can get the new value every frame, and once only, in order to use it in further calculations or to show it in the GUI.
The way you think about this sounds like you have a background in application GUI programming which is commonly stateless and responds to every change immediately. For instance, the user changes a dropdown and the GUI needs to respond to a change event in order to update other fields. But in a stateful game engine, the entire GUI is drawn over and over again every time, so it really only needs to get the currentmost state (value or setting) and display that when its update during the frame occurs. The only thing you need to watch out for is to update values before the GUI draws or the values are used elsewhere, otherwise the value changes will lag one frame behind.
I understood it. Maybe it’s not obvious from my previous answer.
I just don’t really like that I’ll have to make a lot of classes.
But I still dont understand why I should recalculate value on get or do things like this (I just wanted to make a flexible and easy to use system, without gimmicks):
Let’s say I have a lot of modifiers, but they are added very rarely. Why recalculate them every time on get? Why not just recalculate them when adding/removing modifier or when changing a reactive variable that the method depends on. This can only be a problem if multiple reactive fields are updated at the same time and thus cause multiple recalculations where one recalculation is enough. In any case, in my implementation I added the option to recalculate on get.
I have background in nothing, I am just a student.
I think that a developer is free to use what is convenient for him. So it would be stupid not to finish delegate implementation to use it as an option.
I would just have a dirty flag, and only recalculate the value if its dirty when accessed. Ergo, when a modifier has been added/removed when it previously wasn’t dirty.
This is a totally valid approach. I find it highly unlikely that you’ll run into a scenario where so many reactive properties are changing during the same frame, that it’ll become a noticeable performance bottleneck in your game.
And if it does, then it should be quite easy to tweak the system to use a dirty flag instead, like @spiney199 suggested.
Using delegates should also be totally fine performance wise. If you want to get rid of garbage generation when modifying the invocation list, you can just use a List<MyDelegate> instead of MyDelegate.
That couldn’t be more wrong. You can say that for as long as you’re a student or work entirely for yourself. But once you work for someone who also does code reviews, you’ll quickly be in a discussion like this.
Now if you keep trying to stick to your way of doing things while the tech lead and possibly other senior engineers have differing opinions, you probably won’t be employed for long in that company.
Always keep an open mind. The ones who keep using what is “convenient” to them will only aggravate their peers AND increase the cost of programming and maintenance because for sure copypasta is super convenient initially.