Interesting... though the usage of the Hub_* classes seem error prone as you might forget to set the arguments before raising the event and you might also forget which argument class is associated with which event.. I would probably drop the GameEventHub altogether, make the Hub_* classes static and add a method that raises events and takes parameters... So... rename Hub_ScoreChange to ScoreChangeArgs and instead of this: GameEventHub.ScoreChange.player = player; GameEventHub.ScoreChange.scoreToAdd = block.points; scoreChange.Raise(); just raise the event like this: ScoreChangeArgs.Raise(scoreChange, player, block.points) that's far less clunky - what do you say? for completeness sake... the class would look like this then: public static class ScoreChangeArgs { public static PlayerControl player; public static float scoreToAdd; public static Raise(GameEvent e, PlayerControl player, float scoreToAdd) { ScoreChangeArgs.player = player; ScoreChangeArgs.scoreToAdd = scoreToAdd; e.Raise(); } } and in Listeners you can also access the args more naturally: ScoreChangeArgs.scoreToAdd instead of GameEventHub.ScoreChange.scoreToAdd
@fumetsuhito55613 жыл бұрын
Thank you for going out of your way and sharing this. This definitely makes it less prone to error or forgetting what argument you want to send. But I do not know if this approach goes well with a scriptable object based event system. This system is user friendly where you right click and drag the event in the editor, the approach you are proposing does not seem to utilize the scriptable objects, or at least this is what I can visualize at the moment. And while forgetting what arguments to update is a disadvantage, the GameEventHub has the advantage of being flexible that you do not need to pass all arguments every time. And you can update the arguments without raising an event. For example, at the start of the stage you can set the value of all modifiers. Also if you have a decent naming convention you should easily be able to navigate the GameEventHub as it is segment into Events -> Events Arguments.
@DerClaudius3 жыл бұрын
@@fumetsuhito5561 No, the code I wrote just replaces the Hub and the Arg classes... it's still using your System... just makes it easier to call your ScriptableObject Event with Args...
@fumetsuhito55613 жыл бұрын
@@DerClaudius Do you reference the scriptable object event inside the static class, meaning the xxxx.Raise(arg1,arg2) does it also invoke the scriptable object raise method or is this method only to update the arguments?
@DerClaudius3 жыл бұрын
@@fumetsuhito5561 yes... raises the event as well... public static void Raise(GameEvent e, PlayerControl player, float scoreToAdd) { ScoreChangeArgs.player = player; ScoreChangeArgs.scoreToAdd = scoreToAdd; e.Raise(); }
@fumetsuhito55613 жыл бұрын
@@DerClaudius Oh makes sense. Indeed this is more elegant than the implementation I used. Much appreciated for sharing this improvement
@xtreme-software3 жыл бұрын
Fantastic Tutorial. Simply and clearly explained. This is how all tutorials should be.
@fumetsuhito55613 жыл бұрын
Much appreciated for the encouraging words. Hope to continue delivering such videos
@zionen013 жыл бұрын
Great video and very well presented. I too implemented a similar system based on the same video linked and tackled the "passing arguments" problem, but I love watching other approaches to this. My solution was pretty different though, based on generic versions of GameEvent and Listener: public class GameEventGeneric : ScriptableObject { private readonly HashSet _actions = new HashSet(); // rest almost same as GameEvent }
@fumetsuhito55613 жыл бұрын
Thank you for the encouraging comment and hope to deliver more. To be honest when I first faced the problem of "passing arguments" I started searching online for a solution and found a video made by Dapper Dino tackling the issue with generics similar to what you implemented I believe. But to be honest I am not yet comfortable with using generics (never used it before). I went with the approach in my video because I wanted an approach that I can fully warp my mind around.
@Diogo1Bastos3 жыл бұрын
@@fumetsuhito5561 To be honest, I am not very convinced with your solution, since we are adding a dependency to the GameEventHub on nearly all objects. However, if it works for you, go for it!
@fumetsuhito55613 жыл бұрын
@@Diogo1Bastos Really appreciate the feedback. I agree that quite a few objects will have a dependencies on the GameEventHub, but all these objects are objects that require to pass an argument or receive an argument. Regardless of what system is implemented, these objects will always require some sort of dependency. For example when using the normal event system, the objects that require to pass an argument will depend on the defined event to pass the argument, and the objects that responds to the event will depend on the defined event to acquire the argument. In this video the GameEventHub acts as a single entity that pipelines all events data and can be interacted with by anything anywhere and anytime (can even act as a data container for the latest arguments) The normal event system is more like multiple pipelines where each pipeline is a defined event and can only be interacted with when raising an event Also the implementation can be modified where you have multiple GameEventHubs each responsible for a different section/group Hope eases your concerns.
@YasnaKo3 жыл бұрын
Nice tutor, however I don`t like arguments passing method for some reason. And why are subtitles not available?
@fumetsuhito55613 жыл бұрын
Thanks for the nice comment. I do not have subtitles
@susovanbaral81383 жыл бұрын
so informative, thank you!
@Lycam3 жыл бұрын
Arguments can be passed by using Invoke(params[] object arguments) kind of function
@AstralNostalgia Жыл бұрын
how can avoid duplicated events? I mean I follow almost the same steps, but I am seeing duplicated events, do I need to create different events to do the same? I go to my Data folder , and create an event using the scriptable object "GameEvent" which I can create a template for that event, similar to the base tempalte that Ryan Hipple does.... but the problem is I got a problem like you showed in "issue passing NO arguments"... however I pass the 2 arguments and I continue seeing the same change in all of my prefab... like if they using polymorphism, and I am not using polymorphism. the behaviour of bad practice, in my case, is having the same behaviour for duplicated prefabs. also the "hub classes"? are monobehaviours? but they are not still attached to any object? .
@plaidev3 жыл бұрын
High quality tutorial! Thank you 👏
@fumetsuhito55613 жыл бұрын
Much appreciated for the kind words
@darkman2373 жыл бұрын
I heard that using static variables is not good because of memory allocation...
@fumetsuhito55613 жыл бұрын
I will be honest I am not that knowledgeable about memory allocation. But the best solution is to test the implementation and see if affects the game. If you are not trying to make every bit count it might not make a difference
@darkman2373 жыл бұрын
@@fumetsuhito5561 Thanks!
@Cyberfoxxy3 жыл бұрын
Static Class (lol) You can add an argument to the event by deriving a generic version and marking it as serializable: [Seriallizable] class PlayerEvent : UnityEvent { } Though I don't find it very clean either.
@XegaZero3 жыл бұрын
Very interesting video! Thank you!
@fumetsuhito55613 жыл бұрын
Thank you for the comment. And Happy you found it interesting
@skippythemagnificent81033 жыл бұрын
Great clean code and great example many thanks : D
@fumetsuhito55613 жыл бұрын
Really appreciate your comment and hope I can continue doing so
@skippythemagnificent81033 жыл бұрын
@@fumetsuhito5561 I hope you can too ; D
@windwalkerrangerdm Жыл бұрын
This solution is creative, but... but... I have trouble understanding the difference between dragging-dropping events and creating hard references through the code. They are the same thing, almost. When we do this we artificially create hard references anyway, they are just not visible, like sweeping the dust under the rug. Also if we are going to use a centralized static class, we can just create a huge, single static class that is going to be called anyways by any event raiser, and will be listened by all the listeners, and use it as a middleman. The ease of this video solution is to manage the event relationships through the editor, but in reality the cases are more complex. You have to create all these soft dependencies during runtime, and someone needs to manage this. Who will drag and drop the new connection during runtime? If you are worried about megalithic classes, and finding your code inside a huge static event manager, just make it a partial class and separate them into different sheets, label them with comments, or use inheritence or interface to differentiate between them, or just separate the events based on their functionality, like UIEvents, DataEvents, CollisionEvents etc. Which you already do here. Don't get me wrong. I don't have the answer and in fact I'm still searching for it. And this video showed me a new way, a clever way indeed, to change global variables when the event is raised and to read them when responding, because they happen sequentially anyways. And I will think about this, but just can't cope with editor drag&drop connections.
@fumetsuhito5561 Жыл бұрын
Thank you for sharing your experience and happy that this showed you a new method. to me the drag and drop is slightly more organizable and in front of you inshallah we will find the best answer
@windwalkerrangerdm Жыл бұрын
@@fumetsuhito5561 What do you think about the whole ECS system, do you have any experience with it? Does it solve some of these problems?
@fumetsuhito5561 Жыл бұрын
@@windwalkerrangerdm to be honest never used and I do not much background
@dankodev Жыл бұрын
I love this so much
@fumetsuhito5561 Жыл бұрын
Thank you
@fumetsuhito5561 Жыл бұрын
I saw that you had a long comment that you deleted. I believe you were trying to solve a problem you had. But the comment is deleted now. did you manage to solve the problem?
@dankodev Жыл бұрын
@@fumetsuhito5561 yes, I have. I couldn't figure out how to implement this system with responses that are dependent on Update() or FixedUpdate(). Essentially, I was trying to move a door when a platform was stepped on. I realized I could do this by making the response something that changes a bool condition (which then triggers the door to move), or use a tweening library. I decided to go with LeanTween because it looks super clean and achieves the desired effect independently from Update(), so that way I can set one method as the response to the event. LeanTween also has a ton of different acceleration/deceleration curves.
@fumetsuhito5561 Жыл бұрын
@@dankodev happy to hear that
@ewwitsantonio3 жыл бұрын
great tutorial!! wish i found your channel earlier! liked and subscribed :)
@lAztechl3 жыл бұрын
Thank you for the great tutorial. My only problem with the code is like it defeats the purpose of having the event not care about the listener. Like if I want to create an event. The player must know what data and argument to pass in each listener. and is not good at handling multiple types of listeners that have different data needed but listen with the same event. Generics can solve it but add complexity in creating an event. But on in all great tutorial and help me in some concept in the scriptable object.
@fumetsuhito55613 жыл бұрын
Thank you for the feedback and thank you for the compliment on the tutorial. I slightly disagree with the comment "The player must know what data and argument to pass in each listener". it is the listener that needs to know what data and arguments to use from the GameEventHub when an event is raised. Or in other words the method that the listener (since the listener just invokes the method you attach to it) invokes is the one responsible for getting the correct data from the GameEventHub. This supports different listeners having different data needs but the using same event. For example let us say we have 1 event that can take up to 10 arguments in the GameEventHub. Just before raising the event you update the 10 arguments. We can have listener1 (the method that is invoked) use arguments 1 to 5, while listener2 use the arguments 6 to 10.
@lAztechl3 жыл бұрын
@@fumetsuhito5561 I Agree with the player must know what argument to pass. but I disagree with the player knowing the listener with different data. and also the GameEventHub is also prone to error. like if you forgot to set the data or you incorrectly set the data. Also, it can have problems if the same event access by different characters. For example, a Two character access the same event it can happen that the second character first edits the event then comes to the first character to edit the event again. and it will raise the event in which it may or may not produced an error. and also the nature of static variables that can be accessed by all scripts is the problem, it can edit by any script that not particularly needed access to that data.
@fumetsuhito55613 жыл бұрын
@@lAztechl I agree that the current implementation is not suited for multiple characters, but the GameEventHub concept can be implemented to account for multiple characters or group of events by creating a GameEventHub for each character. And a simple modification/logic can be implemented to check what character raised the event so they never get mixed. Also the GameEventHub does not need to be a static class. It can also be implemented as a Scriptable Object. This way you can control what scripts can access the GameEventHub. And your point about error prone is completely fair. This implementation has some bias due to me being a solo developer where theoretically I know the entire code in and out and I am responsible for updating the GameEventHub in the correct order. That is why I made it a static class rather than a scriptable object (I do not want to be dragging a scriptable object to all the scripts that require it)
@MrGluto3 жыл бұрын
Surprised you didn't use GameEventT for the base class of game events. Scriptable events are uncoupled and scalable, but GameEventHub is very coupled and unscalable. If you used GameEventT each defined Scriptable Evenf would define the parameters they pass on broadcast, not requiring couple another class that also needs maintaining.
@fumetsuhito55613 жыл бұрын
What is GameEventT?
@lAztechl3 жыл бұрын
@@fumetsuhito5561 If I Understand it correctly it is a Generic that accept Generic Data. Like GameEvent in that way. the player will not care who listens to the event but only cares is passing the PlayerScore Data in the GameEvent. it is scalable and it can support different types of listeners.
@fumetsuhito55613 жыл бұрын
@@lAztechlThank you for commenting. Does this implementation require to have a specific GameEvent for each type of data? for example in my implementation I right click and only have one GameEvent Scriptable object. Do the generic implementation require different types of GameEvent?
@lAztechl3 жыл бұрын
@@fumetsuhito5561 If you mean the same GameEvent, the answer is no. because if you set a class that accepts generic like GameEvent the T part can be any data you want like int, float, class, struct, and so on. however, listening to that event needs a different listener for each specific data. but it's worth it because if you set up it correctly it can easily scale up. unlike having static data that can be accessed by any script.
pls add an auto subtitle for people who main language is not english :)
@DeathxStrike183 жыл бұрын
Seems like a big round about way of just requesting player ID and passing through player ID on collision update on collision get id of object i.e playerid player=player Id update event like what
@fumetsuhito55613 жыл бұрын
I apologies I do not exactly get what part you are referencing. But I agree that there are some examples of implementation of the Events System that I show in the video that can be made more efficient/effective when working on a real project.
@DeathxStrike183 жыл бұрын
@@fumetsuhito5561 i mean your having the player1/player2 be too concrete/integrated, your already assigning a playerid on creation so have the id be requested at the begining of the event on colision rather than a static player1 value so when a colision is detected the player provides its Id during the event.
@fumetsuhito55613 жыл бұрын
@@DeathxStrike18 I understand now, and completely agree with you that would be a better implementation
@DeathxStrike183 жыл бұрын
@@fumetsuhito5561 yeah sorry its heard to explain sometimes when you know how to do it, but cant put it to words. Why I do and not teach lol.
@Alperic272 жыл бұрын
designing with globals is … .. real bad design
@brannonharris46423 жыл бұрын
Oh you mean what Unreal Engine has out of the box. Got it.