Jump to content

Rick

Members
  • Posts

    7,936
  • Joined

  • Last visited

Everything posted by Rick

  1. Most people are incredibly insecure it would seem. It's sort of detrimental for this industry where you really just have to throw stuff out there to get the experience and learn a ton by doing so and getting feedback.
  2. Should be in AppData\Local\[MyGameNameHere] Where [MyGameNameHere] is your game you are running.
  3. That async loading style seems to be a pain. Ideally I'm guessing there would be one place where the event loop exists, but loading of models may exist in another place. So how do you get the resulting model thst was loaded to the place that called the load async function? C# handles this with an idea of async/await. It's a form of coroutine where you prefix an async function with await and it'll leave that function and continue with the main app loop and enter back in where it left off when that operation is finished. From the programmers point of view you write sequential code that's doing asynchronous stuff. Sadly C++ doesn't have this directly but it is proposed. However all that's really doing is a state machine behind the scenes to make it seem easy. It seems like C++ has some built in libraries that can mimic this behavior in c++11. It's called future library. I haven't used it but it may help get a more streamlined way of loading stuff async without needing some kind of event loop to go through which messes up the program flow and will probably result in bad designs and/or global variables required.
  4. The launcher I tried a couple months ago didn't even work with the new GUI so I stopped using it. Interfaces to publish to those other sites is a good idea. Integration is always good but takes time. Game Launcher should be made with nw.js. Hire a web developer to create it. Should be fairly cheap as web developers are a dime a dozen and give you a much more modern feel to it. You've wasted too much time on GUI stuff for the Game Launcher and it still looks pretty old vs a modern web UI look that a web developer can make that you run with nw.js. I would also see it as a nice intro to nw.js for you to ease yourself into for other tools for the engine that may be handy down the road if you like it and find it easy to work with. It would be nice to settle the GUI library that you make your tools with once and for all.
  5. Collision isn't like an "on enter" type of thing. It's constantly called while the collision is happening which is why I think it's doing what it's doing.
  6. So this results in the below? enum_netchat = { client = 1, team = 2, all = 3, servertoclient = 4, servertoteam = 5, servertoall = 6 }
  7. Unless your game is multiplayer where the users fill it out for you. This is such a double edged sword. Since Le doesn't do this nobody is going to try. With the trend for online survival games in huge procedural worlds where players build structures these kinds of questions are going to keep coming and be more relevant over time.
  8. Since script variables are part of an entity and when you load a new map I'm pretty sure it clears out all entities from the last load, then all those script instances should get deleted which would include your script variables. Every time you load a new map it should be starting fresh with script variables. If you are using global variables anywhere then those wouldn't get deleted.
  9. I'll give a real world example that can help people get in the event action component mindset. Tjheldna and I are making an rpg turned based fighting game. Think old school final fantasy like. So you have your characters and enemy characters. When it's one of your characters turn you select an ability and then select a target to do that ability on. Ultimately a message is sent to that entity to do damage to it. I call that message "do.damage". The target entity gets onReceivedMessage event raised whenever it gets a message. We then hook up components actions we want to do functionality to this entities onReceiveMessage. So in my example case I'm getting a "do.damage" message. So my thought process was I need health in order to take damage so I create a health component. It's a pretty basic component to start with. Simply has a health variable, and an action I named doReduceHealth(args). The arguments table that came along with the "do.damage" message has a 'value' variable with the amount of damage to be done. So inside doReduceHealth() action I reduce my health variable by args.value. I then add the health component to my character entity and hook up its doReduceHealth() action to the entities onReceiveMessage event and part of that subscription I have a filter function that returns true when the args.Message == "do.damage" so that when onReceiveMessage is raised it'll only call my health components doReduceHealth() action when the message we received is "do.damage". Now my health is being reduced! So we were rocking that for some time and all going well. The next part shows the lego like idea of this system. It's very much like snapping legos together ifeach lego was a piece of functionality. Because this is an rpg we need stats like armor, strength, etc. In our specific case one enemy has an aura that reduces all damage by 50%. So another stat would be damage reduction. So I know I need these stats thst kind of all play together in some way. So I create a Stat component. Inside I create a variable named damgeReduction which holds a percent value. The point of this component at this point in time is to reduce the damage trying to be done to me. So I create an action named doReduceDamage(args). One good side effect to our system is that actions are called in the order they were hooked up to the event. So clearly I need to reduce the damage coming into use from the "do.damage" event our entity gets before the health component does the damage to our health. So before the health event hookup of the "do.damage" message event I hook up this stats doReduceDamage() action. The other benefit to this system is that arguments to actions is a table and tables are passed by reference. Which means if I modify a variable value that is part of thst table in one component action, the other component actions thst get called after that will see those changes. Perfect! Since our stats doReduceDamage action is called first I'll reduce the args.value number by whatever our stats damageReduction variable is. Now when the health components doReduceHealth action is called the args.value variable is lower. I just easily and in an isolated way snapped on functionality. The last day or so I've made posts trying to figure out how to draw to a texture and then put that texture on a sprite. The reason I'm doing this is because I need to show the player how much damage was done. I needed the value of the damage being done to show up on screen above the characters head and then rise up a little over time. So guess what? New component! I created a RiseText component. It has an action named doRise(args). I hooked this up to the entities onReceiveMessage event and raise it when it gets the "do.damage" message but it's hooked up AFTER the stats actions so that args.value is already modified and the true value that is being done to that entity's health component. This is where the benefit of coroutines come in. doRise() action of this component does everything right inside itself. It creates the needed variables for buffer, material, etc. draws the text and then loops and tweens the text upward while yielding out. It's nice and more natural to be able to do that all in that one actionvs having to set state variables and do it in that components update() function. An added benefit to why it's nice is because when an action is raised a unique coroutine is created that wraps it. This means that even if the same action is called multiple times each one is ran in its own contained state. So one instance of that action might not be finished while another is called again but because they are wrapped in thier own unique coroutine they don't effect each other. If we didn't have this then calling the doRise() while another rising text was still happening in its update() function would cause thst one to stop so it can do the new request of rising text. If we didn't want that then we'd have to get into possibly storing multiple instances of possible rising text ourself. What a pain. With actions being wrapped into unique coroutine objects we don't have to worry and we can code more naturally. So I hope this illustrates the mindset of this system. Snapping smaller fairly isolated pieces of functionality together to get complex functionality. Maintaining each component is extremely easy since thier code is usually fairly small and it's contained in its own "class" and file. Removing its effects is simply commenting out its event hookups which are usually just a couple of lines of code. The only "coupling" components have is through the arguments. You need to know what variable names are part of the args table from within a components action when you hook up to an event. It's as loose of coupling you can get. It doesn't have anything to directly do with components structure themselves. I remember when josh first started this version of Leadwerks where he allowed multiple scripts to be attached. He found himself querying the other attached scripts to see if a certain script existed and then directly calling its functions. He didn't like that and he was right. That sort of defeats the purpose of components being isolated and creates a spaghetti mess of code. People in Unity still do this and I think it's a mess, and bug prone issue. Once you start working with hooking up actions to events things just become so much easier to work with. Learning what size scope to make your components (not too big not too small in scope) takes a little practice and feel but once you get the feeling down you are off and rolling.
  10. Rick

    Scale a sprite

    Ah it's SetSize() vs SetScale()!. Thanks.
  11. Scaling a sprite doesn't seem to actually scale the square sprite you see in the editor. Is there no way to scale a sprite?
  12. That worked thank you much. Any ideas on how to fade out the text? I can change the alpha of SetColor() but currently I clear the buffer but that results in a white buffer it seems as the sprite goes all white when I call buffer:Clear(). It's like I need to clear it with no color. Currently I call Clear() then SetColor() where the alpha is going down from 1 to 0 and redrawing the text. However, as stated all I see is white.
  13. So that kind of works. I see only the text but even though I changed color to red the text is black and can't really see it because of that.
  14. Rephrasing the question: How can I make the texture background transparent so the only thing I see is my text? local shader = Shader:Load("Shaders/model/diffuse.shader") local currentBuff = Buffer:GetCurrent() local mat = Material:Create() mat:SetShader(shader) local sprite = Sprite:Create() sprite:SetViewMode(0) sprite:SetMaterial(mat) local buffer = Buffer:Create(20, 20, 1, 1) -- draw the text on the buffer Buffer:SetCurrent(buffer) self.context:SetBlendMode(Blend.Alpha) self.context:SetColor(Vec4(1, 0, 0, 1)) self.context:DrawText("Hello", 0, 0) local tex = buffer:GetColorTexture(0) mat:SetTexture(tex, 0) Buffer:SetCurrent(currentBuff)
  15. Download the steam client app. Log in with your id. You'll have a list of all games/apps including Leadwerks.
  16. We have a long term goal of allowing, from the editor, pulling down and publishing from/to github components that people create for this system. GitHub has a web API and if people use a certain tag on thier components on there we can easily query those and display them in a list to pick from. Sort of like a package manager. However, getting multiple people to use the system will help better define it before we do that. At that point we would want to have everything solid for a version 1 release. We think that it's really easy to create actions and events and then visually hook those up together to make more complex functionality.
  17. The zone really does have a special place in LE history so it should really be made an official mini game with functionality. Maybe FPS with some mutant dogs that hunt you down with guns, ammo, and med packs scattered around. The dogs can just randomly spawn every once in awhile near the player.
  18. In my other blog (https://www.leadwerks.com/community/blogs/entry/1908-top-10-celebrities-who-use-component-architecture/) I talked about the component architecture I was using. I've since teamed up with Roland on this idea and together we have fleshed it out more. It's not 100% ready to be released yet but I realized that we've come a long way and that where we are today might seem confusing to someone if they weren't along for the ride. So I thought I'd use this blog to start down the journey we went on. The main goal for our journey was as follows: - Components will NEVER depend on other components. Decoupling as much as possible was priority #1. So the idea of events and actions(functions) is still at the core of the system. Now components need to work together obviously but the lowest level of dependencies the system has is via the arguments that events send along to actions. If you're hooking an action to an event you need to know what args that event is sending you. This is primitive data vs classes and it's as decoupled as you can get while still allowing interactions between components. Coroutines! One day Josh asked about coroutines and if anyone knew anything about them. I've used coroutines in the past so I replied and did some examples (still need to finish that for him). Then one day on my way home from work it hit me. Since we have a common communication method in having actions (functions) being called from events we had a centralized place where all component actions (functions) were being called (the event class). This meant incorporating coroutines into our system was simple. Thanks to Roland for fleshing the idea out, all component actions are now coroutines automatically created (no work on the component creator's part). This was very exciting as it meant action functionality that required sequential coding could be done right in the action directly. This helped eliminate the need for most components needing an update() method that might not be doing anything except in certain situations. Situations that are now all handled in an action itself. It also meant a lot less state variables were needed to manage this sequential code. Roland had a doAddHealth() action where he instantly added the value passed in to the players health. This resulted in a snapping of the UI to the new health value. While that clearly works and you can do it that way, the test of our system was to make that health slowly increase to the final value over time, giving it a nice visual animation of increasing health. We were able to do it directly in that doAddHealth() function with about 2-3 more lines of code. It was insanely easy and much more logical in nature. Roland had never used coroutines before and he was able to get it working in a couple mins because it's just very logical and intuitive to work with when you don't have to deal with the details of the coroutine setup and management. You simple work with coroutine.yield() inside your action and that's all you really need to understand. This is an idea I'd like to see Josh think about with normal LE script functions as I think it can make life easier. Tim and I are working on a turn based game. Everything in the game is basically a sequence of actions over time so this idea of all actions are coroutines has been HUGE for our game. More on that at the end of Aug. Entity to Entity Communication! We fleshed out the entity to entity communication. My last blog talked about component communication with other components inside the same entity (the event system). This works great because all the components are inside the same entity and can access each others events and actions to hook up. But how can a component in entity A do something because a component in entity B wants it to, without those components knowing anything about each other? It was a challenge to get the right idea for this. We wanted to stay with the idea of events for this but we also didn't want entities to know about each other's internals.Decoupling of entities is important or else you end up with dependency hell and tigh coupling. We ended up with giving entities a SendMessage() function and an onReceiveMessage event. So from entity B we can hook up one of it's components events to the entity action SendMessage. The arguments for these required 2 special variables. Dest and Message. Dest is the entity to send the Message to. To get the Dest entity you're component is doing some kind of picking or GetNeighbors(). Dest can be 1 entity or a table of entities. On the receiving entity(s) the onReceiveMessage event is raised so that you can hook it's component actions to a received message. So all communication is done via the event system in some way. This introduced 2 needed features to our event system. When you get an event onReceiveMessage is raised no matter what event you got. However you'd only want certain events to trigger certain component actions. This requires some kind of routing of string messages to actions being called. We did this currently with a filter function on the event's subscribe() method. When the event is raised and a filter function exists it'll call the function passing in the arguments of the event and if the function returns true, raise call the action method. If false it wont call the action method. So generally what you do is pass a function that checks the args.Message for the value you want to call the action. self.onReceiveMessage:subscribe(self.healthComponent, self.healthComponent.doHurt, function(args) if args.Message == "hurt" then return true end return false end) In the above event hookup when the Message is "hurt" then we hook it up to our healthComponent doHurt action. Because this is a very common thing to do, it can be bloated to have to define the function that does exactly this but for different string messages, you can just pass a string instead of a function to make it more streamlined: self.onReceiveMessage:subscribe(self.healthComponent, self.healthComponent.doHurt, "hurt") As you can see communication is NOT done inside the component. We didn't want components handling communication. We view component functionality and how that functionalty is communicated as 2 different types of coding. Communication is done inside the event hookups and functionality is done inside the component. Because of this and staying with the SendMessage/onReceiveMessage idea, we introduced another idea to the event subscribe() function. Another callback that is called before the action is fired. This also passes in the args and exists to let you modify the args before the action is called. This is used mostly when hooking a component event to SendMessage so that at that point you can give the string Message value. This way the component itself isn't concerning itself with the message which helps keep it more generic. This makes communication implementation specific to YOUR game and not the component. The component is just doing it's ONE job and raising events. That's it. What that means to your game is up to you to code. An example of this is: -- nil is the routing/filter function we talked about above which we don't need because we are sending out not receiving in self.inputComponent.onPicked:subscribe(self, self.SendMessage, nil, function(args) args.Message = "use" end) The inputComponent will raise an onPicked event when an entity is picked by left clicking. It doesn't care what you want to do with that. That's your part of coding your game and is game specific. It will fill in the args.Dest value with the entity but we need a place outside the component to specify what we want our message to be for our game. The supplied function does just that. It let's us create the Message variable on the args and fill it in. On the receiving side then it's up to us to hook up to that entities components when the message is "use" like above in the onReceiveMessage examples. This idea of 2 types of coding I think really helps create more separation and isolation of code which helps with maintainability and reusability. If components were to define the Message value inside then their influence starts to leak out as another component needs to be programmed to deal with that exact Message. We don't want that. We want the messages to be game specific and decided on by the user of the component system not the component creators. There is an alternative syntax to the above code where instead of a function you can specify a table. This table will be merged into the args parameter. self.inputComponent.onPicked:subscribe(self, self.SendMessage, nil, { Message = "use" }) So to summarize entity communication, when sending messages the arguments callback function (or table that gets merged) is useful. When receiving messages the filter/routing callback function (or string message) is useful. Cool Side Effects! And interesting side effect to to the event system is that they are raised in the order they were subscribed to. You can use that to your advantage if you want to modify the args in any way between multiple components. Tim and I use this concept in our game. When we get a "hurt" message come into a character entity we first pass it through a Stats component which stores information about the player armor and other defenses. The args has a value property on it that is how much damage we should take, but by first running through our Stats component we can reduce that value by our armor. The 2nd component it's hooked up to is the health component which will reduce our health by the value property but now it's less because our Stats component reduced it. Since args is a table and tables are passed by reference the change to an args property in one component is visible to subsequent components. Summary Having a common communication protocol between components and entities has been a big help in structuring my code for maintainability, adding new features, and reusability. One of the benefits of Lua is that table properties can be accessed via their string name. So something you might notice about event hookups to actions given knowing that Lua table properties can be accessed via string names, is that hooking up events is really just configuration. Even though the above examples is code that code can be made very generic where the property names are string values stored in a file. For example the above inputComponent hookup is the same as: self["inputComponent"]["onPicked"]:subscribe(self, self["SendMessage"], nil, { Message = "use" }) Because it's a common structure all event hookups follow the same pattern. So imagine this information is coming from a json file and that json file is built from a visual editor. You would code your components in a code editor but hookup your components in a visual editor as you select the event source, event, action source and action from drop down menus. Thanks to Roland, we will have more to come on that in the next few months...
  19. Do you really need physics for your floating object? That's a big question because there are pros and cons if you do or don't.
  20. From the editor when I click Workshop->Browse Workshop it opens and I get a message saying it's being upgraded so it would seem people can't buy from this custom interface?
  21. Yep same issue with us. Prefabs enter the map with correct casing but reopening a map their names get changed. Pretty annoying bug. Entity names should never change like this.
  22. On top of this we see that the instance naming of prefabs changes if you open a new map and then go back into the original map. It looks like it changes to camel casing. This currently causes us issue as I'm expecting certain spelling of things.
  23. Sorry, I should have said when do you think it'll be pushed to release branch?
  24. How long do you think this will be until it's in? In the next 6 months I think I'll have a need for this.
×
×
  • Create New...