Expression Trees in Unity: The SECRET to Flexible Game Logic

Поділитися
Вставка
  • Опубліковано 22 лис 2024

КОМЕНТАРІ • 114

  • @git-amend
    @git-amend  5 днів тому +18

    Happy Sunday Everyone! Looks like the channel is about to hit another milestone! Thanks for all the Likes, Shares and Comments! 🤘

  • @asafelbaz6
    @asafelbaz6 5 днів тому +26

    After 2 years of programming and 1.5 years of working with Unity, I still can't understand most of the black magic you're doing in your videos. But one day, I will, sir.
    One day, I will.

  • @unseenexception
    @unseenexception 5 днів тому +10

    The coolest thing about using Expression Trees in Unity is you can set up different behaviors right from the Inspector - no code changes needed! Plus, with key-to-GameObject dictionaries, you can directly reference Unity components in your expressions, making it super handy for game logic binding like {sensor}.Radius = {skill.Radius}. It’s a game-changer for flexible setups!

    • @git-amend
      @git-amend  5 днів тому

      That is a very good tip! Thanks for sharing that!

  • @AnkitWarkade-v2b
    @AnkitWarkade-v2b 3 дні тому

    In my 2 years of game development journey I haven't seen anyone using expression trees as a data structure in a game. Thank you for covering so insightful and advanced topics through your videos😊😊. I hope you will be creating more such videos.

    • @git-amend
      @git-amend  3 дні тому

      You're very welcome! Check out some of the other comments for some examples of how others have been using them and tips from the community!

  • @TopHATTwaffle
    @TopHATTwaffle 5 днів тому +14

    My brain is too smooth to even understand what is happening.

    • @git-amend
      @git-amend  5 днів тому +3

      Try out the first example for yourself, maybe it will help it make sense.

  • @drhoeppner
    @drhoeppner 4 дні тому +4

    Really cool, Its possible to make some really fancy stuff using Expression Trees. But some problems I See when using them like this are that u loose the ability to Cross Reference the properties for example the health of the Hero through you IDE. And you lose compile time Errors If you remove some property which name is hardcoded in some Expression behaviour. This could lead to Errors that are realy hard to find. Using them in combination with some Interfaces could solve these problems but making them less variable in some cases.
    But still very cool Video👍

    • @git-amend
      @git-amend  4 дні тому +1

      Yes, I agree! Thanks for the comment!

  • @selmansavas586
    @selmansavas586 5 днів тому +7

    How you always make video about the thing i need for my project when i need it? Simply amazing content 🥰

    • @git-amend
      @git-amend  5 днів тому +3

      Happy to hear that! Comments like that always make my day!

  • @AnotherGameDev2411
    @AnotherGameDev2411 2 дні тому

    Your video quality keeps improving, and the comprehensiveness and clarity of the subject matter are improving also! I love your channel!

    • @git-amend
      @git-amend  2 дні тому +1

      Thank you, that is very kind to say!

  • @rutchjohnson
    @rutchjohnson 5 днів тому +5

    Ohhh boy! Here we go! Thanks for the weekly vid :)

  • @정동우-n2x
    @정동우-n2x 5 днів тому

    As I always feel, I learn a lot from your videos. Studying while changing your videos to my style helps me improve my skills

    • @git-amend
      @git-amend  5 днів тому +1

      Happy to hear that!

    • @정동우-n2x
      @정동우-n2x 5 днів тому

      ​@@git-amend A personal question, but could you how to cleanly create attack colliders for melee monsters in a 2D game ? In 3D, you can simply attach colliders to the weapon mesh, but in the case of 2D sprites, you have to generate colliders. What would be the best way to do this cleanly?I implemented 2D colliders by reusing them with an Object Pool. Could there be a better approach?

  • @vSwaize
    @vSwaize 5 днів тому +1

    Again another video that just blew my mind. Thankyou so much!

  • @arthur-Ludwig
    @arthur-Ludwig 5 днів тому +2

    Rooting for 20k subs for you ;)
    Nice video XD

    • @git-amend
      @git-amend  5 днів тому +1

      So close! A few more days! Thank you!

  • @silchasruin4487
    @silchasruin4487 5 днів тому

    I see you've updated your assets in your scenes for your examples. This is another banger video, you've got a style of teaching and the examples that prompt alot of ideas! Looking forward to seeing how the channel grows, you're criminally underrated, but I can easily see you passing several milestones soon!

    • @git-amend
      @git-amend  5 днів тому +1

      Thank you so much! I'm glad the examples are inspiring ideas-comments like this keep me motivated. Excited to keep growing and sharing more! 🎉

  • @kugursari
    @kugursari 5 днів тому

    Dude you're awesome! Your content is exactly what this community needs. Thanks a lot for everything and godspeed 🙌🙌

  • @Ayushmaan_Singh77
    @Ayushmaan_Singh77 4 дні тому

    Like always an amazing video, after release of your every video I refactor my practice code and find it even more logical, clean and performant.

  • @youtubechannel548
    @youtubechannel548 5 днів тому +4

    What about the performance? This seems nice and flexible but it uses a fair amout of reflection it seems. If this is adapted everywhere and used frequently during runtime, wouldn't it cause performance issues? I might not understand something, but just curious.

    • @git-amend
      @git-amend  5 днів тому +4

      When you create and compile your delegate, that's the only heavy lifting. Then you can cache it for reuse. Changes to things like a runtime state machine shouldn't be done every frame, just when your game requires it. And try to structure your expressions in such a way that most of the dynamic parts can be passed in as params or a config object to the compiled code.

  • @marlonruvalcaba386
    @marlonruvalcaba386 5 днів тому

    My biggest concern is debuging the code, because it is compiled you cannot use breakpoints.
    One big use of expression trees is parse of information, so you can have the damage formula as a string that be updated by the server, and the client will compile it and resolve it easily.
    Code generation is fun!!!
    Side note, If you want to go full data driven it will be better to use a node editor and send the graph as an asset and run it at runtime. :P

    • @git-amend
      @git-amend  5 днів тому

      I like that use-case! Great comment, thanks for sharing that!

  • @absamurai
    @absamurai 3 дні тому

    1) Does it work for IOS?
    2) What are the benefits over using static methods with reflection (which is definitely happening here too behind the scene), which do the same?

    • @git-amend
      @git-amend  3 дні тому

      IOS is discussed at the end of the video. Yes it uses reflection for some operations but you compile the delegate for reuse so it’s only at compile time.

  • @ranejeb
    @ranejeb 5 днів тому +5

    Are there actual commercial projects that felt the need to adopt such an approach?

    • @git-amend
      @git-amend  5 днів тому +3

      Here are a few examples: The Sims, Spore, Creatures, and the classic game Black and White all rely on dynamic scripting or runtime logic evaluation because the sheer amount of boilerplate required to handle every possibility would be untenable for these types of games. These systems dynamically generate or adapt logic at runtime to handle evolving behaviors or player-driven changes. Expression trees, introduced in C# 3.0, allow developers to dynamically construct, modify, and execute logic at runtime, making them a natural fit for such systems. A similar concept exists in C++ with function objects (functors), though C#’s expression trees provide more introspection and flexibility.

    • @Tymon0000
      @Tymon0000 5 днів тому

      If anyone knows I would also like to know

    • @lucascastro2802
      @lucascastro2802 5 днів тому +2

      I can see this being the backbones of a visual scripting implementation, but for actual game code architecture it feels like overengineering, the GetStat method could use enums, etc etc
      But I can see it being powerful when building a behavior tree or something like that I guess

  • @LuizMoratelli
    @LuizMoratelli День тому

    Do you plan to do something cover editor tools, specially Graph View. Would be cool to do something like a depth scriptable object editor, so you have SO A that has B and C attached, and in 1 screen (a bit like gamemaker display stuff) you see a graph with A pointing to B and C and is able to change value for all 3 at same screen.

    • @git-amend
      @git-amend  День тому +1

      Not a bad idea. I've been thinking about doing a video about how to use the underlying framework of the new behavior package to do that.

    • @LuizMoratelli
      @LuizMoratelli 21 годину тому

      @@git-amend I didn't know we could use it, now I want to see haha

  • @zangwoosung
    @zangwoosung 5 днів тому +1

    now I started to doubt my way of coding , After watching your videos. Thank you.

  • @suntonio7187
    @suntonio7187 5 днів тому

    Kuddos!
    I didnt know that kind of runtime expression even existed!
    I'm not sure about the other examples, but the stats getter looks neat, tho it is string based, and might be error-prone in that regard?
    As always, thanks for the lesson :D i'll have to watch this a couple more time and try myself.
    Much appreciated :D

    • @git-amend
      @git-amend  5 днів тому +1

      Thank you so much! You’re right-string-based approaches can be error-prone, but you can make it safer by combining strongly-typed lambda expressions or tools like source generators. Glad you enjoyed the lesson-have fun experimenting with it! 😄
      e.g. var healthGetter = CreatePropertyGetter(e => e.Health);

    • @herbert9039
      @herbert9039 День тому

      @@git-amend interesting topic and cool video, I look forward to the next one :)
      Nitpick: I think the examples in this video could have demonstrated the power of expressions better, if you changed the strings at runtime via the inspector. Imho all examples of the video would have been better implemented by passing around lambdas directly, instead of creating expressions using hardcoded strings and passing that along. Since "healthGetter = e => e.Health" is just the better option compared to "healthGetter = CreatePropertyGetter("Health")"
      cheers

  • @512Squared
    @512Squared 5 днів тому +1

    It's Sunday. The Git-Amend video has dropped - time to strap in folks....!!

  • @ianjcv
    @ianjcv 4 дні тому

    Dude I just started using these a couple days ago, what the hell. Are you gonna do a video on Lua scripting next too? Might actually be a fun topic. I used MoonSharp

    • @git-amend
      @git-amend  4 дні тому

      Not a bad idea!

    • @ianjcv
      @ianjcv 4 дні тому

      @ There isn't much up-to-date info about it around anymore, and the project itself seems to be abandoned. It still works though, I'm using it in Unity 6 perfectly fine.

    • @git-amend
      @git-amend  День тому

      Interesting. There have been several requests on Discord for that topic.

  • @TheMystogrigen
    @TheMystogrigen 5 днів тому

    Suggestion: Scene State Management. What doors are locked/unlocked, or whatever can change in the environment. Not the player or enemies. The other stuff. I'm having issues finding an elegant way to do so and be able to load without having a bajillion sound effects and animations firing off on load.

    • @git-amend
      @git-amend  5 днів тому

      Maybe in the future! There's a section on Discord where we track these kinds of suggestions if you want to add it there.

  • @lemetamax
    @lemetamax 5 днів тому +3

    Does it always have to be done with strings?

    • @git-amend
      @git-amend  5 днів тому +1

      Excellent question. No, it doesn’t always have to be done with strings. If you have access to strongly-typed members, you can use expression trees directly to reference properties or methods, avoiding the reliance on strings and reducing potential errors.
      public Func CreatePropertyGetter(Expression propertyExpression) {
      return propertyExpression.Compile();
      }
      // Usage
      var getter = CreatePropertyGetter(e => e.Health);
      var health = getter(enemy);

    • @lemetamax
      @lemetamax 5 днів тому

      ​@@git-amend That's pretty neat! Thanks!
      What about performance on low end devices, would you still advise to use expression trees on the average android phone? I ask because I noticed it falls under the linq namespace and linqs are known to have overhead in terms of performance.

    • @git-amend
      @git-amend  5 днів тому +1

      Well, the nice thing about Expression Trees is that you compile them one time and then can reuse. So the cost of LINQ is only when you call the Compile method. After that, executing the compiled function will be very fast. So cache it if possible.

    • @lemetamax
      @lemetamax 5 днів тому

      @@git-amend Alright damn! Sold😅
      I'll be mindful about that. And the iOS thing. Thanks again.

  • @neatodev2249
    @neatodev2249 4 дні тому

    Whenever someone tells you not to use reflection because it's slow, just laugh and call them a noob for not using expressions.

  • @TheArghnono
    @TheArghnono 5 днів тому

    Very interesting approach!
    LogWin really piqued my interest. I bought it, and discovered that it is abandonware and the doc link points to a dead end. Moreover, it doesn't use assemblies, and the central class does not even use namespaces. I am pondering whether it's worth adding namespaces to the source files manually and adding an assembly to use it in my projects. Since you use it: Any advice?

    • @git-amend
      @git-amend  5 днів тому +1

      Interesting, I never even noticed it's last update until you pointed that out. The tool itself is actually quite simple, and should work in any version of Unity. I think you should customize it and make it your own!
      Docs here: julien-foucher.com/unityassetstore/logwin/

    • @git-amend
      @git-amend  5 днів тому +3

      I remember why I bought this actually. Another subscriber recommended another Asset by the same author (//TODO (Code Todo List)) which is also a great little tool. I see from it's release date it was updated more recently (2023). It was cheap so I bought this too, and it turns out I use it more than the other one that was recommended.

  • @tonymax7503
    @tonymax7503 5 днів тому

    Is it correct that expressions need JIT and it won't work with IL2CPP compilation?

    • @git-amend
      @git-amend  4 дні тому

      The problem is with iOS and WebGL. I talk about a tool on the asset store that will solve these issues at the end of the video.

    • @tonymax7503
      @tonymax7503 4 дні тому

      @@git-amend I've found unity forum conversation about AOT restrictions and that from some unity version we have shared generics so it should normally execute Expressions without restrictions in IL2CPP builds. However I also saw mentions about worse optimizations in comparison with JIT when compile expressions.

  • @damonfedorick
    @damonfedorick 5 днів тому

    Nice!

    • @git-amend
      @git-amend  5 днів тому +1

      Thank you! Cheers!

    • @damonfedorick
      @damonfedorick 5 днів тому

      @@git-amend Almost 1 Million total channel views, congrats.

    • @git-amend
      @git-amend  5 днів тому

      Yeah, crazy. Couple more weeks!

  • @forbiddenbox
    @forbiddenbox День тому

    What's the difference between this and reflection?

    • @verdantblast
      @verdantblast 6 годин тому

      reflection, expression tree and emit(DynamicMethod): reflection is slowest, emit is powerful but need some knowledge of IL, expression tree is more restricted but simpler.

    • @forbiddenbox
      @forbiddenbox 2 години тому

      @verdantblast ill look more into it, thank you!

  • @Fitz0fury
    @Fitz0fury 5 днів тому +3

    Something something something. My project. Something Something.

  • @AliHosseiniDev
    @AliHosseiniDev 5 днів тому

    Thanks for the tutorial, but for a multiplayer game where obsfucating the codes is crucial, this type of system that rely on reflection cant be used to prevent cheating, is there any other similar solution ? its a realy usefull system for code flexibility.

    • @git-amend
      @git-amend  5 днів тому +3

      You’re right that reflection-based systems can expose potential vulnerabilities in multiplayer games. A safer alternative is to use code generation at build time, where dynamic behaviors are compiled into static code. Tools like Source Generators in C# or Unity’s ScriptableObjects can provide similar flexibility while keeping the final code obfuscated and secure.
      We will likely have a video about source generators soon.

    • @AliHosseiniDev
      @AliHosseiniDev 5 днів тому

      @@git-amend Thats nice this way we would solve il2cpp probelms as well i guess.

  • @ruslankudriachenko5673
    @ruslankudriachenko5673 5 днів тому +1

    This shouldn't work for AOT platforms, am I missing something?

    • @git-amend
      @git-amend  5 днів тому +1

      At the end of the video I talk about an Asset from the store you can use to overcome that limitation. Link in the description. The Asset is called C# Eval()

    • @ruslankudriachenko5673
      @ruslankudriachenko5673 5 днів тому

      @git-amend thanks!

  • @gustorvo
    @gustorvo 3 дні тому

    Good stuff but I would probably avoid it for 2 reasons:
    1. refactoring
    2. most of devs won't understand this

    • @git-amend
      @git-amend  3 дні тому +1

      These techniques are powerful but not for beginners.

    • @gustorvo
      @gustorvo 2 дні тому

      @git-amend. Definitely! But I would argue that not only for beginners, but neither for mid- and even some seniors either.

    • @gustorvo
      @gustorvo 2 дні тому

      @ btw, what is a percentage of juniors, middle and seniors in a general game dev team?

    • @git-amend
      @git-amend  День тому

      That's a hard question to answer, because it's really project specific and sometimes the roles are beyond just junior/intermediate/senior (such as technical product manager for example, or team lead).

  • @RonaldMcDonald-o5z
    @RonaldMcDonald-o5z 5 днів тому +1

    For the algo!

  • @neilmarkcorre5524
    @neilmarkcorre5524 5 днів тому +1

    Hmm, this is indeed an interesting approach. But, I can only see this being used to make editor tools that might require being dynamic based on designers. But, runtime-wise, I feel like the same benefits can be achieved using interfaces and injection, might actually be more flexible. I might be missing something, and I'd like to be educated if I am 😅 still a fantastic demo. I usually stay away from LINQ but, this looks fun to play with.

    • @git-amend
      @git-amend  5 днів тому

      Thanks for the thoughtful comment! While interfaces and injection work well for static, pre-defined behaviors, expression trees shine when runtime dynamism is required, such as enabling modding systems, scripting engines, or data-driven AI behavior that can adapt without recompiling. They’re also great for cases like runtime property binding or dynamically building logic pipelines (e.g., AI decision-making), where pre-defined structures like interfaces fall short.

  • @goodolboy6994
    @goodolboy6994 5 днів тому

    have you ever tried something like Gameplay Tags for unity?

    • @git-amend
      @git-amend  5 днів тому

      You mean like this Asset? assetstore.unity.com/packages/tools/utilities/game-tags-280149

  • @ИванЛашин-и7и
    @ИванЛашин-и7и 5 днів тому

    Hm, it is interesting method. But the world already created ECS)

    • @git-amend
      @git-amend  5 днів тому

      Well, Expression Trees serve a different purpose than ECS: they excel at dynamically building and executing logic at runtime, which is difficult to achieve with a static ECS architecture. Both have their strengths and can even complement each other in certain use cases.

  • @JaguarPanda
    @JaguarPanda 5 днів тому

    Your tutorials are really good but damn, that's complex

    • @git-amend
      @git-amend  5 днів тому

      Thank you! I try to balance depth with clarity-complexity like this pays off in flexibility and scalability for certain use cases.

  • @あれくす
    @あれくす 5 днів тому

    FIRST

  • @shuoyuanchen7800
    @shuoyuanchen7800 3 дні тому

    I feel like this approach requires tremendous planning on paper

  • @reigota
    @reigota 5 днів тому +2

    Honestly sounds overengineering... way to complex to do something that can be achieved via other strategies :DO Regardless, great tutorial!

    • @git-amend
      @git-amend  5 днів тому +1

      Thank you! While these simple examples might seem overengineered, keep this concept in your back pocket for times when static solutions are not possible or practical.

    • @reigota
      @reigota 5 днів тому

      ​@@git-amend for sure, already cementing in the back of my mind!

    • @Tymon0000
      @Tymon0000 5 днів тому +1

      I think this could be useful when you are making a game about a programmable robot and have a custom in game programming language that would represent the code with expression trees.

    • @reigota
      @reigota 5 днів тому

      @@Tymon0000 I would likely think about integrate a lua interpreter os something else before this solution:D

  • @fahimfaysal1753
    @fahimfaysal1753 5 днів тому +1

    The real win here is logwin. Never knew I needed something so bad until I saw it.

    • @git-amend
      @git-amend  5 днів тому

      Haha, yes. It's a great little tool!

    • @Tymon0000
      @Tymon0000 5 днів тому

      What do you need it for? I am curious.