Jump to content

Rick

Members
  • Posts

    7,936
  • Joined

  • Last visited

Blog Comments posted by Rick

  1. @AggrorJorn Yeah for sure. I was thinking about how when a person is new to game dev or coding this is a hard concept to convince a person to use. It's not straight forward and seems like overkill UNLESS the person feels the pain of the more brute force way of coding themselves. You allude to this in your blog post on how Josh isn't a fan of stuff like this and is a more direct programming person. Direct programming I think is the best way to teach a new engine for sure as it gets to the meat without any kind of framework around it, but once a person gets those basics they generally quickly see how unruly and complex to maintain direct programming can be.

     

    As soon as a persons game starts becoming more complex and they see how fragile their brute force way of coding becomes then they either give up, thinking it's their fault and they just can't do it game dev, or they start searching for a better way to structure their game.

     

    So I feel like really explaining WHY coding this way is so important. Otherwise it can just seem like unnecessary complexity (even though in the long run it's less complex). It's a hard concept to get people to understand why they need it if they've never felt the pain.

    • Upvote 2
  2. Just because I like talking about this I'll give my mindset with component design when I need to add a new feature so others can see the thought process.

     

    In our game the player can get drunk. That's the feature. Now we start breaking out how to add that feature to the game.

     

    First, this instantly becomes it's own component I call PlayerDrunknessComponent. It manages what getting drunk means. I then think about if this was a library on it's own how would I handle it. Well I would have a Drink() function that's for sure. But maybe different drinks affect how fast you get drunk. So I would probably pass an argument to the Drink() function that tells me what the drink is. I can now test this on it's own which is nice.

     

    I then think about what would I like this component to tell the outside world? Well the functionality I want is that we have different drunk levels. Maybe 0-5. Every 2 beers raises the drunk level by 1. So I should probably tell the outside world every time the drunk level changes so I'll have an onchangeDrunkLevel event.

     

    So now I have a way to get drunk with my Drink() function and I can tell the outside world about my drunkness. At this point the PlayerDrunknessComponent is completely self contained and ignorant about the outside world.

     

    Then I start thinking about what will call this Drink() function? Well my inventory component has an onUse event that passes the item that is trying to get used. That seems like a great way to get drunk right? So I hook up the inventories onUse event to my Drink() function. Now I may have to go back and refactor the params since events will send 2 arguments in my case (the sender of the event and 1 args table that holds other data specific to that event).

     

    The interesting thing is how is the onUse event of the inventory getting called? I don't care right now. I just know it's called when an item in the inventory is right clicked because that's what I deemed use to be. So all I care about is that the event exists and gets called and passes the item trying to be used and that meets what I need for this.

     

    Interestingly enough I have a Thirst and Hunger component that hooks into onUse as well. The Thirst component has a Drink() function as well that's hooked into onUse. It checks if the item being clicked was a drink and if so it decreases our thirst and sets one arg to true which will tell the inventory component to delete that item once it comes back from raising it's onUse event. That works out great because when I drink alcohol I want my thirst to be reduced too!

     

    So this is how I get drunk. I right click an item in the inventory and if that item has alcohol (a check inside drunkness component against the item in question) I can get drunk. But just setting a drunk level isn't really doing anything. What shows the player they are drunk?

     

    Well let's randomly rotate the camera to show the head bobbing around. Let's screw around with the controller strafe value so even though the player is moving forward we are adding random strafe values to represent staggering around. Then let's add some shader blurring effect too!

     

    By talking about what we want we already see what components need to be affected. When we made those components we weren't thinking about the drunk feature so we probably didn't put any functions for that. So let's go into each component and add function(s) that will handle this. Again, we can test these out in a unit test if we want. Then we hook the onDrunkLevelChange to all these components functions/actions and the parameter would be what the drunk level is. Each component can read this drunk level and act accordingly by increasing/decreasing the effects.

     

    We also want to sober up over time so our drunkness component will look at that in it's Update() function. As time passes without having alcohol we'll reduce our drunk level and raise the event to tell the other components.

     

    So that is the thought process with adding a feature. Notice the nice thing is that you can add each effect separately to each component over time if you want. Add the staggering to the controller component first. That could pass as drunk for now. At another time add it to the camera for head bobbing. Nice and easy tasks that can be created and checked off with various degrees of what the drunk feature looks like.

     

    That's my thought process with component design. Just wanted to show people what goes on with making a new feature.

    • Upvote 2
  3. The religious experience for me was when I went to add functionality and how easy it was.

     

    1) The class component I added the functionality was JUST that. It wasn't all this other **** that isn't about the functionality I needed to add like when you have the massive player class. This was a huge relief and minimized searching and fear that I'd break something else. Not having to worry about the state of all this other ****.

     

    2) Seeing all my events to actions hooked up in 1 spot showed me how my game worked at a high level. This was an amazing realization. The high level idea of my game didn't really even need comments. You could easily see how it worked with the events assigned to actions. You don't get this when you have a giant player class. You have to go hunting for functions but then they are all over the place and figuring out the high level of what that player class does is actually really difficult.

     

    When you have all the event hooks in one function you can simply go down line by line (no loops/if's/function calls to follow) and just say what you see on that line and that tells you the functionality that exists for that player/game object. I was shocked by that. That was incredible. So easy for someone else to pick up quickly or you when you revisit something 6 months from now.

    • Upvote 1
  4. FYI, each component generally will have an Update() function to it and can be calls in the game objects Update() function.

     

    Like a lot of things in life not everything works perfectly. Generally you don't want to have an event that is raised every frame inside the Update() function of a component, but when you break out the controller and camera you'll find the controller needs the Y rotation value from the camera so the best way to do this without making them aware of each other would be to have the camera fire and event in it's update every frame where it passes as it's argument it's Y rotation value. Then have the controller component subscribe to that event so it can take that Y rotation value and store it off to an internal variable so it can use it inside it's Update() function when you pass it to SetInput(). Ideally events aren't raised every frame but this would be an exception.

     

     

    Also I would say have your GameObject take an LE entity and store that as well. Since your components gets a reference to the GameObject it can then have access to the LE entity. The Controller component would need this since it's calling functions on that entity like SetInput().

    • Upvote 1
  5. The player does not have to be involved in this. Am I on the right track here Rick??

     

    That's correct. The player class, which is a game object, shouldn't care about those events because ideally the player class isn't handling them. It wouldn't be controlling the camera. Instead it would have a camera component inside of it and the camera component is the class that creates the camera and gets the events it needs (mouse input) and fires events that other components may be interested in.

     

    Your player class then becomes the central hub where you add the components it needs and hook up the events between them.

     

    The good news is your comments on functions in your header class help show you the different domains your player class is doing. Those are good hints on how to break out components. Each component should be it's own domain (group of similar logic). So in your case:

     

    I don't know what Toolbar is but I can tell it should be it's own component because you have a fair amount of functions supporting it. Health should be it's own component. Carrying it's own component, pick and collision it's own component as well.

     

    You are in the exact same boat I was in, and I'm sure a lot of people are in, and I'm telling you when you refactor to use the component design it's like a lightbulb goes off and it becomes fun to make changes/additions because it becomes so easy.

     

    Step 1 would be split your domains into their own components. Just create a component class for that specific domain. Now when you're staring at this empty class (PlayerCameraComponent for example), just think of it as it's own library that doesn't know anything else about the other components in your game. It's job is to deal with the camera period. Anything that has to do with the camera happens here. This should create the LE camera entity and then act on that camera entity. What functions does it need to do that? What events should it fire off so outside components can use that.

     

    Actions generally are easier to think about at this time. You generally think about another components events when inside a different component and you find yourself saying "I need to know when the camera is in this state". That's your trigger that tells you the event the camera needs. You can right then and there go to your camera class and add the event and fire it when it needs to be fire. Done! It's that simple. You worry about hooking it up later then. It's so nice to be able to do that because it's kind of how we think.

     

    Now, the question is how will you do messaging. With events you'd have an event class (I made one a long time ago in C++, I think you mentioned at one point you've used it before) or you do what unity does and you send messages from within a component to the game object.

     

    If you go the route of events (I like this way as it's less wasteful and mapping becomes centralized which helps see at a high level what's doing on):

     

    // notice how you don't event have a player class in this case. you don't need it as a game object is just a collection of components and the components do ALL the work so no need for a player type class.
    GameObject* player = new GameObject();
    
    CameraComponent* camera = new CameraComponent(player);
    player->AddComponent(camera);
    
    InputComponent* input = new InputComponent(player);
    player->AddComponent(new InputComponent());
    
    // hook up the event
    input->onmousemove.Subscribe(camera, CameraComponent::RotateCamera);
    

     

     

     

     

    If you go the route of SendMessage();

     

    class Component{
      GameObject* owner;
      public Component(GameObject* o) : owner(o){}
    };
    
    class CameraComponent: Component{
      public CameraComponent(GameObject* o) : Component(o){}
    }
    
    class GameObject{
      list<Component*> components;
    
      void SendMessage(msg, args){
         foreach(auto c in components){
            c->ReceiveMessage(msg, args);
         }
      }
    }
    

     

    It's been awhile since I've done C++ so I probably screwed syntax up but you get the idea.

     

    While the send message approach seems simpler I don't like it. In my view components shouldn't be left to map messages to functionality. For one it takes all that mapping and segments it in different places. Making it hard to see a 10,000 foot level of the interactions that make up that game object. Having it centralized really does help see how your game object behaves at a high level which is handy. I also look at components as separate libraries and it's cleaner if they just raise events and have functions that work on it's internal state. Event args is how you can share details about it's internal state. If you need to know the mouse position inside the camera's Rotate function that is hooked to the input's mouse move event, then the argument (handle this like .NET does where your functions take specific argument classes) can pass that data. So we know that the function inside the camera class should have a parameter of MouseArgs class. The camera class doesn't care how it got that information though right. It just knows it got it and it uses it.

     

    The good news is you have all the functionality already. You just need to get it organized better and component design helps with that. So refactoring this stuff is fairly simple.

     

    Avoid this trap: I noticed I had an issue with naming my component functions very specific the events I knew they would be hooked up to. Try to avoid doing that. Component functions (actions) should be named for what they do internally to that component. The events are named to what they do. So don't name the camera function MouseMove(). That doesn't make any sense when you think about the camera functionality itself. What's really happening is that the camera is rotating. We know that we're going to do that rotation with the input component but put that out of your head because really anything could call that function and pass in the parameters it needs. Anything!

     

    Why is that important? Well, the side benefit of doing all of this is your components are now easier to test. You can do unit tests if you want. In your tests you make an instance of that 1 component, and start calling it's functions passing in whatever data you want to see if you get the right result (examine it's internal state or did an event get called when it should have). That in itself is huge and opens up an entirely new door which will make your code way more stable and make changes way more trustworthy that you didn't screw something up. Generally you'll build up tests and after a change you run them all at the same time and if they all pass you have confidence that you didn't screw anything up.

    • Upvote 1
  6. You have a component that can act upon its own data by methods called from the outside world. The component can also send events to the outside world (the user of the component). How is this done?

     

    Generally by events or messages being broadcasted to all other components and the components who care about those messages do whatever they need to do with them to change their internal state and/or raise events/messages.

     

    To be clear the player is a game object not a component. The game object has a collection of components and it really only acts as a place to configure how the components interact.

     

    Also component design really shines when things get complicated and you have a lot of code. So with your small example we have to imagine a lot since there isn't a lot of functionality but we can do that.

     

    So the question I ask about your example is what is the player class doing with mouse input? Why does it need to know that in the intimate way that it does (inheritance, even though you're using it as an interface it's still inheritance)? Your comments in your mouse functions seem to suggest the player class itself will act on the mouse input. What is it going to do with it? Is the reason you didn't show me your actual player class because it's doing a lot of stuff in it and too big and would be confusing for someone to follow? If that is the case, and you don't mind, I'd love to see it to perhaps explain why component design could help it.

    • Upvote 1
  7. The problem with "Game Maker" type products is lack of scalability and lack of flexibility.

     

    That doesn't matter in business terms. There are always younger people wanting to make these games and these makers are the entry point for them and that's perfectly fine. People will outgrow them the more they dig into game development but some people are simply fine not growing and simply working within the bounds of the game maker. However, if they did want to grow then BAM, this game maker is built from Leadwerks engine which is more flexible at a cost of complexity, but now they have graduated to want that challenge.

     

    Just throwing out different thoughts here. Not many people start their game making journey by just buying a generic engine. I'm willing to bet a lot start from modding or game makers as they are less intimidating. Breaking the intimidation factor is harder than providing another option at the start of the game dev chain.

     

    Saying the comment you said above is like saying a honda civic isn't worth making because it can't grow and scale with your life. Well the honda civic meets a need and so does an huge SUV. There are markets for both. Maybe it works the same with engines. One size just doesn't fit all.

  8. I want to reach people who just have an idea and teach them to turn that idea into reality as easily as possible.

     

    This is a good mission statement. My question would be are you adding constraints to do this statement in that it should be done within the bounds of what Leadwerks Engine is today? Adding game templates to LE I think is trying to achieve that mission statement in the bounds of what LE is. Can a generic game engine ever really achieve that mission statement? That's the debate I guess.

  9. These people did not know anything about mipmaps, debuggers, texture coordinates, and they did not care. They just wanted to make games.

     

    I'm drawn to this statement. Do game templates really address the above statement is the first question I asked? What I hear with the above statement is how can you take all that game dev related stuff and encapsulate it away so the dev doesn't have to know about it?

     

    All the dev really cares about is defining the gameplay logic in an easy way. They expect a list of common camera controls to just pick from vs defining their own. It's hard to blame them on this as basically every camera style has been done already. I'm sure they look at that and wonder why they have to care about the camera code.

     

    I wonder if templates don't go far enough. You have the core engine, but what if you made separate game engines for each type build from Leadwerks and sold them separately on Steam? I mean RPG maker is basically that. You could make your own 3D RPG maker using Leadwerks but sell it as it's own app as it would be highly specific to RPG's with dialogs and a lot of UI specific things. Then do the same with RTS, FPS, etc. All using Leadwerks under the hood but each being their own application really. It takes the templates idea further providing new revenue streams while all still using Leadwerks under the hood. RPG makers, FPS makers sell and I think they would be valid separate apps as they would all have very specific beginner friendly dialogs to build that type of game.

     

    There is no doubt that you making those would also help with ideas on what the core engine needs in it as well so the core engine wouldn't suffer from not being updated. The question is, does that spread you too thin? Perhaps you can employ people for each game maker type?

     

    Just ideas on how Leadwerks can reach those beginners. Getting more specific in the application has been a trend these days. It would sort of be like that one Leadwerks user who made that RTS engine using Leadwerks at the core.

    • Upvote 1
  10. You do? Why is it when I have mutilple ai go to the player they all lump up and slide all over each other? They seems more like they are all trying to move to the same spot and they are colliding with the character controller and because it round they slide off each other.

  11. That won't do you any good because you'll have to avoid collision with other AI, and a plotted path won't do that.

     

    I think it gives us more control and options specific to our games. We can check for our own BB collision once every 200ms and adjust accordingly. We can have flocking logic to make sure they don't collide with each other but are all following one general path. This is generally what's games do anyway. They don't allow character controllers to collide and slide over each other like what happens in LE when 2 or more collide. That's not a case in my view to not give us the path data.

     

    There is a common theme of some ppl asking for access to data the engine already has. These topics are for the ones who push the limits. It's data that already exists. I'm curious on the constant push back on said requests for data? You provide the data and one system that works with data. If that system doesn't meet the needs the user is responsible to make it work. This way you have something for the beginner and something for the advanced. Win win.

     

    Steering is the term for what I'm talking about and I think can be combined with the path and dynamic obstacles in realtime at an interval that works best for the game. https://gamedevelopment.tutsplus.com/tutorials/understanding-steering-behaviors-collision-avoidance--gamedev-7777

     

    Open source liberal for steering. http://opensteer.sourceforge.net

     

    This article is amazing! http://www.valvesoftware.com/publications/2009/ai_systems_of_l4d_mike_booth.pdf

    • Upvote 1
  12. "For example, there's basically no physics in that game and you will never see an enemy kick over a box like ours do."

     

    You're not making a game so that's a strange comment to make. Most games don't require physics on AI characters so the fact that characters in LE can kick over a box doesn't really add much value. The ability to choose would be better. That would start with access to the recast path, specifically from Lua would really help.

  13. You said above 45 fps with 25 zombies is reasonable. I'd argue agasint thst given he gets 120 fps until they start chasing him. I've noticed this slow down when being chased in the past as well. The question is what's causing 120 to drop to 45. I suspect it's the physics. I'll run a small test with just tweening, or Move() even, the models towards the players this evening to see if that helps narrow it down.

     

    Jen mentioned before SetPosition was slow so maybe that causes something behind the scenes?

     

    L4D can have 200+ zombies on screen at once. That should be the bar I would think. Sure poly count will come into play there but something else is also going on.

  14. So a big portion of that is physics then and not the models. I can test when I'm home in 9 hours, but that tells me it's more physics and that's nothing to do with Lua but Newton.

     

    @Josh is Newton running on a separate thread? If not I would think we'd get a big boost from that. Or perhaps we can control physics update interval? Do zombies chasing on relatively flat ground (game specific) require the update rate physics currently runs at?

     

    I'm interested in this because we are working on a game where this matters as well. The interesting thing to ask is does something chasing you really even need physics? If we are able to get the path points that raycast is using then we can use our own move code and bypass physics completely. The physics is what's killing this.

  15. I assume that is because of the physics and not the models. What if you don't make the model a character controller and just a static model sitting there? If you get higher fps then it's the physics which really wouldn't have anything to do with Lua. If it is the physics couldnt couldn't that be threaded to improve performance?

     

    I'd also be curious as to what happens if you remove the UpdateWorld function from their script. That would be more of a Lua thing. We noticed when a lot of objects have the UpdateWorld function it slows things down. There can be alternative ways to achieve behavior without the UpdateWord function in each script. It's not enough to have an empty UpdateWorld, it can't exist so that c++ doesn't call it because the function calling overhead is what slows things down when you have a lot of entities that have an UpdateWorld function and if you're doing a good amount of things in there it hurts even more. You want to bail out of UpdateWorld quickly if you can.

  16. @Brutile NOOOOOOOOOOOOOOOOO!

     

    Aggror you have to figure out how to show the ghost path the first place on each track as we are racing. Would love that playing at the same time I'm racing to see how well I'm doing. The first Mario cart had that and it would be sweet! Push josh to get generic web api calling into le so this can happen!

     

    Or you could just add it yourself and take the game out of the game launcher as it's limiting the potential for gameplay features.

    • Upvote 1
  17. Level 1 ball how the hell did you get 13 seconds! At least I'm in 2nd place now!

     

    [edit2]

    Nevermind I see the shortcut now! I got 14 seconds. I'm coming for your title!

     

    [edit2]

    NOOOOOOO. I had it in 12 but overshot the goal!

     

    [edit3]

    And you're looking at the new champion of the world!!!!!!!!! 12.610

     

    [edit4]

    Got ya in level 3 ball too (barely)! That was was fun!

     

     

    It would be nice to see scores before I try a level.

    • Upvote 1
  18. Ok so you will put the data for quest in the database but instead of reading the database at runtime you're doing these other steps to get the data out at design time and it all gets loaded up on startup? Couldn't you just load all the quests on server startup from a query to the db to get it all in memory right away vs doing these extra steps each time you update a quest in the db?

×
×
  • Create New...