Results 1 to 17 of 17
  1. #1

    scoring system broken

    So I'm building a game and this game has a scoring system, when an enemy is killed it adds 1 to your score. This is done with messages so when an enemy is dead it sends a message that tells the score object hey add a point because the player killed me. Here's the problem though, if two of the same enemy type die at the same time and this particularly common with things such as explosive barrels, it doesn't detect the increase in score for both of the enemy's only one of them. This is obviously a big problem and so I'm wondering if there is a better system I can use.
    Also not sure how important this may be but just in case the enemies are spawned in by empties.



  2. #2
    Member Nemescraft's Avatar
    Join Date
    Mar 2016
    Location
    Somewhere on Earth
    Posts
    210
    Well, I don't know how you are detecting the "dying", but if this is a sensor, e.g. bullet hit, then it shouldn't be too hard. I suggest:

    -Logic Bricks. Selecting your enemy that the empty spawns (has to be in inactive layer, not the one/s you are using). -> Selecting the player. -> Connecting the "died" sensor on the enemy to the add point actuator on the player (if it isn't on the player, select the object that property is on instead of the player). By selecting both objects you can connect their logic bricks.

    -Python. Maybe write a code such as:
    Code:
    import bge
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    
    player = scene.objects["Player"] #<- player name here
    own = cont.owner #enemy playing the script
    
    died = cont.sensors["Died"] #the "died sensor"
    
    
    if died.positive: #if enemy dies
        player["Points"] += 1 #add 1 point
        own.endObject() #remove enemy

    I haven't tested it, but it should work. Make sure that a Python Controller is added to the enemy you spawn (in inactive layer), and connected to the "died" sensor. For this to work the playyer has to be called "Player" and the points property "Points".

    If none of these work, sorry, I tried.



  3. #3
    use the globalDict to store data that is persistent. this way anything can happen to the player object, scene, or even load a new blend, and the score will remain.

    run this script whenever a score needs to change. the game property "POINTS" will tell the script what to do.
    Code:
    import bge
    
    owner = bge.logic.getCurrentController().owner
    
    points = owner.get("POINTS", None)
    
    if "SCORE" not in bge.logic.globalDict:
        bge.logic.globalDict["SCORE"] = 0
    
    if points == "RESET":
        bge.logic.globalDict["SCORE"] = 0
    elif points != None:
        bge.logic.globalDict["SCORE"] += points
    
        if bge.logic.globalDict["SCORE"] < 0:  #EDIT: Prevent Negative Score
            bge.logic.globalDict["SCORE"] = 0
    you can have negative point values to lower the score and if the points property is "RESET", then it will put the score to 0.

    EDIT: run this on any object you need to access the score. it will copy the the globalDict value to the game objects property "SCORE"

    Code:
    import bge
    
    owner = bge.logic.getCurrentController().owner
    
    if "SCORE" in bge.logic.globalDict:
        owner["SCORE"] = bge.logic.globalDict["SCORE"]
    Last edited by Daedalus_MDW; 31-Aug-17 at 04:28.
    System "IVAN" (rev 1.3b) - Win7 64bit - Blender 2.74:
    CPU- Intel i3-3220 3.30 Ghz | GPU- EVGA GTX 970 | RAM- GSkill Ares 16GB 1600 Mhz | MB- ASUS P8Z77-V LK



  4. #4
    Originally Posted by Nemescraft View Post
    Well, I don't know how you are detecting the "dying", but if this is a sensor, e.g. bullet hit, then it shouldn't be too hard. I suggest:

    -Logic Bricks. Selecting your enemy that the empty spawns (has to be in inactive layer, not the one/s you are using). -> Selecting the player. -> Connecting the "died" sensor on the enemy to the add point actuator on the player (if it isn't on the player, select the object that property is on instead of the player). By selecting both objects you can connect their logic bricks.

    -Python. Maybe write a code such as:
    Code:
    import bge
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    
    player = scene.objects["Player"] #<- player name here
    own = cont.owner #enemy playing the script
    
    died = cont.sensors["Died"] #the "died sensor"
    
    
    if died.positive: #if enemy dies
        player["Points"] += 1 #add 1 point
        own.endObject() #remove enemy

    I haven't tested it, but it should work. Make sure that a Python Controller is added to the enemy you spawn (in inactive layer), and connected to the "died" sensor. For this to work the playyer has to be called "Player" and the points property "Points".

    If none of these work, sorry, I tried.
    Nope they don't



  5. #5
    Originally Posted by Daedalus_MDW View Post
    use the globalDict to store data that is persistent. this way anything can happen to the player object, scene, or even load a new blend, and the score will remain.

    run this script whenever a score needs to change. the game property "POINTS" will tell the script what to do.
    Code:
    import bge
    
    owner = bge.logic.getCurrentController().owner
    
    points = owner.get("POINTS", None)
    
    if "SCORE" not in bge.logic.globalDict:
        bge.logic.globalDict["SCORE"] = 0
    
    if points == "RESET":
        bge.logic.globalDict["SCORE"] = 0
    elif points != None:
        bge.logic.globalDict["SCORE"] += points
    
        if bge.logic.globalDict["SCORE"] < 0:  #EDIT: Prevent Negative Score
            bge.logic.globalDict["SCORE"] = 0
    you can have negative point values to lower the score and if the points property is "RESET", then it will put the score to 0.

    EDIT: run this on any object you need to access the score. it will copy the the globalDict value to the game objects property "SCORE"

    Code:
    import bge
    
    owner = bge.logic.getCurrentController().owner
    
    if "SCORE" in bge.logic.globalDict:
        owner["SCORE"] = bge.logic.globalDict["SCORE"]
    This isn't really what I need, I don't need the score to save and I don't need negative points, I just need it to tell the player wether or not they killed every enemy in the level. The problem is that if two die at the same time it only counts as one if they are the same kind of enemy.



  6. #6
    Member WKnight02's Avatar
    Join Date
    Aug 2017
    Location
    Earth
    Posts
    129
    One other way using purely logic bricks would be to do some kind of "heartbeat", as long as you don't need to really count enemies, but only to know if one is still alive:

    Make each enemy emit a message say every 5 ticks. Then, have your "game controller" looking for message, and trigger level's end when no more heartbeats are detected.

    If you want to use Python, you can do something like this (place an empty used as a "main logic controller", with an "always" brick in true-pulse mode connected to a Python controller running this script when needed)

    Code:
    import bge
    
    scene = bge.logic.getCurrentScene()
    enemies = len([object for object in scene.objects if object.get('enemy', False)])
    
    if enemies == 0:
        print("this is the end")
    Make sure to put a property on your enemies objects with "enemy" as name and some value that will be evaluated to "True" (a boolean is the easiest way)
    Last edited by WKnight02; 02-Sep-17 at 00:56.



  7. #7
    Originally Posted by WKnight02 View Post
    One other way using purely logic bricks would be to do some kind of "heartbeat", as long as you don't need to really count enemies, but only to know if one is still alive:

    Make each enemy emit a message say every 5 ticks. Then, have your "game controller" looking for message, and trigger level's end when no more heartbeats are detected.

    If you want to use Python, you can do something like this (place an empty used as a "main logic controller", with an "always" brick in true-pulse mode connected to a Python controller running this script when needed)

    Code:
    import bge
    
    scene = bge.logic.getCurrentScene()
    enemies = len([object for object in scene.objects if object.get('enemy', False)])
    
    if enemies == 0:
        print("this is the end")
    Make sure to put a property on your enemies objects with "enemy" as name and some value that will be evaluated to "True" (a boolean is the easiest way)
    Doing that has crossed my mind problem there is that having messages sent out on a pulse greatly impacts preformance. The other problem would be this wouldn't solve the issue as simce most enemies spawn at the sametime this beat would be sending the same message acrross all the same enemy types meaning it would still only register as one enemy.



  8. #8
    Hi,


    I'm not sure but I think your problem is with two different objects sending essentially the same subject over a very short period of time (resulting in a single read of messages).
    One thing you could try in python is checking how many messages were sent on the logic frame in concern.


    Counter code:
    Code:
    ...
    if messageSensor.positive and 'Died' in messageSensor.subjects[:]:
        count += messageSensor.frameMessageCount
    ...
    Enemy died code:
    Code:
    ...
    bge.logic.sendMessage('Died')
    ...

    I'm wondering if there is a better system I can use
    If you're looking for an alternative solution, I believe Daed's idea is great, because it can solve the multi-collision death problem, and it is efficient and relatively simple to implement, as in:

    Counter code:
    Code:
    ...
    globalDict = bge.logic.globalDict
    if 'scoreCounter' not in globalDict:
        globalDict['scoreCounter'] = 0
    ...
    Enemy died code:
    Code:
    ...
    globalDict['scoreCounter'] += 1
    owner.endObject()
    ...
    Also, please clarify how exactly you want your point system to work. In your beginning post you stated:
    when an enemy is killed it adds 1 to your score
    But then later, you said:
    I don't need the score to save and I don't need negative points, I just need it to tell the player wether or not they killed every enemy in the level
    If you are looking for a way to determine if every enemy has been killed, I think using globalDict could be a simple and efficient solution in this way:

    1) Initialize globalDict['numEnemies'] to however many enemies there are at the start
    2) Every time the empty adds an enemy, globalDict['numEnemies'] += 1
    3) Every time an enemy is killed, globalDict['numEnemies'] -= 1
    4) When globalDict['numEnemies'] is 0 or less, do something.

    But please, clarify what end goal will be. Thanks.

    Hope that helps.
    Last edited by Mirror|rorriM; 02-Sep-17 at 06:34.



  9. #9
    Originally Posted by Mirror|rorriM View Post
    Hi,


    I'm not sure but I think your problem is with two different objects sending essentially the same subject over a very short period of time (resulting in a single read of messages).
    One thing you could try in python is checking how many messages were sent on the logic frame in concern.


    Counter code:
    Code:
    ...
    if messageSensor.positive and 'Died' in messageSensor.subjects[:]:
        count += messageSensor.frameMessageCount
    ...
    Enemy died code:
    Code:
    ...
    bge.logic.sendMessage('Died')
    ...
    If you're looking for an alternative solution, I believe Daed's idea is great, because it can solve the multi-collision death problem, and it is efficient and relatively simple to implement, as in:

    Counter code:
    Code:
    ...
    globalDict = bge.logic.globalDict
    if 'scoreCounter' not in globalDict:
        globalDict['scoreCounter'] = 0
    ...
    Enemy died code:
    Code:
    ...
    globalDict['scoreCounter'] += 1
    owner.endObject()
    ...
    Also, please clarify how exactly you want your point system to work. In your beginning post you stated:

    But then later, you said:


    If you are looking for a way to determine if every enemy has been killed, I think using globalDict could be a simple and efficient solution in this way:

    1) Initialize globalDict['numEnemies'] to however many enemies there are at the start
    2) Every time the empty adds an enemy, globalDict['numEnemies'] += 1
    3) Every time an enemy is killed, globalDict['numEnemies'] -= 1
    4) When globalDict['numEnemies'] is 0 or less, do something.

    But please, clarify what end goal will be. Thanks.

    Hope that helps.
    Both are true I was just stating how I did it, so there are 15 enemies in a level lets say, so I have a text object that says 15 and another object that is an actual counter, the 15 is only there to tell the player how many enemies in total are present in the level. When an enemy dies it adds +1 to the counter therefore if you kill 15 enemies the counter should read 15.

    In regards to your other suggestion I don't really know coding as of now, once I wrap up this project I hope to take a course or something in the vain of learning it but for roght now thats not a priority. It would be extremely helpful to me if you could maybe give me a better more solid understanding of how to implement something like this.



  10. #10
    Both are true I was just stating how I did it, so there are 15 enemies in a level lets say, so I have a text object that says 15 and another object that is an actual counter, the 15 is only there to tell the player how many enemies in total are present in the level. When an enemy dies it adds +1 to the counter therefore if you kill 15 enemies the counter should read 15.
    Ok, but then globalDict is still something you would want to consider in storing both a score counter and the total number of enemies in the scene.

    In regards to your other suggestion I don't really know coding as of now, once I wrap up this project I hope to take a course or something in the vain of learning it but for roght now thats not a priority. It would be extremely helpful to me if you could maybe give me a better more solid understanding of how to implement something like this.
    Oh I see. In that case, I recommend this concept (which can be implemented in Blender):
    Have a system which stores both the score counter and the total number of enemies in the scene. A good first step would be to initialize (basically set) the starting values of the score counter and the number of enemies in the scene. The score counter adds 1 every time an enemy is destroyed, and the number of enemies counter is subtracted by one. Every time an enemy is spawned, the system (enemy or counter, etc.) adds 1 to the number of enemies counter. Both the score counter and number of enemies are accessible by enemies and whatever is keeping track of the score (for this concept, I will try to keep things simple. There are ways which can be harder to understand with limited programming knowledge but may be better for some purposes). Once the number of enemies counter reaches 0 (or less in some cases), you count up the score (or do something).

    Unfortunately, a lot of the functionality of the message sensor can only be accessed through code.
    Last edited by Mirror|rorriM; 02-Sep-17 at 13:56.



  11. #11
    Member WKnight02's Avatar
    Join Date
    Aug 2017
    Location
    Earth
    Posts
    129
    Originally Posted by awesreeker View Post
    Doing that has crossed my mind problem there is that having messages sent out on a pulse greatly impacts preformance. The other problem would be this wouldn't solve the issue as simce most enemies spawn at the sametime this beat would be sending the same message acrross all the same enemy types meaning it would still only register as one enemy.
    You can delay pulses, say to 1 each 20 logic frames, so that performance is not so bad. I thought you said the actual count wasn't relevant, if true, as long as there are enemies, sure they will send the same message, but when everyone is out, no more messages: then you can trigger the end of the level.

    On the other hand, you can use this little script, which count objects with a certain property:


    Code:
    import bge
    
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    owner = cont.owner
    
    # This var just count enemies with a property "enemy" set to True...
    enemies = len([object for object in scene.objects if object.get('enemy', False)])
    
    if enemies == 0 and not owner.get("lock", False):
        owner["lock"] = True # This ensure the rest is executed only once
        print("this is the end")
        # do whatever you need here
    EDIT: Is it me or the CODE tags do not really make code easily readable ...?



  12. #12
    Originally Posted by WKnight02 View Post
    You can delay pulses, say to 1 each 20 logic frames, so that performance is not so bad. I thought you said the actual count wasn't relevant, if true, as long as there are enemies, sure they will send the same message, but when everyone is out, no more messages: then you can trigger the end of the level.

    On the other hand, you can use this little script, which count objects with a certain property:


    Code:
    import bge
    
    cont = bge.logic.getCurrentController()
    scene = bge.logic.getCurrentScene()
    owner = cont.owner
    
    # This var just count enemies with a property "enemy" set to True...
    enemies = len([object for object in scene.objects if object.get('enemy', False)])
    
    if enemies == 0 and not owner.get("lock", False):
        owner["lock"] = True # This ensure the rest is executed only once
        print("this is the end")
        # do whatever you need here
    EDIT: Is it me or the CODE tags do not really make code easily readable ...?
    At the end of level there is a score screen, it say basically you killed (text object that recieves messages and increases by a value of 1 each time it recieves message) of this many enemies (a text object with a flat value with how many enemies there are in the level)
    When an enemy dies it sends a message to the text object with coding. So if you finish the level killing only 1 enemy the scoreboard will basically read something like {kills 1/15}
    The problem is if two enemies die at the same time that are the same enemy type they both send a message, that is they send two of the same message.
    but the text object recieving the message only reads the two messages as one. so even if you kill all the enemies it will read something like
    14/15 kills or 13/16 or something like that depending on how many enemies and how much it happens.


    Furthermore I don't know code and wouldn't know how to implement this... so basically it doesn't help.



  13. #13
    heres a basic example of how to use my code:
    Attached Files Attached Files
    System "IVAN" (rev 1.3b) - Win7 64bit - Blender 2.74:
    CPU- Intel i3-3220 3.30 Ghz | GPU- EVGA GTX 970 | RAM- GSkill Ares 16GB 1600 Mhz | MB- ASUS P8Z77-V LK



  14. #14
    Member
    Join Date
    May 2007
    Location
    somewhere north of Syracuse
    Posts
    431
    Here is a rather complex way. ('cuase I don't understand python)This code/blend was written by Monster. And modified slightly by me for this post.

    http://pasteall.org/blend/index.php?id=47806

    EDIT:

    so there are 15 enemies in a level lets say, so I have a text object that says 15 and another object that is an actual counter, the 15 is only there to tell the player how many enemies in total are present in the level. When an enemy dies it adds +1 to the counter therefore if you kill 15 enemies the counter should read 15.
    As a gamer, if I'm killing enemies, and the number 15 isn't changing along with the number of kills. I'm thinking more enemies are spawning.

    The number 15, should decrease, as the number of kills increase. The blend I posted, does that.
    Last edited by theoldguy; 15-Sep-17 at 05:20.



  15. #15
    Originally Posted by theoldguy View Post
    Here is a rather complex way. ('cuase I don't understand python)This code/blend was written by Monster. And modified slightly by me for this post.

    http://pasteall.org/blend/index.php?id=47806

    EDIT:



    As a gamer, if I'm killing enemies, and the number 15 isn't changing along with the number of kills. I'm thinking more enemies are spawning.

    The number 15, should decrease, as the number of kills increase. The blend I posted, does that.
    Well that complex way just saved my butt and was pretty easy to put in so thank you!



  16. #16
    Member Nemescraft's Avatar
    Join Date
    Mar 2016
    Location
    Somewhere on Earth
    Posts
    210
    I am glad you managed to solve it! Make sure you edit your first post, go Advanced, and put the [SOLVED] prefix on.
    My game project - Scroll Tanks



  17. #17
    Member
    Join Date
    May 2007
    Location
    somewhere north of Syracuse
    Posts
    431
    Well that complex way just saved my butt and was pretty easy to put in so thank you!
    Professor Monster is the one who should get the thanks. He wrote the code.



Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •