Empty Objects

I am making a game where you track a character to an object for interactions, but I am having a major error. When I go to call for several property values in an object so characters can “Interact” it keeps giving me an error saying that the argument of type ‘NoneType’ is not iterable. Now I sort of understand what this means, but I don’t understand why this occurs. However what baffles me even more is that somehow I know I had previously overcome this obstacle before with a virtually identical setup (Started with a normal cube, built a mesh, added game properties) However one object doesn’t come back as a NoneType, and my new object does. Now they both use the same script, and the same character has been used for testing. As far as I can see, the only difference is the mesh, and the names of the properties. I am not sure how I worked this out before, but if anyone can help, that would be great Thanks.


import bgeimport GameLogic
SCollisionR = "sCollisionR"
SCollisionC = "sCollisionC"
ATrackTo = "aTrackTo"
AState1 = "aState1"
AAfter = "aAfter"
def resource(cont):
    obj = cont.owner
    aTrackTo = obj.actuators[ATrackTo]
    sCollisionR = cont.sensors[SCollisionR]
    sCollisionC = cont.sensors[SCollisionC]
    resource = sCollisionR.hitObject
    aState1 = cont.actuators[AState1]
    aAfter = cont.actuators[AAfter]


    if sCollisionR.hitObject == None:
        print ("Broken")
        print(resource["houseHealth"])
    
    elif "Wood" in sCollisionR.hitObject:
        print("YES, wood")
        print(resource["Wood"])
        
        if resource["Wood"] <= 10:
            #obj["wood"] = obj["wood"] - obj["strength"]
            obj3 = obj.childrenRecursive["tools"]
            obj3["toolset"] = 0
            obj['steering'] = 0
            cont.activate(aState1)
            
        resource["Wood"] = resource["Wood"] - obj["strength"]
        print(resource["Wood"])
        obj["wood"] = obj["wood"] + obj["strength"]
        
    elif "Collected" in sCollisionC.hitObject:
        print ("Collected")
        obj3 = obj.childrenRecursive["tools"]
        obj3["toolset"] = 0
        obj['steering'] = 0
        cont.activate(aState1)
        
    elif "houseHealth" in sCollisionR.hitObject:
        print("YES, House")
        print(resource["houseHealth"])
        
        if health["houseHealth"] == 90:
            #obj["wood"] = obj["wood"] - obj["strength"]
            obj3 = obj.childrenRecursive["tools"]
            obj3["toolset"] = 0
            obj['steering'] = 0
            cont.activate(aState1)
            
        resource["houseHealth"] = resource["houseHealth"] + obj["buildingStrength"]
        print(resource["houseHealth"])

This code is being used to have units build/destroy houses and resources, as well as display their tools (Axes, pics, whatever) and send it between different states.

pretty simple, supposing the “worse case” , cont.sensors[SCollisionR].hitObject not hit nothing, so is None


def resource(cont):
    obj = cont.owner
    aTrackTo = obj.actuators[ATrackTo]
    sCollisionR = cont.sensors[SCollisionR]
    sCollisionC = cont.sensors[SCollisionC]
    resource = sCollisionR.hitObject          #     <-- resource = None 
    aState1 = cont.actuators[AState1]
    aAfter = cont.actuators[AAfter]




    if sCollisionR.hitObject == None: # statement True (so read the indentation) since   sCollisionR.hitObject is exactly None
        print ("Broken") # >>> Broken
        print(resource["houseHealth"]) # error since it treat the obj None as some dictionary or a class -> print(None["houseHealth"]) 



seem that is the variable resource that create confusion

If you define a variable that contains hitObject I suggest to use it. You can use the original source but it is confusing when this is mixed together.


resource = sCollisionR.hitObject
...
if sCollisionR.hitObject == None:
#vs
if not resource:

I think this is the reason, why you did not see the situation MarcoIT describes.

Variables with a number suffix are usually a sign of dirty code (obj3). I recommend to think about a better name. According to your code “tools” would be a good name ;).

Further more your code becomes long. I suggest to extract the single code blocks into separate functions.

