bansama Posted January 31, 2014 Share Posted January 31, 2014 I picked up Leadwerks on Steam earlier this week. As such I have very little experience with it. I also currently have no experience with Lua. So forgive me if this question is a little silly. As the title suggests, I would like to pass variables between scripts. For example, I want one script tied to an entity/object in the scene to manage what happens when there is a player collision with it. I want another script to manage text output to the screen based on such collisions. How would I pass a variable set in the collision script to the text output one? Could someone please point me in the right direction? Quote Link to comment Share on other sites More sharing options...
Mordred Posted January 31, 2014 Share Posted January 31, 2014 It's basically the same i did earlier today. You have to set a "target" in your colission script, once you did that you can access functions within the target script using for e.g. self.target.script:<function>() Here's the code i did so far, it passes a damage value (named xDamage) from this code, to the "FPSPlayer" script (Atually: Its more to the "entity carrying the FPSPlayer script" than to the script itself) using its builtin "Hurt()" function. You just have to make sure that the "self.target" somehow point's to your Character, i did this using the "GetEntityNeighbors" function like done in the "EnemyAI" Script. -- give access to code in "GetEntityNeighbors.lua" import "Scripts/Functions/GetEntityNeighbors.lua" Script.target = nil --entity "Target" Script.damage = 0 -- float "Damage" Script.teamid = 2 --choice "Team" "Neutral,Good,Bad" function Script:Use() -- load entities within range (30) of self.entity into "entities", only load entities that have scripts attached local entities = GetEntityNeighbors(self.entity,30,true) local k,entity -- loop thrrough the result of "entities". k = key, entity = result for k,entity in pairs(entities) do -- only select entities with teamid == 1 ("good") if entity.script.teamid == 1 then -- and if the entity has at least 1 health remaining if entity.script.health>0 then local d = self.entity:GetDistance(entity) -- set self.target = resulting entity self.target = entity end end end -- prepare random function math.randomseed(os.time()) if self.target ~= nil then -- randomize the damage dealt a bit, setting the "min damage" value if self.damage > 0 then if self.damage > 5 then minDmg = self.damage - 4 else if self.damage > 2 and self.damage <= 5 then minDmg = self.damage - 2 else minDmg = self.damage end end -- actual code randomizing the damage. Damage is dealt between "minDmg" and "self.damage" (the value is set in the assets browser) xDamage = math.floor(Math:Random(minDmg, self.damage + 1)) -- fire function "Hurt" at self.target --> thus the player or entite that was found before. self.target.script:Hurt(xDamage, self.target) end end end My bad, didnt say that.... forgive me welcome to Leadwerks bansama Quote Link to comment Share on other sites More sharing options...
Rick Posted January 31, 2014 Share Posted January 31, 2014 One way is to use flowgraphs. Make a new script file called something like TextCollision and copy the code from here: http://leadwerks.wikidot.com/wiki:collision-enter-exit Add a script level parameter called Text (Script.Text = "" --string). This makes it so you can reuse this script many times and display different text on each instance. Attach this to some csg brush and change it's physics to Trigger (and probably paint the invisible material on it). Create a function like: function Script:GetText()--arg return self.Text -- this is whatever you named your script level variable to store the text end Then place your pivot in your scene and make a script that does the drawing of the text to the screen (the thing is you could just have this collision script do the drawing if you wanted but if you don't for some reason this is another way). This script should have a function for setting the text that it should display defined like: function Script:SetText(text)--in self.text = text end function Script:PostRender(context) if self.text ~= "" then context:DrawText(fill this in) end end Open the Flowgraph from the menu bar. Drag in your csg brush and your pivot. Drag a line from the CollisionEnter output to the SetText input. You'll see a little dot in that line. That shows up because SetText() takes a parameter and this dot represents this parameter. Drag the GetText() (which had the --arg after it in the function def) to this dot in the middle. This is one way to pass variables around between objects when outputs raise inputs. Quote Link to comment Share on other sites More sharing options...
bansama Posted January 31, 2014 Author Share Posted January 31, 2014 This is one way to pass variables around between objects when outputs raise inputs. Thanks for the reply. I guess I don't fully understand it though as I can't get it to work (yet). I copied the text in the script you linked to, and added Script.Text = "" (I've tried this alternately with both text and Text in case the difference matters as I notice you have alternated between the two). In the same file I added the Script:GetText() example you provided and created the SetText counterpart in the display script. In the flow editor GetText() is shown in <>, but SetText() is not. Does this indicate a problem? I added a little if/elseif loop to the function I'm using to output text to check if self.text is empty, which it is. So I think I'm still not passing the variables correctly. I'm not even sure how I should be calling the functions. When I try to set a variable with text = SetText(text), I get an error about attempting to call global "SetText" (nil value). So I guess my PHP knowledge is not going to help me here. The reason I want to do this instead of just outputting the text with the collision script is that I figure the need to pass variables will be important, and outputting to the screen is a good way for me to test if something I code works or not. Quote Link to comment Share on other sites More sharing options...
Rick Posted January 31, 2014 Share Posted January 31, 2014 CollisionScript (attach to some csg) Script.entered = false Script.exited = false Script.hadCollision = false Script.Text = "" --string function Script:GetText()--arg return self.Text end function Script:UpdatePhysics() if self.entered then if self.hadCollision == false then if self.exited == false then self.exited = true self.component:CallOutputs("OnExit") self.entered = false end end end self.hadCollision = false end function Script:Collision(entity, position, normal, speed) self.hadCollision = true self.component:CallOutputs("OnCollide") if self.entered == false then self.component:CallOutputs("OnEnter") self.entered = true self.exited = false end end DrawTextScript (attach to a pivot) Script.Text = "" --string function Script:SetText(text)--in self.Text = text end function Script:PostRender(context) context:DrawText(self.Text, 0, 0) end Bring both pivot and csg into the FLowgraph and you should see the outputs/inputs/args. Drag CollisionEnter to SetText, then drag GetText to the little dot on the line between CollisionEnter and SetText This was done off memory but it should work. Quote Link to comment Share on other sites More sharing options...
shadmar Posted January 31, 2014 Share Posted January 31, 2014 You can also in App.lua just make a table for "global" storage which is usable by all other scripts. Inn App.lua Start() self.data = {} Then in object scripts you can read and write to this table, and just use the the entity as index. -- write some data about the local entity. App.data[self.entity] = somedata -- get some data on another entity local value = App.data[entity] Quote HP Omen - 16GB - i7 - Nvidia GTX 1060 6GB Link to comment Share on other sites More sharing options...
bansama Posted January 31, 2014 Author Share Posted January 31, 2014 This was done off memory but it should work. This does indeed work. Which means I probably made an error. Based on this, I'll look tomorrow at the first two scripts I tried to use and see if I can figure out why they didn't work. Thank you. @shadmar Thanks. I'll look at that too. @Mordred That was a little over my head, but thanks. Quote Link to comment Share on other sites More sharing options...
Rick Posted January 31, 2014 Share Posted January 31, 2014 While I think Shadmar is great and the things he provides this community are amazing, I would warn anyone, especially people maybe newer to programming, to try and avoid global variables. They will eventually make your program a nightmare to maintain. The code will eventually become a spaghetti mess of dependencies that almost nobody but yourself will be able to follow, and after a couple months even you won't be able to follow it. They are convenient, but deadly. The tight coupling that they can also introduce sucks. You can make an entire game without global variables which will yield many benefits. A google search as to why globals are bad will explain these. 1 Quote Link to comment Share on other sites More sharing options...
gamecreator Posted January 31, 2014 Share Posted January 31, 2014 This is why I compromise and use global classes. It's almost kind of organized. Quote Link to comment Share on other sites More sharing options...
Rick Posted February 1, 2014 Share Posted February 1, 2014 I joined a team once to make a game from GameDev. I can't even tell you how horrible of a nightmare their code was. Everything, I honestly mean that, was global. One giant switch statement to control the entire game. They would complain about some other developer who would say they need to fix this, but the reality is they were very new to programming, very determined, but very new, and globals were the easy route to take because you didn't have to think about designing how things would interact, but that's really what is meant by a good design. That's 1/2 the challenge to making good maintainable code. I would challenge anyone who uses globals on a regular basis to think about how they would remove those globals. Once you do that over and over again, then you won't go back. Quote Link to comment Share on other sites More sharing options...
bansama Posted February 1, 2014 Author Share Posted February 1, 2014 While I understand that globals are bad (it's the same with PHP and I expect all languages), I'm still left wondering how I can pass variables actually set within a script to another script or function. Say I wanted to pass the remaining amount of ammo, or wanted to define a longer string (for a journal entry or similar) can that still be done with the flow editor? Is there a way to do it without the flow editor? In other words, how can pass information I can't realistically set with the defined input box that appears in Leadwerks when --string, etc. is used? Quote Link to comment Share on other sites More sharing options...
Rick Posted February 1, 2014 Share Posted February 1, 2014 You can connect entities that have scripts attached to other entities. Let's say you have a HUD script that needs the Ammo count from the player. In the player script you can define a HUD parameter as entity: Script.HUD = nil --entity. Then drag and drop the HUD pivot that you have the HUD script attached to it. Now inside player you have access to all of the HUD scripts functionality via: self.HUD.script. You can call functions, assign variables, whatever. Another way to do it with flowgraphs, would be to fire an output each time the weapon is fired. Then in the player script have a GetAmmoCount()--arg, and in the HUD script have SetAmmo(count). Then you can link the Shoot or Fire output to the HUD's SetAmmo(), connecting the GetAmmoCount() arg function. If you aren't going to set the information with the boxes, then how will you get it? Well, you could load a file to get it. What file? Well that could be the information you hold in the input box. What file to open, read, and set a variable. Then you can still use the same GetText()--arg idea. There are no real need for globals. I'm trying to understand why you think you would need globals in the situations you mentioned. You can also loop through every entity that is loaded with: for x=0,App.world:CountEntities()-1 do local entity = App.world:GetEntity(x) if entity:GetKeyValue("type") == "player" then self.target = entity end end You could do this in the Start() function of a script to get entities that you care about and store them in a variable to use later. You'd have to set your own key inside said entities to identify them. However, this isn't that much different (and is slower) than just assigning things via the script parameters. 1 Quote Link to comment Share on other sites More sharing options...
bansama Posted February 1, 2014 Author Share Posted February 1, 2014 I'm trying to understand why you think you would need globals in the situations you mentioned. Did I say that? It wasn't my intention so apologies for any confusion. I'm trying to avoid globals. Thanks for the information on linking scripts and the idea of using another file to store information in. I'll be trying to work out how to use both. Quote Link to comment Share on other sites More sharing options...
Josh Posted February 1, 2014 Share Posted February 1, 2014 Well, once you get the handle to that entity in a script, you can access its own script and do whatever you want. The most direct way to do this is use a script where one of the fields is another entity. Quote My job is to make tools you love, with the features you want, and performance you can't live without. Link to comment Share on other sites More sharing options...
Mordred Posted February 1, 2014 Share Posted February 1, 2014 You can connect entities that have scripts attached to other entities. Let's say you have a HUD script that needs the Ammo count from the player. In the player script you can define a HUD parameter as entity: Script.HUD = nil --entity. Then drag and drop the HUD pivot that you have the HUD script attached to it. Now inside player you have access to all of the HUD scripts functionality via: self.HUD.script. You can call functions, assign variables, whatever. Hey Rick, that sounds quite interesting, but somehow i do not get the "hang" on it. So, would be cool if you (or someone else) could explain it to me a bit further. I'd like to do the following: Generate a script named "Skillsystem", that script contains several "Script.XYZ" variables holding stats (like strength and such --> yes i know i maybe could use tables but i'm not that far into the code yet). How do i acces the value stored in "strength" from another script and how do i write into the "Skillsystem" script new values? A simple link between "HUD" and "FPSPlayer" didnt work. I did enter "Script.hud = nul --entity "HUD"" in the FPSPlayer script and i linked the entity carrying "Skillsystem.lua" onto it. Skillsystem.lua again has the player as "target". So from what i read the idea was to get access to functions / variables in this manor: self.HUD.script:<Function>() or self.HUD.strength or smth. like this, well, it always returns an error like "attemt to index field 'HUD' (a nil value)". So basically i added this line to FPSPlayer.lua - Script.hud = nil --entity "HUD" and want to call it this way: - context:DrawText("Strength" .. self.HUD.strength, 200 ,200) while strength is not a function but only a variable defined in "HUD" (while HUD points to skillsystem.lua) Quote Link to comment Share on other sites More sharing options...
Rick Posted February 1, 2014 Share Posted February 1, 2014 You need to access it via the actual variable name (hud in this case) NOT it's label name "HUD". Lua is case sensitive. If you have Script.hud = nil --entity "HUD", then you access it's functions/variables with: self.hud.script:Function() self.hud.script.strength = 5 The part in "s is just a label name to show in the editor and not the name of the actual variable. Quote Link to comment Share on other sites More sharing options...
Mordred Posted February 1, 2014 Share Posted February 1, 2014 Darn, again a super stupid error. I didn't really think about that, i always though i wrote it uppercase...well, thank you a lot again Rick! Quote Link to comment Share on other sites More sharing options...
bansama Posted February 11, 2014 Author Share Posted February 11, 2014 Just wanted to say thanks for the help, I'm now starting to understand how to pass variables between scripts using the flow editor and defining entities to be dragged and dropped with "Script.entityName = nil --entity", etc.This has allowed me to fetch the player entity's position when they collide with a trigger box and to output that as string displayed along with debug/FPS information. This now raises some more questions, but I figure it'll be better to make new topics for those. Quote Link to comment Share on other sites More sharing options...
Rick Posted February 11, 2014 Share Posted February 11, 2014 This has allowed me to fetch the player entity's position when they collide with a trigger box Note that the entity that collided with a trigger box gets passed into the Collision() function by the engine so that should be all you need to know if it's the player or not, by either checking it's key values (entity:GetKeyValue("tag")) or by checking if it has certain variables/functions in the script (if entity.script.GetType ~= nil then type = entity.script:GetType()). Generally you'll pass entities around through script level parameters only if you can't get it another way. For example I have an enemy spawner object that spawns bombs. These bombs need a target to move towards, but the bombs are created at run-time so I can't directly set this in the editor. However, my spawner entity (just a pivot) is setup in the editor at design time and so it can have a parameter that is the target (player or something else) as a script parameter and it can pass on that information to the bombs it creates when it dynamically creates them. Quote Link to comment Share on other sites More sharing options...
bansama Posted February 11, 2014 Author Share Posted February 11, 2014 Note that the entity that collided with a trigger box gets passed into the Collision() function by the engine so that should be all you need to know if it's the player or not I specifically want their location. Now if that gets passed into Collision(), then great. How would I fetch it from there? Quote Link to comment Share on other sites More sharing options...
Rick Posted February 11, 2014 Share Posted February 11, 2014 The entity is the gatekeeper to all information about the entity. local pos = entity:GetPosition() 1 Quote Link to comment Share on other sites More sharing options...
bansama Posted February 12, 2014 Author Share Posted February 12, 2014 So I looked at this code for getting entities: for x=0,App.world:CountEntities()-1 do local entity = App.world:GetEntity(x) if entity:GetKeyValue("type") == "player" then self.target = entity end end And changed it so that it printed out entity names. Doing that, I notice it doesn't fetch the name of entities with collision set as a scene/have a mass of 0. So if I have the my floor as an entity with a mass of 0, set as a scene, is there no way to get its name? I really want define the floor as a parent for a pressure pad so I can get the local coords of the player in respect to that pressure plate. I tried setting the pressure plate as the parent for the player and lets just say bad things happened. I also wonder if it's best to fetch entities with such a loop as opposed to specifically defining the one I wish to use via Script. Wouldn't the faster of the two be better -- and wouldn't the fastest be Script? Or am I misunderstanding something? Quote Link to comment Share on other sites More sharing options...
Rick Posted February 12, 2014 Share Posted February 12, 2014 So if I have the my floor as an entity with a mass of 0, set as a scene, is there no way to get its name? If you need to do this, create an empty script (I call my dummy.lua) and attach it to that entity. CSG objects will get collapsed and not be seen by themselves unless they have mass or a script attached. I really want define the floor as a parent for a pressure pad so I can get the local coords of the player in respect to that pressure plate. ?? What are you trying to do with this? I assume the pressure pad itself is a model/entity? You can attach a script to that model that looks for the player colliding (in Collision() check the entity's name that gets passed in and see if it's player). Then you have the player position from there. I also wonder if it's best to fetch entities with such a loop as opposed to specifically defining the one I wish to use via Script. Wouldn't the faster of the two be better -- and wouldn't the fastest be Script? Or am I misunderstanding something? Looping like that will always be slower than getting your entities another way. I would avoid looping like that if at all possible, and generally it's very possible to avoid having to do that. If you do have to loop, looping on startup will be better than looping during playing. Quote Link to comment Share on other sites More sharing options...
bansama Posted February 12, 2014 Author Share Posted February 12, 2014 If you need to do this, create an empty script (I call my dummy.lua) and attach it to that entity. CSG objects will get collapsed and not be seen by themselves unless they have mass or a script attached. Aha! Now I remember reading about that somewhere. Thanks for reminding me. I tried giving it mass as a scene object and it spun off into the void killing me. Was funny to watch, but not very helpful. ?? What are you trying to do with this? I assume the pressure pad itself is a model/entity? You can attach a script to that model that looks for the player colliding (in Collision() check the entity's name that gets passed in and see if it's player). Then you have the player position from there. This is where it gets a bit complicated to explain. But basically, like the teleport tutorial (I think that was in one of your topics) I want to teleport the player between different points on the map. However, I want to do so in a manner that player doesn't know it's happening. I figure to do this, I need two identical locations in different parts of the map. When they hit the pressure plate, they should teleport. But in a manner that keeps their current position and rotation relative to the pressure plate they hit. Looking at the teleport tutorial, it didn't look like the player was keeping this information. If you've played Stanley's Parable, then that should give you an idea of why I want to seamlessly transport players, so that I may change the world around them without them realising it. Looping like that will always be slower than getting your entities another way. I would avoid looping like that if at all possible, and generally it's very possible to avoid having to do that. If you do have to loop, looping on startup will be better than looping during playing. That makes sense. Thanks. Quote Link to comment Share on other sites More sharing options...
bansama Posted February 13, 2014 Author Share Posted February 13, 2014 So any suggestions on the best way to fetch player position? Am I right in thinking I will need to specify something as a parent in order to actually get local coordinates? It makes sense to me in theory, but I won't be able to try it out in practice until Saturday. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.