Throwing Objects! Rewrite without Try/Except??

throw following code block is to ‘throw’ an item from one actor object to a target object stored in the list target.

the thrown object gets .addObject’d and then stored in string type game prop in a one-time run function outside and before the following code block is run.

Please look in the code block for the line of #'s to see the comment and my problem. See if you can solve or rewrite the idea

The other rules: can’t store the item in the global dict and cant retrieve the item with list comprehension or a for loop

gd = logic.globalDict    
    actor = battle_functions.get_actor_obj() <- a kx game object
    target = gd[actor['battleID']+'_target'] < - a list of kxgame objects
    
    
    
    
    thrown = actor['thrown_item'] < a kx game object inside of a string type game prop
    


    
    
    try:
        logic.getSceneList()[0].objects['CameraParent'].alignAxisToVect(Vector(logic.getSceneList()[0].objects['CameraParent'].getVectTo(thrown)[1]),1)  # look at the camera
        if actor.children[0].getActionFrame(2) == actor.children[0].actuators['Throw'].frameEnd:    
            thrown.removeParent()  # take from parent in order to apply velocity on the correct frame
                    
            vect_to_target = thrown.getVectTo(target[0])[1]  
            thrown.alignAxisToVect(vect_to_target,1) # align to the target kx game object
            thrown.setLinearVelocity(vect_to_target*48,1) # set velocity towards it
            actor.children[0].stopAction(2)  # stop throw animation
        
                    
        distance = thrown.getDistanceTo(target[0])
        print(distance)
        if distance < 10: # if its close enough to the target kx game object
            thrown.endObject()  ######      THIS is where the problem occurs because the game prop is converted back to string type once the KX game object that was stored in it disappears
                     
    except:                 
                              
# this stuff is for functions that are called afterward, but i only thought to put them in an exception

        if logic.getSceneList()[0].objects['DefaultCamera']['action_timer'] > logic.getSceneList()[0].objects['DefaultCamera']['action_time'] -1:
            logic.getSceneList()[0].objects[str(actor)+'WM'].setVisible(True)                
            logic.getSceneList()[1].objects['CameraHUD']['throw_item'] = False
            logic.getSceneList()[0].objects['DefaultCamera'].timeOffset = 40.0
            
            return True

In general you better never use unspecified “except:” unless you are dealing with ALL possible errors.

The reason is simple. You silently hide the fact that there was an unexpected error. This will result in missing operations and therefore inconsistent state without telling about it. Typically you get other errors and unexpected behavior without any indication why that happens.

Errors are not bad. They tell you that there is a situation you did not cover by your code.

Catching specific errors means you know about the situation and you know how to deal with it. Even here you have to be careful not to catch errors that you did not expect. That can happen when the try: block is too large and there are several different situations that throw the same error.

Now, this might sound a bit abstract to you. So here is an example:

Catching Unspecified Error - Eexample
imagine you mis-spelled the line


distance = thrown.getDistanceTo(target[0])

instead of getDistanceTo you wrote getDistance

On the first sight the code looks fine. But getDistance does not exist and calling it throws an AttributeError.

Unfortunately you catch all errors including AttributeError, silently ignoring them. You will not get any information that there is a spell error. I hope you see this is not a comfortable situation, especially when someone else is playing the game.

Catching specified Error - Eexample
Now lets solve that by catching expected errors. Lets assume that “thrown” might have getDistanceTo or might not (different object types). You know about that and you want to deal with that situation. Typically there are several solutions here is one that expains the try:except: clause a bit more.

We expect AttributeError and provide a solution for this situation:


try:
    distance = thrown.getDistanceTo(target[0])
except AttributeError:
    distance = 0

Now imagine this code:


try:
    thrown.doOtherThings()
    distance = thrown.getDistanceTo(target[0])
except AttributeError:
    distance = 0