E.g.


...
    sCollisionR = cont.sensors[SCollisionR]
    resource = sCollisionR.hitObject
    if not resource:
        dealWithMissingResource()
    
    elif "Wood" in resource:
        dealWithWood(resource)
        
    elif "Collected" in resource:
         dealWithCollected(resource)
        
    elif "houseHealth" in resource:
         dealWithHouseHealth(resource)

This way you can focus on each single (and small) task


def dealWithWood(wood):
        print("YES, wood")
        print(wood["Wood"])
        
        if wood["Wood"] <= 10:
            obj = getOwner()
            #obj["wood"] = obj["wood"] - obj["strength"]
            tools = obj.childrenRecursive["tools"]
            tools ["toolset"] = 0 
            obj['steering'] = 0  
            aState1 = cont.actuators[AState1]
            cont.activate(aState1)
            
        wood["Wood"] = wood["Wood"] - obj["strength"]
        print(wood["Wood"])
        obj["wood"] = obj["wood"] + obj["strength"]

Additionally it enables you to access objects when they are needed (e.g. you do not need any actuator when resource is None.)

What means 0 in property “toolset” or in “steering”? I suggest you define some “constants” with names similar to the brick names. Or even better use a string e.g. “ultimate toolset”. So it is easier to debug.

Just some thoughts

true , but can be a bit annoyng “regenerate” all dependences.
to avoid this can be used a set of properties (obj[“prop”]) where each property contain a value(usually string) that mean a precise “command”
for the reader that can be the bricks or another gameobject.

advantage , is (should be) a lot flexible .

disadvantage is that the command can be executed in the current frame (if read from python) or the next.
but this is not really a problem if you know it.
(secondly is not “extremely” fast)


def dealWithWood(own, wood):
    if wood["Wood"] <= 10:
        own["toolset"] = 0   # the message for the children "tools"
        own['steering'] = 0  # the message for the bricks (0=deactivate steering, 1=activate steering)
        own["STATE"] = "state2" # the message for bricks (if STATE == "state2" -> and -> setState(2))
    wood["Wood"] -= own["strength"]
    own ["wood"] += own["strength"]

or at least is my more recent experiment as design :wink:

Ok, So I fixed my script, however my “House” is still a NoneType. I know my guy is colliding with the object in the game engine, so it’s not that he is colliding with nothing. But I don’t understand why this is still an issue. The code below is what I “fixed” with it, by updating it with Monster’s suggestions. I have two separate objects that have properties I can change and use, but I can’t access these ones. (And it works if I duplicate the objects and recreate the house with the same properties and settings) I don’t know if this is the cause, but the only differences in the meshes is that the House has separated parts, and the others don’t. But I don’t think that would cause that. If someone can tell me what cases other than No Object causes something to be returned as a NoneType object, maybe I can figure out what is going on.


import bgeimport GameLogic
SCollisionR = "sCollisionR"
SCollisionC = "sCollisionC"
ATrackTo = "aTrackTo"
AState1 = "aState1"
AAfter = "aAfter"
def resource(cont):
    obj = cont.owner
    aTrackTo = obj.actuators[ATrackTo]
    sCollisionR = cont.sensors[SCollisionR]
    sCollisionC = cont.sensors[SCollisionC]
    resource = sCollisionR.hitObject
    aState1 = cont.actuators[AState1]
    aAfter = cont.actuators[AAfter]
    
    if "Wood" in resource:
        dealWithWood(resource)
    
    elif "Collected" in resource:
        dealWithCollected(resource)
    
    elif "houseHealth" in resource:
         dealWithHouseHealth(resource)
         
def dealWithWood(wood):
    print("YES, wood")


    if wood["Wood"] <= 10:
        cont = bge.logic.getCurrentController()
        obj = cont.owner
        heldTool = obj.childrenRecursive["tools"]
        heldTool["toolset"] = 0
        obj['steering'] = 0  
        aState1 = cont.actuators[AState1]
        cont.activate(aState1)
    
    cont = bge.logic.getCurrentController()
    obj = cont.owner
    wood["Wood"] = wood["Wood"] - obj["strength"]
    print(wood["Wood"])
    obj["wood"] = obj["wood"] + obj["strength"]
    
