Jump to content

Rick

Members
  • Posts

    7,936
  • Joined

  • Last visited

Blog Comments posted by Rick

  1. Speaking of consumers and things wanted or needed, I bought FPS weapon/arms pack on Unity Asset Store, import it into Unity, then go to the folder and pull out the fbx and texture files to import into LE4. Kind of sucks that this is the new normal but it's just the way things are these days as the Unity Asset store is the best place to get models.

     

    Anyway, now I was tasked with lining up the gun which is kind of a pain honestly. To get it perfect in game with minimal restarts I added code to the gun script that allows me to change the position/rotation/scale with some keys. Once I visually have it looking good I print out those values and then set them in the script. All this to say that perhaps the ability to set position/rotation/scale of things during run-time could be something to look into. Currently I'm using this idea to line up the hip fire position, the aiming down sights position, and when running I want to tilt the gun downward as well. Without being able to modify these values during run-time it would be a nightmare of change values, restart, rinse and repeat.

  2. On the Marketing side of things, my wife recently became a realtor and what she's finding that works within the social media realm are short daily videos. People often think their videos or posts have to be very interesting but the reality is they don't. People like a "behind the scenes" view of things as long as it's about 30 seconds or less. She's finding that a lot of people just don't want to be in front of a camera or put the time or energy into doing that and so their social media presence fails. In the social media realm text loses to video every time. Nobody wants to just read text on social media, the exception being if it's a meme that has about 1-3 sentences on it but a short video (and people will look at the length before watching or as they are watching) works really well for driving engagement for her. 

    • Like 1
  3. You are starting to get into the idea of behavior trees a little there (which you should totally implement as well into Turbo as they are amazing for AI). I did implement a general behavior tree with Lua. I found a lua implementation and converted it to work with LE. I implemented that into that dinosaur game me, Tim, and Shad did (which I can't find in the games section for some reason).

    https://github.com/tanema/behaviourtree.lua

    If you start reading up on behavior trees you see they have the idea of sequences. So the actual task or doing is the thing you originally were talking about with coroutines, but the structuring of all these tasks is what the behavior tree handles. A nice UI to manage the trees is handy as well. I think I used this https://github.com/behavior3/behavior3editor which exported to json and then the library I modified I created a function that loaded the json result of this editor into the behavior tree structure. It was pretty slick actually.

    Behavior trees have the idea of interrupts that you can put on any branch that would look for things that should take the AI off the current task and rerun the entire behavior tree again. When behavior trees are being ran through they run through your sequences in order so you always put the highest priority sequence first like getting shot at, or starving. Some sort of self preservation. When you're in a lower priority sequence the interrupt would say check your hunger level and if < 10 it would bail out of the current sequence and rerun the tree, which then the highest priority sequence would see you're hunger and then run it's sub sequences like find food, or cook food, whatever.

    You can play around with the editor online: https://www.behaviortrees.com/#/editor

    So if you hover over the Nodes header section on the right a New link comes up and that's where you'd make your new node actions like WalkToPoint or condition like IsHungry.

    It's actually really cool, very powerful, and fun to play with!

    You can even dynamically expand an actors "knowledge" by adding entire behavior trees as sub sequences!  Imagine you creates a behavior tree that just has the sequence of "How to start a fire when cold". Then let's say you have 5 AI around your map. You start them with a basic behavior tree that has sequences like Find Food, Hunt, etc but you give 1 of them the "How to start a fire when cold" sequence. Then you code the game that when 2 actors bump into each other they share sequences in their behavior tree they have so you then give the other actor the "How to start a fire when cold" sequence and now you have AI teaching each other sequences they know. One might be able to expand this to make a more "real" AI that tries to randomly mix and match actions (you'd just have to code a bunch of actions that are available) to create their own sequences and if those sequences help their stats (hunger, thirst, etc) in any way they keep them and if they don't help they remove them! How cool would that be!

     

     

     

     

  4. Yes with coroutines. These things are amazingly powerful.

    So I'd say while this does work for some situations it's not as flexible. Sometimes you want to run multiple coroutine enabled functions at the same time and not continue until both are done. In a cut scene let's say you want to move from point A to B but also kick off some audio (the bad guy is talking as he's moving). He will reach his destination point before his speech is finished. The next step is to move again, but you want his speech and his first movement to be completed before you continue on.

    In my cut scene library I had my coroutine enabled methods return ID's of the coroutine itself, then made a method that would check if that coroutine was completed or not. So I could start both coroutine enabled functions and then do a loop checking if both were in a 'dead' state before continuing.

    local moveId = MoveToPoint(...)
    local speechId = StartAudio(...)
    
    -- don't recall lua syntax for looping atm
    while(IsCompleted(moveId) == false && IsCompleted(speechId) == false)
    loop
    
    -- continue with something else

     

    I would think at a minimum what you could do is make each script function a coroutine, or maybe let us somehow define that a script function should be coroutine enabled. This is what I did in my state manager library. In the scripts Update() I call my state manager Update() function. You change states by just calling stateMgr:ChanageState("StateName"). You added the script 'self' when creating the state manager because when you changed states it would look for functions of that state in that script by concatenating the state name to _Enter(), _Exit(), or _Update() and since you can search for functions by string name in Lua if the StateName_Enter() or StateName_Exit() function existed it would make a coroutine from them. The Update() was a normal function that looped while in that state, but I found myself using _Enter() more because coroutines rock.

    It might be nice to just have state type stuff built into these LE scripts honestly. States usually have an enter, exit, update. You don't need an update as you can get the same behavior by looping in enter and yielding out though. Exit is nice in case there are any cleanup stuff for that state. Wherever you are in that script you can change the state by calling some kind of ChangeState() function. Since you're dealing with coroutines you sort of need that because it tells the underlying system to stop calling that coroutine.

    If you're curious on how this works I have the library uploaded to the workshop. It's FSM (Finite State Machine). I think it's easy to use and gets the point across with states that have their enter/exit functions coroutine enabled.

  5. 9 hours ago, Josh said:

    I can't really think of a way in which user events would be useful. It seems like it just complicates the code for no reason. I am curious to see what you think.

    I think user events are very useful in this kind of subscribe/publish style because it allows for other scripts to know about whatever gameplay event you want without the script needing a reference to another script or entity. Cross script communication via events in my view is the best way to communicate. Allow the EmitEvent() to take a second parameter which is the value that will be passed to the subscribed functions. Since it's lua you only need 1 parameter and it can be a table that the person emitting the event fills out and if you are catching an event you have to just know the structure of said event parameter (a reasonable request).

    This would make using 3rd party scripts that emit events easier I think.

    Let's think about when we create enemy instances on the fly (not via the editor) and we need them to know about certain things the player is doing. How do we do that today? The enemy script either needs to get a hold of an instance of the player or the player get a hold of instances of the enemies so they can communicate. How they do that isn't very efficient or wise because it either means you're storing a global variable of the player so the enemies can get it (globals bad and error prone) or the player script is looping over every enemy to get a reference of them but enemies could be loading all the time so this doesn't work well. With the event system you can screw all that noise and just have the enemy script bind to a certain event that the player will emit. This way neither need a reference to each other to talk. 


    So in summary user events gives us a way to have cross script communication without needing references of scripts/entities.

     

    2 hours ago, gamecreator said:

    I hope that even if events were implemented, we were still left with at least the basic Down function we have now.  It's great to have options but often simpler is better.

    I agree with this. Events work well for cross script communication and UI stuff, but gameplay stuff like picking objects work best with these inline procedural functions. Both are needed.

  6. The way you bind those events, in my opinion, is how the GUI events should work as well.

    self.btnExit:BindEvent(ON_CLICK, self, self.btnExit_OnClick) or self.btnExit.onClick:Bind(self, self.btnExit)

    Normally you have to pass the script itself as well so you can pass it as the first parameter tot he function so we can define it like

    function Script:btnExit_OnClick()

    end

     

    and use self inside of the function to refer to the script itself as 'self' is hidden 1st parameter to the function

  7. When I was playing with this idea I was doing a treadmill system. A 3x3 grid where the player always stays in the middle tile. When they move "left" for example and get close to the edge of that middle tile it would load in the 3 new tiles to the left of the most left 3 tiles and then when they actually crossed the line it would swap the entire grid position wise and then move the player back. Since everything moved relative in 1 go the player would never tell the difference. It would then unload the right most 3 tiles that aren't needed anymore. In my case it wasn't real terrain but modeled terrain and it was all flat. Given LE didn't have multithreaded loading of assets all assets had to be loaded up front so instances could just be created which was more instant.

     

     

  8. Resident Evil 2 Remake all the way. Pretty easy mechanics. Puzzles that would work well with the scripting side of things. Smaller kind of map zones making it easier to extend by people. Doesn't require a ton of enemies at one time. Easy enemy mechanics, they basically just slowly follow you.

  9. On 12/5/2017 at 2:36 PM, martyj said:

    Infinite terrain would be really cool, but how do you add infinite content to infinite terrain?

    It would be wonderful for a sandbox type of game

    User driven content is the answer and it's what more and more games are moving to. Let your users create the content. Just provide them with terrain and veg. The ability for users to build in game has really taken off and it's only getting stronger.

     

    I always thought it was odd how physics is always taking up cycles on objects that can't possibly be hit by anything just by existing. Seems you'd only want a small moving section around something that is moving to check things directly around it. If you gridded out the terrain, a player walking around would only need to check collisions with a 3x3 grid around it with it in the center grid at all times. Dynamically load the 3 grids and unload the other 3 grids as the player moves around. I would think the same ideas of a 3x3 gird could be done for actual terrain chunks. Loading/unloading as they player moves around.

    There is no need for things to happen in unloaded terrain chunks in real-time. When one gets loaded it just may needs to know about some state variables so things inside of it can adjust accordingly. Push a button in 1 grid that should cause x to happen in an unloaded grid? That state variable is set (and stays with the player) and when grid x is loaded whatever entity is on it checks that state variable and puts itself in whatever state it needs to show the effect. So terrain state variables become special and whenever a terrain chunk is loaded all entities on it have a method called which has all state variables available to it so they can do what they need to do based on the value of the state variable.

     

  10. I'm not following why you want to programmatically generate a script to do this specific game requirement. With the idea of connecting functions to 'events' that's just configuration information between generic/reusable user created scripts that is ran when the starting 'event' is triggered.

    If you're thinking about a per-entity flowgraph, all you need is the information of the entity, attached scripts, and the connecting information. After all that is loaded, read a text file that stores the config of what hooks to what and since lua is so flexible that string information in the text file should be able to be setup since everything in lua can be accessed with it's string name.

    So let's imagine entity1 has a health script, sound script, postprocessing script attached to it. The per entity flowgraph could just be a text file config file I would think. When saving you're saving the 'event' variable name and what script it belongs to and the called function and what script that belongs to. All that information can be stored as a string and read back as a string and then you can get all the actual lua objects from their string names. So not following the idea of generating a script to do this functionality.

  11. Because that script has properties like the current noise does. So you need a way to capture those and combine it with the functionality. The noise script can emit events as well like onFinishedPlaying, onHalfWayThrough whatever. A script is just a class to hold all that information, then you make instances of it by attaching it to entities.

    It's not engine commands. We have our own scripts that do a lot of gameplay specific events and functions that we would want to hook up as well. OnScorePoint, OnFoundTreasure, etc etc. Stuff that you can't make common commands for but are useful for our games scripts.

    Now, once you've attached a bunch of scripts to an entity then yes you need a place to connect everything between those scripts and a per entity flowgraph is a good idea on how to do that actually. One could do another script that is specific to that entity and do it all by typing the code if they wanted to do that but a flowgraph for each entity would be a really cool way to do that. If you could save the entity as a prefab and it has all that information (scripts, flowgraph, model, etc) that would be cool too!

  12. You would never have/want health information inside a sound script. That script has stopped being generic enough for reuse at that point and it's now very "leaky". Other responsibility is leaking into that script. If you want 3 instances of that script all playing sound on different events you either bloat the 1 script to handle all use cases (which no reuseable script will know all possible use cases) or you create 3 different scripts. Both are bad options.

    There are all sorts of different events that you want to play a sound and trying to build in common functions or sharing information directly inside another script is just a mess waiting to happen and again kills reuse.

    Being able to link the event to a function in the editor is the way to go. Those other ways you're showing can't really be done via the editor unless you start doing code generation inside the scripts themselves which leads to a whole other mess.

     

    I think your StartThePain()/StopThePain() is the right idea as long as the idea is that I can connect (in another script or via editor) my sound scripts Play() and Stop() function to those functions (events). That way the 2 scripts are unaware of each other on the inside of themselves. The sound script has no knowledge internally of the health script and vice versa. A much cleaner and reusable situation.

     

    • Upvote 1
  13. Just to put a more concrete example with this. Let's say the goal is to have a heartbeat sound play when health is < 25% and to also add pulsing red post process effect around the edges of the screen. A reusable way to do something like this so that others can modify it to their needs would be to:

    1. Create a Noise.lua script. This would have a property of what sound to play and a function Play() (some other properties as well).
    2. Create a HurtPostProcessing.lua script. It will do the pulsating red boarder when you call it's TurnOn() function and you can turn it off by calling it's TurnOff(). It can have properties as well to control certain aspects like how fast the pulsating is.
    3. Create a Health.lua script that has a Hurt() function and an onHealthBelowTwentyFivePercent event. It also has properties like max health and such.

     

    All 3 of these scripts are separate and were created by 3 different people and put in the shop. I come along and add all 3 scripts to my player entity. I set the noise.lua wav file property to some heavy breathing sound and I connect it's Play() function to the Health scripts onHealthBelowTwentyFivePercent. I do the same for the HurtPostProcessing script. I now have 3 scripts that knew nothing about each other working together help me reach my goal effect. This is the benefit of "events" and event programming.

    If we used the polling and shared variable way of doing things this wouldn't be ideal. My reusable Noise script should never know or care about a Health script. Inside it's Update() method it shouldn't be checking if self.health is < 25% because self.health has nothing to do with Noises. It couldn't possible know all the use cases for playing noises. The main benefit of attaching multiple scripts is the reusability and cleanliness of code. Put a nice user interface on top of hooking all this up and you have a very friendly system to make complex functionality without much coding by the user. This gives the coders a framework to create reusable scripts, and down the line a revenue stream by selling those scripts.

  14. 1 minute ago, Josh said:

    You could create a connection with a made-up name, and then call entity:Fire("myevent") but why would you do that when it is just as easy to call the code you want to call?

    You do that for the reason my blog post talks about. When making reusable scripts for others to use, you create events for things that happen inside that script. Users connect whatever they want to that scripts events (I wouldn't know what they want to do at the time of writing my reusable script) and when I fire them inside my reusable script their custom code is called. 

    If a health script wanted to tell the outside world when the health dropped below 50% then inside it's Hurt() function when health is < 50% is would raise or call an event. Since these are functions in  your example it can just call the function onHealthBelowFiftyPercent()  I guess but internally that function would be empty and only exists for others to connect to it so they can call their functions. So in that regard it acts more like an event since it's sole purpose is for others to just connect to it and it's body is empty.

    • Like 2
  15. So :Connect() is an entity function. Can we have our own event? In the example above "Collision" can be thought of as an event. When it's called/raised you call the connected functions. Can we have our own lua defined event that we can call and it'll call all the connected functions? 

    -- treating this more as an event that I can call and it'll in turn raise all the connected functions
    function Script:MyScriptsEvent()
    end
    
    self:Connect("MyScriptsEvent", target, "Foo")

    So when inside self I "raise" or call MyScriptsEvent function it'll call Foo() from target?

    Why do you do the AddArg()? Is that for the C++ side of things? Lua can do variable number of arguments so you could capture all arguments after the 3rd one and pack them off in a table and then unpack them when you make the call and they'll fall in the right order.

     

  16. 4 hours ago, Josh said:

    model:Connect("Update", "EmitEvent(EVENT_GAME_BEGIN,model)")

    Update is the "event". In all the stuff you're saying you are linking functions to "events". Then when that "event" is fired/called it's actually calling the function we assigned to it. Being able to assign multiple functions would be nice. In your example your 2nd param is basically a script in itself as it's getting executed, but before you were assigning things to straight functions to be called and so I was wondering if you could assign to multiple functions and when Update is called it'll loop through and make the call to all functions attached.

    If you changed the name of Update to onUpdate then it becomes clearer that it's basically an event that the engine is raising every frame. There are other events like onCollision. Same idea. You're simply allowing users to have their functions called when these events are raised (when those engine functions are called).

    That's the premise of my blog post. Raising and event is just like calling a function, except the event can have N number of functions that the user assigned to be called when the event was raised. It allows one to make events when they don't know what a user wants to do with said event. It gives them the flexibility to do whatever they want when an event is raised.

    It's easier to think about a GUI button. When it's pressed the UI library "should" raise and event and the user would have attached a function (or more than 1) to that event that gets called. This is opposed to the polling that you use in your GUI system. So you either poll or use events.

×
×
  • Create New...