It works similar as before. But here we can’t tell what is the cause of the attribute error (line 2 or line 3). Even worse: the attribute error can be somewhere doOtherThings().

This means the try block is too large, dealing with too many situations.

Therefore my advice:

  • when using except: specify what you expect
  • when using except Error: make try as small as possible
  • let unexpected errors provide as much information as possible for later investigation. The standard output is pretty good.

Code review

In general, your code is way to complex. I’m sure you understand it right now. But will you understand it after two weeks not looking at it?

Why does the code deal with so many objects? Why does the code deal with several scenes? What is the relation between the objects? What should the code do?

I do not know why you have the try: except here. As it is right now it makes not much sense to me. There can be errors anywhere in the try: block.

Could it be you want to deal with what is often called “init”?

What do you want to achieve?
As far as I understand you have:

  • an (throwable) object that is parented to
  • the (source) object
  • a (target) object somwhere

You want
Event A (please specify)

  • unparent throwable from source
  • calculate linear velocity (towards a target object)

I realized what my question should have been:

I want to have a line that is something like this:

If type(variable) is bge.types.KX_GameObject:

Is there a right way to write that above code? Because it doesn’t seem to be working for me.

That should work, but it’s not pretty.
Usually, you’re better checking if it’s an instance of (isinstance(var, Type)), which includes cases where var is a subclass of type.

However, in game-code you should rarely if ever need to do this. Better split things up more.

Thank you for the reply! Another problem I have is the error:

Type: KX Game Object, expected a Kx Game object… why does this happen?!

To add to that, when I check the type with that if statement

If KX Game object

it doesn’t return positve/

But if i print (type(object)) It prints Kx Game object

** the reason its happening is im trying to store an kx game object in a string type game property on an object, which i thought was just a dictionary value, so i figured it could store a KX Game object for a bit, then just make it an empty string the code tries to call it as a KX game object again



if isinstance(target,bge.types.KX_GameObject):
    if not target.invalid:
        #Do stuff


Don’t store anything but a string in a string property. It makes it confusing for you later on, and others, when looking at the file. You can create properties at-runtime even if they’re not in the properties panel of the game object. Do that - treat the gameobject like a dict.

How should a KX_GameObject become positive? Positive is an attribute of an SCA_Sensor. Better check sensors for positive rather than KX_GameObjects. It seems you simple confused the usage of your variables:


if sensor.positive:

A property is of the type of the value it contains. There is no problem to do something like this:



gameObject = controller.owner

owner["property"] = gameObject
owner["property"] = "string"

Due to the dynamic programming language it is up to you what you store in the property. The compiler does not care. The price is that you will get runtime errors when the property contains unexpected values.


value = owner["property"]
value.worldPosition # will fail if the value is None or a string

As agoose77 wrote, better do not mix different value types. You will not become happy with that as you need to do type checking all the time.


value = owner["property"]
if isinstance(value, KX_GameObject):
  dealWithGameObject(value)
elif isinstance(...

There are situations when you need this, but it is not a typical situation. To avoid confusion you can choose a descriptive property name.


owner["targetName"] = "Cube"
owner["targetObject"] = scene.objects["Cube"]
owner["maxAmount"] = 100

I usually tend to have short names on external properties (= properties visible at the GUI) and longer more specific names on internal properties (= properties created and set by Python = not supposed to be used by the GUI).


PROPERTY_TARGET_NAME = "target"
INTERNAL_PROPERTY_TARGET = "_internal.target"


...
targetName = owner[PROPERTY_TARGET_NAME] # I expect a string
target = scene.objects[targetName] # otherwise this fails
owner[INTERNAL_PROPERTY_TARGET] = target # I create internal properties via Python

The short name avoids the user to type much at the GUI (reduces possible typing errors).
The long name is more specific where I need it.

I create “constants” for two reasons:
A) I show the possible properties right on top of the code (readability)
B) It is easy to change the property name without digging through the complete code (maintenance).