Jump to content

How to pass variables between scripts? (Lua)


bansama
 Share

Recommended Posts

On the pressure plate attach a script. In this script you'll probably want to make a script parameter that is an entity, that is the target pressure plate on where to send the character. Go into the Script:Collision(). Check if the entity passed in is type or name player by using:

 

entity:GetKeyValue("name") (this is the name your player object is in the scene tab.

 

If it's the player then you can get the position of the player with:

 

local pos = entity:GetPosition()

 

Then you can get the position/rotation of the pressure plate with self.entity:GetPosition() in the collision function. Now you have both positions and can tell where the player is in comparison to the plate. Now move the player to the destination plate that is your script variable. This will put it right at it's center point, but then you can offset the player position based on your calculations above.

 

I only did the demo of the Stanley's Parable, but make sure this is what they are doing. They might be doing subtle things that somehow skew the camera view when they make their transitions? Not sure.

Link to comment
Share on other sites

  • 2 weeks later...

So another evening spent on this at last. But another evening where I feel I'm hitting my head against a brick wall.

 

Right now, I'm pointing scripts to the player entity with Script.PlayerTarget = nil --entity and dragging the Player to the related box in the editor. But when I switch between projects, etc., this has often seems to be wiped out. So I need to keep setting it. It's frustrating, so I figure I should just tell the script directly that I want it to work with the player entity.

 

 for x=0,App.world:CountEntities()-1 do
local entity = App.world:GetEntity(x)
entInfo = entity:GetKeyValue("name")
System:Print(entInfo)
end

 

As I think I've said before, the above works to output entity names to the Output window. This gave me the idea to use GetEntity() to specify the name directly with: self.PlayerTarget = App.world:GetEntity(Player)

 

But this just throws back an error. I can find no documentation on GetEntity.

 

Is there a simple way to state directly in a script that variable X should equal entity y, without using a loop like the above to search for it? [self.PlayerTarget = entity:GetKeyValue("Player") didn't seem to work either]

 

On an unrelated note, I've noticed that the way in which I was outputting coordinates to the screen no longer works unless I'm in debug mode.

 

function Script:PostRender(context)
local font = Font:Load("Fonts/arial.ttf", 12)
App.context:SetFont(font)
App.context:SetBlendMode(Blend.Alpha)
if self.Text ~="" then
context:DrawText("Current output is " .. self.Text, 102, 22)
end
App.context:SetBlendMode(Blend.Solid)
end

 

This used to output text when "Running" the game but now only does so when "Debugging". Did something change?

 

EDIT: Actually come to think of it, I don't recall seeing the FPS counter that usually displays either. Is there a hotkey toggle for that which I may have accidentally pressed?

Edited by bansama
Link to comment
Share on other sites

In App.lua you can see that if you toggle F11 in Release mode it'll show you FPS. Make sure you have updated your projects too if you haven't already.

 

The only way you can get an entity is by looking over using GetEntity() and checking the name or passing it as a script parameter. If you have issues with that you should post a test map that has the issue as a bug so Josh can fix it.

Link to comment
Share on other sites

  • 1 month later...

I know it's been a while, but I have another question...

 

While I've made some progress, I'm still not sure how to reference a variable set in one script from another script without using the flow editor or setting it as a global. For example, I know have the following in App.lua:

 

 --Set player entity
if setPlayerEntFlag ~= true then
-- Locate the player entitiy
for x=0,App.world:CountEntities()-1 do
local entity = App.world:GetEntity(x)
local entInfo = entity:GetKeyValue("name")
if entity:GetKeyValue("name") == "Player" then
EntPlayer = App.world:GetEntity(x)
System:Print(EntPlayer)
end
end
setPlayerEntFlag = true
end

 

This locates the player entity and as the system:print line shows, it does indeed locate it. This allows me to use the player without having to loop to it each time and allows me to use it without needing to link it in the flow editor (as that still randomly wipes such connections -- I can't work out when or why though, so have yet to report as a bug).

 

So I can then reference the player entity in another script I use:

 

self.PlayerTarget = EntPlayer

 

In the other script. This currently works because EntPlayer is global. But I'd like to know how to interact with it when it's a local variable. I tried App.EntPlayer, script.App.EntPlayer, and variants thereof, but always get the "global is a nil value" error. So my question is, what am I missing?

Link to comment
Share on other sites

Does thus all have to be automatic? Do the scripts have to be able to detect another scripts presence is, does the collision script need to find the text script? Or do you want to be able use the editor to attach the scripts together? Or do you want to use the flow graph? There are at least 3 different ways to pass values to other scripts.EDIT:On my phone so didn't see the second page, ignore the above text if you want. I'll give it a try when I get home in a few hours.

Edited by Einlander
Link to comment
Share on other sites

In App.lua if you defined EntPlayer as self.EntPlayer then in other scripts you'd be able to use App.EntPlayer to access it. This is still basically global though as the App object is global. This isn't the worst since you'll probably only have 1 player in your game but it makes the scripts you make dependent on this App.EntPlayer meaning you can't share your scripts with others or between projects without your version of App.lua because of how it sets this variable. It means your other scripts are tightly coupled with your App.lua file. This may not seem like a big deal to you but it breaks things for people who have their own App.lua and when the workshop starts could cause issues if you want to share your scripts with others and now you have to modify their App.lua to work with your scripts. They may already have App.lua modified to do certain things like changing maps or whatever. You don't want to modify App.lua and if you do you want it to be a very generic modification.

 

 

To avoid globals, it sounds like you know what the player is at design time. You could pass that entity around to the scripts that need it as a script variable.

  • Upvote 1
Link to comment
Share on other sites

  • 1 month later...

Once again, thanks for the replies. I've been busy with work thus have had no time to revisit this for while. But I have some free time again now for a few days and with that time, another question!

 

Am I right in that self.whatever can only be used within a function? If I try it outside of a function, I get the usual error about attempting to index a nil global. If this is so, then I'll need to slightly tweak my current code that I put in App.lua so that it's now a function. I'm also going to try moving that out of App.lua and attaching it to a pivot as its own script.

 

EDIT: So apparently I can't get that to work either. Using the following as a script attached to the pivot:

 

--Set player entity
function Script:SetPlayerEntity()
-- Locate the player entity
for x=0,App.world:CountEntities()-1 do
entity = App.world:GetEntity(x)
entInfo = entity:GetKeyValue("name")
if entity:GetKeyValue("name") == "Player" then
self.EntPlayer = App.world:GetEntity(x)
System:Print(self.EntPlayer)
end
end
setPlayerEntFlag = true
end

if setPlayerEntFlag ~= true then
Script:SetPlayerEntity()
end

 

And then trying to reference it in a different script with:

 

self.PlayerTarget = LocatePlayerEntity.EntPlayer

 

I get the usual "attempt to index global 'LocatePlayerEntity' (a nil value)" error =S

 

EDIT 2:

 

A final edit as this is driving me crazy again.

 

If I modify the end of the above script which is attached to the pivot so that it is:

 

if setPlayerEntFlag ~= true then
Script:SetPlayerEntity()
end
System:Print ("Hello")
System:Print(self.EntPlayer)

 

Then the first System: Print works, and I see Hello being output, but the second throws up the same self related error. Which pretty much tells me that self is not doing what I need, or I simply have no clue as to how it's supposed to work (which is very likely). I've tried Googling for information on this and can find nothing helpful that is written in a manner I understand. So I would appreciate some help with understanding why this isn't working. This is both with and without "return self.EntPlayer" being added to the function.

Edited by bansama
Link to comment
Share on other sites

It works in Lua i used it in Shoot em up script (http://www.leadwerks.com/werkspace/files/file/524-shootemup-kit/)

 

Player script declaration :

 


Script.id = "player"

 

Pivot declarations :

Script.player = nil 

 

Pivot code start() function :

	 local entities = GetEntityNeighbors(self.entity,5000,true)
 local k,entity
 for k,entity in pairs(entities) do
		 if entity.script.id ~= nil and entity.script.id == "player" then

						 self.player = entity

		 end
 end

 

Now the Pivot have a reference to player object, so Pivot can get position or any other information form player object.

I would say stop using GetName, use like above a strong system as script variables (like id in example above).

  • Upvote 1

Stop toying and make games

Link to comment
Share on other sites

@YouGroove Thanks, I'll have a look at that in a little bit.

 

What is your final goal here?

 

To recap, I want to be able to reference coordinates and rotation of the player entity so I may use this information when moving the player around on a collision event inspired by the teleport tutorial found on YouTube (which, if I'm not mistaken, was uploaded by yourself?)

 

I would prefer to be able to do this without needing to be dependent on the flow graph editor due to it wiping links. Which, so far, I still have not determined the cause of. And as I mentioned earlier, I don't want to need to loop through entities to find the player each time, when doing that once should theoretically suffice.

 

I did have this working with the code above in App.lua, but based on your previous comments, I'm now trying to to do the same without needing to alter App.lua.

 

So in short, I am currently trying to set up a pivot that will locate the player entity so that I may reference that entity later without having to loop through all entities to find it each time (and without needing to link to player to specific entities in the flow graph editor each time).

Link to comment
Share on other sites

Then the first System: Print works, and I see Hello being output, but the second throws up the same self related error.

 

I think you should be carefull , and search player entity one time only on the Start() function, this way it is initialized and found at game start. The example above i posted , the pivot object in Start function will find player entity refrence once, so you'll be able to use that reference on UpdateWorld(), upadtePhysics() any time you need it.

Stop toying and make games

Link to comment
Share on other sites

To recap, I want to be able to reference coordinates and rotation of the player entity so I may use this information when moving the player around on a collision event inspired by the teleport tutorial found on YouTube (which, if I'm not mistaken, was uploaded by yourself?)

 

OK. There is no need to have the player be global for this to work. So in your collision event you can check if the entity that the engine passed into the event (the entity that collided with the trigger) is the player. You can do this by checking a key value. Let's say you named your player entity "Player". This gives it a "name" key of "Player". So in your collision event you check if entity:GetKeyValue("name") == "Player".

 

Now if that statement is true, the player has just collided with that trigger and the entity that was passed into the collision event IS the player entity. So now you have the player entity available to you without it having to be global or looping over anything. Now you can do anything you want with the player entity.

 

In my teleport tutorial I have a teleport script and in that script it has a variable that says where should I teleport to. In the editor I link the 2 teleporters together so that they know about each on during run-time so that I can transfer the player between them on collision enter, which I can do because I have the player entity from my if check. You would need to do that same setup on linking 2 teleport triggers.

 

Now in your case you wanted relative positions to the teleport pad so that you could position the player, which you have inside the collision function and that's where you would want to do the positioning of the player, on the other teleport pad. You should have all the information you need right inside that teleport pad. You have that teleport information, you have the player entity, and you have the linked teleport information.

 

If you make both your triggers the same size, then inside the collision you can get the offset between the trigger and the player positions, and then inside the trigger, where you already have the player entity because that's what collided with the trigger and the engine passed it into that function for you, you can position the player to the other triggers position, then offset by the same amount.

 

 

Does that make sense? You have all the information you need inside the collision function so there is no need to loop over anything to get the player.

  • Upvote 1
Link to comment
Share on other sites

You can do this by checking a key value. Let's say you named your player entity "Player". This gives it a "name" key of "Player". So in your collision event you check if entity:GetKeyValue("name") == "Player".

 

In theory, this all makes perfect sense. In practice, I'm still getting the "attempt to index global/local 'entity' (a nil value)" errors. Which again brings me back to the question, how do a I pass variables between scripts and functions?

 

For example if I try adding the following function to the collision script template:

 

function Script:PlayerPosition(entity)--arg
if entity:GetKeyValue("name") == "Player" then
self.PlayerTarget = entity
return self.PlayerTarget:GetPosition():ToString()
end
end

 

I get an error that makes it clear the function cannot find the entity.

 

As I said when I first made this topic, this is something that was so elementary easy in PHP that I just can't work out why I cannot do it in Lua and it is driving me completely crazy and leaving utterly frustrated. So I would really appreciate some concise help with actual example code so I can work out what is wrong with my own code.

 

As YouGroove said, making errors and learning from them is good. But only if someone can help point out why it's an error and how to fix it. Otherwise, you may just end up in an infinite loop of errors and learning nothing. That's why we need teachers, and why I would really appreciate help with this.

Link to comment
Share on other sites

Reading through this thread I realized the most obvious question has not been asked. Are you actually setting the keyvalue to the entities you want to find? The leadwerks does not actually set this for you, it needs to be set in script. The fpsplayer already has this in the script.

Link to comment
Share on other sites

Are you actually setting the keyvalue to the entities you want to find? The leadwerks does not actually set this for you, it needs to be set in script. The fpsplayer already has this in the script.

 

A good point I will bear in mind. But for now it's the player I am trying to work with. The collision function finds the player, but how do I then pass the player as found by the collision function to a different function in the same script?

Link to comment
Share on other sites

Just declare a global variable on you script or call a function that have player entity as parameter.

 

Pseudo code : function

 


function Collison (entity , ... )
self:DecreasePlayerHealth(entity, 10)
end


function:DecreasePlayerHealth(playerEntity, number)

playerEntity.script.health = entity.script.health - number
-- or call a function that player entity script already has
playerEntity.script:Decrease(number)

end

 

 

Pseudo code : global variable

 


Script playerFound = nil


function Collison (entity , ... )
self.playerFound = entity
self:DecreasePlayerHealth(10)
end


function:DecreasePlayerHealth( number)

self.playerFound.script.health = entity.script.health - number
-- or call a function that player entity script already has
self.playerFound.script:Decrease(number)

end

 

I hope it helps.

Stop toying and make games

Link to comment
Share on other sites

"name" actually does get set by the engine so that should be fine.

 

Tell us the exact error you are getting and on what line. I'll leave this site open all day so send me an IM on it and if I'm on I'd be more than happy to help you in real-time.

 

@YouGroove Your examples doesn't make any sense according to the issue he's having and only confuses matters. For instance you aren't doing any checks to see if the entity passed into the collision is actually the player, which is fundamental to his issue. Be careful with the help you give in programming (since you are newer) as it might just cause more confusion.

Link to comment
Share on other sites

@YouGroove Your examples doesn't make any sense according to the issue he's having and only confuses matters. For instance you aren't doing any checks to see if the entity passed into the collision is actually the player, which is fundamental to his issue. Be careful with the help you give in programming (since you are newer) as it might just cause more confusion.

 

I'm not so new to Lua i think, as i also done some games demo on my own, and if he could dig the script i done he would have all he needs as mechanics to have entities id and dealing with them in a simple way tongue.png

http://www.leadwerks.com/werkspace/files/file/524-shootemup-kit/

 

But i agree it was a fast example on how to easy pass variables, he don't have Lua enought experience as some others to pick up only what he needs. My goal was only to give him directions to not let him blocked, but yes i should avoid helping people new to programming and Lua.

Stop toying and make games

Link to comment
Share on other sites

I didn't say stop 100% but just be careful with the advice you give and know that you are still new to programming and you might be giving less optimal ways of doing something. It would be the same as if I started giving less optimal advice on art related stuff. Some may have you believe that as long as something works then that's all that matters (which for small apps may be true) but how you structure your game matters when things start getting larger. How you do things in programming do matter for maintaining and being able to read and follow it 6 months from when you write it. The same holds true for doing things in the art world.

Link to comment
Share on other sites

Tell us the exact error you are getting and on what line. I'll leave this site open all day so send me an IM on it and if I'm on I'd be more than happy to help you in real-time.

 

I've put some of my old code back in for now, which has gotten rid of the errors. However, what I am trying to do is fetch the player on collision. This I've now done.

 

if entity:GetKeyValue("name") == "Player" then
System:Print("Found Player")
PlayerEnt = entity
System:Print(PlayerEnt)
return PlayerEnt
end

 

The system print shows it's found the player entity.

 

I was hoping the return would allow that to be picked up by another function in the same script.

 

function Script:PlayerPosition(PlayerEnt)--arg

System:Print("Testing...")
System:Print(PlayerEnt)
System:Print("...End testing")

end

 

This system print shows that PlayerEnt has not been passed as the output is 0x00000000 and not the address found on collision.

 

I know I don't need to do any of this. I could just handle everything within the function handling collision, but I really would like to know how to pass PlayerEnt from one function to another as I'm sure it will help in the future with other things.

 

Right now this PlayerPosition function is where I'm testing all the coordinate stuff, ie. breaking it down into x, y, and z (which again, I've managed). The only thing holding me back with this current approach is passing that PlayerEnt between the functions.

 

Also, thanks for the offer of helping via the IM function (I assume that's the bar at the bottom of the site, right?) if you're still around in a couple of hours, I'll take you up on that. I just need to get the children in bed first.

 

@YouGroove Yes, I lack the knowledge I need to do what I'm trying to do, hence the topic. I appreciate your examples though they have provided some food for thought.

Link to comment
Share on other sites

You are returning the player entity from the collision script? The engine calls this collision function so the question is why would you think returning it would do anything? Not being mean just trying to get your thought process so I can understand and help you understand.

 

Also what I'm seeing from your code is that you maybe think there is a relationship between script variables and function parameter variables (since you have them named the same)? There isn't. Just because your PlayerPosition parameter is named the same as the variable you seem to set in the collision function doesn't mean they are the same. In fact they are not.

 

You don't need to set the entity to anything in your collision. You said you want to pass the player to your function. So pass it as a parameter (this is the normal way to handle it).

 

 

if entity:GetKeyValue("name") == "Player" then

System:Print("Found Player")

self:PlayerPosition(entity)

end

 

So you will notice in the above example that should be in your collision function, if the entities name is "Player" you print out a message and you pass that entity to the PlayerPosition function. You use self: because that function is part of that script (self refers to the script itself and since that scrip that function self:PlayerPosition() means the PlayerPosition() function in that script). Note that script variables uses a dot and not a colon. That's just how Lua works (functions can use dot as well but colon is easier for use that as you have to do something else to use dot for functions. colon is a shortcut that you should use).

 

 

Now you may have thought that PlayerEnt was global and if it's global why wouldn't it work in your PlayerPosition() function. Well 2 things. Since you named the parameter to that function the same I'm thinking Lua gets confused when inside that function (reality is it's probably not confused and it's probably using that parameter which is probably nil when the function is called). When is this function called? These are reasons to avoid globals. Even without the parameter to that function the order of when that function gets called has a requirement of setting the PlayerEnt variable, so if that function gets called at any point before PlayerEnt isn't set to something then it'll be nil.

 

But you'll notice how much easier things are without globals in this case. Just call the function from the collision passing in the entity when it's the player. There is no need for the global and in fact having the global caused more typing and more things that can go wrong.

Link to comment
Share on other sites

if entity:GetKeyValue("name") == "Player" then

System:Print("Found Player")

self:PlayerPosition(entity)

end

 

But you'll notice how much easier things are without globals in this case. Just call the function from the collision passing in the entity when it's the player. There is no need for the global and in fact having the global caused more typing and more things that can go wrong.

 

That did it. Now I see where I was going wrong. As the function appears to be processed without being called I had not thought that calling with self:PlayerPosition(entity) was required. Your comment about globals though is something I'd like to clarify. Using local before the variable name ensures it's not a global, right? If such a variable is passed with self:FunctionName (variable) does it remain non-global?

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...