def dealWithCollected(collected):
    cont = bge.logic.getCurrentController()
    obj = cont.owner
    print ("Collected")
    heldTool = obj.childrenRecursive["tools"]
    heldTool["toolset"] = 0
    obj['steering'] = 0
    aState1 = cont.actuators[AState1]
    cont.activate(aState1)
    
def dealWithHouseHealth(houseHealth):
    print("YES, House")
    print(resource["houseHealth"])
        
    if health["houseHealth"] == 90:
        cont = bge.logic.getCurrentController()
        obj = cont.owner
        heldTool = obj.childrenRecursive["tools"]
        heldTool["toolset"] = 0
        obj['steering'] = 0
        aState1 = cont.actuators[AState1]
        cont.activate(aState1)
            
    houseHealth["houseHealth"] = houseHealth["houseHealth"] + obj["buildingStrength"]
    print(resource["houseHealth"])

I just assumed it all went into one script, but please do tell me if I am wrong.

Also, the [“Steering”] property is the “block” I am using to control which units are, and are not “Selected”. and the [“Tools”] tell what mesh the character’s tool is supposed to display, i.e. an axe = 1, or nothing = 0.

I think we need a demo .blend. According to your description there are dependencies to the concrete situation of the scene.

It sounds like you should read these threads:
Double execution of a python controller
Sensors - A Word on Pulses

edit:

There are still some issues in your code:
you use “Wood” and “wood” . I see you use one for the resource and one for the worker.
I suggest

  • to stay consistent lower case with property names.
  • to use different property names e.g. worker[“wood”] and wood[“amount”]

edit2:
just now the worker seems it can get more wood as available (wood < 10)

This might become handy to you. It encapsulates some often used access methods.

bgeUtils.py:


from bge.logic import getCurrentController

def getOwner():
   return getCurrentController().owner

def getActuator(actuatorName):
   return getCurrentController().actuators[actuatorName]

def activate(actuatorName):
   getCurrentController().activate(actuatorName)
   
def deactivate(actuatorName):
   getCurrentController().deactivate(actuatorName)

def allSensorsPositive():
   for sensor in getCurrentController().sensors:
      if not sensor.positive:
         return False
   return True

your code would look like this:


...
def takeResource(): 
    if not allSensorsPositive():
       return

    resource= getActuator(SCollisionR).hitObject 

    if "Wood" in hitObject:
        dealWithWood(resource)
    ...

def takeWood(wood):
    worker = getOwner()

    amount = wood["wood"]     
    chunkSize = worker["strength"]
     
    if amount &lt;= chunkSize:
        chunkSize = amount
    
    remaining = amount - chunkSize
    wood["Wood"] = remaining
    worker["wood"] += chunkSize       

    if remaining &lt;= 0:
        stopTakingWood()
		    
def stopTakingWood():
    heldTool = obj.childrenRecursive["tools"]
    heldTool["toolset"] = 0
    obj['steering'] = 0 
 
    activate(AState1)
    # I think this is too much unrelated knowledge
    # this code should simply notify the worker to stop taking wood
    # how to do that - should be somewhere else
    # e.g. in state 1

Seeming as I can’t get the attachments on here to work, I am using a link to a website I have seen several people suggest. I have scrapped down the file to what is required to run the actual game (i.e. no extra random meshes and codes)

http://www.pasteall.org/blend/25653

I have continued to try to look into what exactly causes a nonetype object to occur without much luck. Maybe if you mess with my game you can find a solution. And no, the character is collecting the right ammount of wood because I am using a negative pulse mode inverted on my collision, and that is causing it to collect the right amount.

I’m a bit confused what should happen when.

I can see there are clickable buttons. When I click them I get a house to place and a tree to place. This works pretty fine. But I do not see how the mentioned code fits into this. What do I need to do?