Hi! I have started to work on python scripts and I had some questions for you. So here I go :
What I want to do is that every object that can damage my player will send an message to the lifebar telling it in the body of the message how many life points to remove. So far so good, I have managed to do that.
Once the lifebar receive the message, I want it to look if my player mesh, that is another object, is in its invincible state and if it isn’t the case, I want it to remove the number of life point contained in the body of the message to its life property.
The problem is that I can’t find how to check another object and how to check its state.
Here is what I have so far :
import bge
def main ():
cont = bge.logic.getCurrentController()
own = cont.owner
player_Mesh = #how do I get another object?
message = cont.sensors["damage"]
body = message.bodies()
if #player_Mesh second state is false:
life -= int(body)
I wouldn’t do it that way. It creates confusing dependencies.
The display
The Health-/Life-bar should be display only. It should not calculate the life.
This means you send the current value to the the display, which shows it in a proper form.
Advantage:
you can have a s much displays as you want (even none).
The health calculation/processing is independent from the display and the form of display
you can have several forms of displays (bar, number, warning messages …)
You already discovered how to do that:
send a message with the new value to all displays
If you do that with a well chosen subject (e.g. “update player’s health”) the sender does not need to know the displays.
Displays should listen to this subject. Btw. Messages are scene-independent. That means a display can be at any scene.
What calculates the health?
The best place would be the character itself. But it could be a separate object to.
The input is everything that potential can change the health. It calculates the new health value from this input and sends the result around.
Surely the “health”-calculator should be known by all objects that provide input to it (player, collision sensors).
Examples:
A weapon collided with the player’s mesh:
-> weapon damage + player protection + current health + water temperature + size of the moon => new health
A “magic spell” heals the player:
-> spell + magic resistance + current health + color of a flower => new health
The player gets poisoned:
-> the poison is active + strength of the poison + poison resistance + age of London => new health
health changed? => send update message as described above
SolarLune, if I understand correctly [i for i in obj.children if i.name == “Player_Mesh”][0] will create a variable named “i” that will check each object in the scene. Then, for each object it will look if the name is the same as the name of the object I am looking for. So far so good. Two questions, 1, does it work for all the scenes? 2, why the [0] ? Also, there is no real way to check if a state is enable or disable, is it? You have to create a property to do it, right?
Monster, I have to say I love your examples, they’re really funny.XD Thanks for your suggestion too. It makes completely sense and I’ll make sure to follow this logic in my next creations to prevent problems.^^’ Just a quick question however, I was curious to know if it was significantly more expensive to send a message to every object rather then sending it to just one?
Actually, that line will create a list and populate it with the elements from the list specified (obj.children) that meet the condition (i.name == “Player_Mesh”, which should be only one object). After this, you should have a list like: [Player_Mesh], so you don’t need the list anymore, but just the object. So, the [0] isolates the object (the first, and only, value in the list), leaving you with the reference to the Player_Mesh object.
There might be an obj.state or obj.states variable, but I’m not sure.
The way I structure my player statistics is quite good (I think)
I have a scene with only empties in it called “Properties”
Then anything that deals with energy, shield, weapons-available etc goes and checks this one scene.
This not only makes sure that all the information is in one place, but also means that when you change level it will keep your inventory and such.
Hm… I took a break of python and just went back in and it seems the technique proposed by solarlune isn’t working.
As a quick resume, I want in object A to check a property in object B.
Solarlune proposed : player_Mesh = [i for i in obj.children if i.name == “Player_Mesh”][0]
Where player_Mesh is object B
It doesn’t work, mainly because obj is not defined in my code. I though that it would work if I were to change obj by own which is object A (own = cont.owner) but when I do that, it tells me “list index out of range” when I run the script. =/
Also, I have object C sending a message with a subject to Object A. Anybody knows how to get this message? I’m trying with :
message = cont.sensors[“damage”] #Where “damage” is the name of the message sensor activating the script
body = message.bodies()
but it doesn’t work.
That error means two things. 1 you don’t have a child object of the object running the script called ‘cube’
Or 2 that children recursive function doesn’t exist
1 you don’t have a child object of the object running the script called ‘cube’
The object running the script is called healthBar_green and does not have any child. Cube is another object that I try to influence from healthbar_green and does not have any child either. I’m not sure to understand what is parenting doing in this issue.
2 that children recursive function doesn’t exist
Children recursive function? I don’t really know what it is, but let us admit the problem is here. Would you have a solution to suggestion me? How would you access the information of one object with another one using python?
Sweeeet! It works perfectly! And the code is clean too. I had a feeling there was an simple way to do it that I just couldn’t find. Thanks to all of you, I’ll now be able to progress.^^
Sorry for the double post, but is there an easy way to get another scene? The object I try to get is in another scene.^^’
Something like : Scene = bge.logic.getScene(“sceneName”) ?
I have searched the api and the closest thing I have found from this is getSceneList. I think I could probably get the scene I want from this list but I’m not too sure how. =/
Well, if you know that the scene is in the list, then you could do a:
scelist = logic.getCurrentSceneList()
othersce = [i for i in scelist if i.name == "OtherSceneName"]
You could also use a for-loop to loop through the list and pick out a scene if the name’s equal to something (like the above code does). Also, since the lists are arranged in order of drawing, you can presume a scene is in a slot (like the GUI scene would be drawn before the game scene) and grab it using an index:
scelist = logic.getCurrentSceneList()
guisce = scelist[2] # Just an example; presumes there are three active scenes, and that one is a background scene and one is an overlay scene.
bgsce = scelist[0] # Background scene's drawn first, so would be the first in the list
I’m not entirely sure about the above method, though - it might be flipped (i.e. BG scene’s last in the list, and overlay’s first).
Thanks again a lot solarlune. The code you send me is working. I used to work in C++ and I am not sure to understand what is going on however. If I try to decompose it, here is what it gives me :
[ i # we create a variable named i
for i in scelist # here I am a bit lost. I understand that we start a loop where we look at each possibility in scelist but the syntax confuse me a bit.
if i.name == “OtherSceneName”] # If I understand correctly, we stop the loop the moment the current value of i is equal to the desired name
What I don,t understand the most, is that sooner you suggested me something similar but I had to put [0] at the end to take the first element of the list. Why don’t I have to do it now?
Also, I would have a last question. Does someone know how to get the body of a message?
Here’s the situation : The script is triggered when the object receive a message called “damage” that have and a body containing a string with the value “30”. (It is containing a string instead of a int because I couldn’t figure how to send a body containing something else then a string while using python.) I want to get this message and transform the string in it into an int to be able to subtract this int to my life variable.
here is what I have so far :
message = cont.sensors[“damage”] # I create a variable message and make it equal to the message sensor that started this script
body = int(message.bodies()) # I create a variable body and make it equal to the integer version of the body of my message
here is what the console is telling me :
‘CListValue’ object is not callable
I get this error message for the line containing body = int(message.bodies())
Does someone know what I’m doing wrong or how to get the body of a message? Thanks!
I’m not familiar with messages, but I’m guessing that:
body = int(message.bodies())
isn’t working because message.bodies is a list, not a function. So, you need to pick out what from the list you need. Try printing it out to see what it contains.
As for my example, I forgot to put the [0] at the end of it. :I - Totally my mistake. The idea is that in this code:
[item for item in list]
You’re looping through all items in the list and returning them to make a list. Putting [0] at the end gets the first value in that list. This way, you can isolate a single item from a list if you don’t know where it is:
[scene for scene in logic.getSceneList() if scene.name == “MySceneName”]
In this above example, we isolate a scene using the scene’s name. The code loops through the scene list returned by logic.getSceneList(), and tests each scene, adding it to the finished list if its name is what we specify. At the end of it, we have a list that contains all scenes that met our criteria. If we only want one, we can isolate it by using the [0] at the end to specify the first value in the finished list.
EDIT: If you don’t have to do it now, that’s odd. Why not post that portion of the script, as you should have to do it. I also want to mention that the scene object list is special in that you can refer to the objects by name to pick them out, like:
I got it working. You were right SolarLune, message.bodies was a list and not a function. I had to write “message.bodies[0]” in order to get the right value. And thanks again for your explanations, it starts to make more sens now. After a few more usage I should be at ease with this new syntax.
P.S.: Ouuups. My bad, I had to write the [0] at the end, I misread my code.^^’
Now everything is working! I am now ready to conquer the world! Woohoo!