I have a script that is dealing with a list of ships that are racing. The number of racers can be selected at the start of the race.
Now the fun begins!
During the race each ships lap is recorded- and so as each ship passes the start/ finish line a string will print the result. The main problem I am trying to get around is that ships can be destroyed, so the way in which this information is recorded needs to be flexible.
This is the code:
try:
ship[0] is not None
ship_in_race = bge.logic.getCurrentController().sensors["Collision"].hitObject
if collision.positive and ship_in_race == ship[0] and ship[0]["lap"] > 0:
ow["ship_1_lap_time"] = ship_in_race["race_time"]
print(str("First place:") + str(" ") + str(ship_in_race) + str(" ") + str("lap time:") + str(" ") + ow["ship_1_lap_time"])
except IndexError:
print("ship was not selected at race start or has been destroyed")
As you can see, the script creates a reference to any ship that activates the collision sensor, and checks to see if that object in the list exists. There are eight properties that are used by the controlling object to store up to eight lap times.
If the racer does not exist, it generates an exception (in the case of a ship that is destroyed or is not selected) and one of the eight lap time properties is not written to.
Now my question: is there a way to get around using a try / except style approach to this? In essence is there a way of stopping an ‘out of index’ error without ignoring it like I am doing here?
If I do choose to keep the try /except approach, would it cause any problems later along the line?
By what I saw you really don’t need to use lists, which are for things which need to be checked into a given order; use a python dict, dicts are for when you do not need to check things into a given order, as you’re just storing the racer state and laps; dicts are easier to use also.
I would store available racers into that dict, if the car isn’t selected, it shouldn’t even be part of the dict(your game should load only what it needs); if the car is destroyed, you could just replace the object pointer in the dict by a None, ‘destroyed’ or whatever you think would be explanative for that state, and if it should forgot about cars which are destroyed(including laps), you should delete the dictionary entry regarding that racer when that happens.
Dicts are like:
racers = {}
racers['racer_1'] = [car_obj, [put laps here]]
I would use a for loop to check for each racer in that dict if it hit that collision point you talked about.
I think also you could write for the print instead:
You could check gameobject.invalid to see if the game object has been removed.
Better yet, don’t get in the situation in the first place. Every time you delete a game object, make sure you also remove any references to it elsewhere in your code.
And in my opinion, try / except is totally acceptable for use although it does make debugging a bit harder.
try…except is quite a good message to deal with expected error processing. It is a well known method. E.g. in Java this is called Exceptions.
The advantage is the processing can return multiple different error conditions rather than mixing it with “good case results”.
It is strongly advised to keep the caught statement as small as possible.
This is because after an error the succeeding code will not be executed as Marco mentioned. This can produce to a broken program state (e.g. half of the variables are set with new values, others remain with old values).
Additional multiple statements can produce the same error. With one try…except it is impossible to tell which one was the error cause.
All caught errors should be expected. The error handling should be specified. Error handling can produce errors too.
Its an interesting problem as I am thinking of trying to get the chunk of code above to be generated per ship, but it seems a lot of hassle when I can just have eight pre-coded chunks. I was considering globalDict but I would still need to order the ships as they cross the line…shame as it would be easier to do (in theory).
As an amendment, I have moved the ow[“ship_1_lap_time”] into a Python created variable within the code block as a start (so unless the try /except is true the property is not generated…or I hope thats whats happening…). This is the code below:
try:
ship[0] is not None
ow["ship_1_lap_time"] = 0.0 # code generated property / variable
ship_in_race = bge.logic.getCurrentController().sensors["Collision"].hitObject
if collision.positive and ship_in_race == ship[0] and ship[0]["lap"] > 0:
ow["ship_1_lap_time"] = ship_in_race["race_time"]
print(str("First place: ") + str(ship_in_race) + str(" lap time: ") + str(round(ow["ship_1_lap_time"],2)))
I suppose I could make a function that does this… maybe some homework foe me to do!
i hope to not adding confusion…
regard the try…except , and the “jumping line”:
suppose which “target” can be a reference wrong for some reason.
try/except can avoid other error
in this case "time" is assigned corrrectly(with or without reference wrong by "target")
#################
try:
own.worldPosition = target.worldPosition
except:
pass
own["time"]+=1
#################
without try/except, if "target" is a reference wrong also "time" is not assigned (so ,can due other error)
#################
own.worldPosition = target.worldPosition
own["time"]+=1
#################
Thanks for the follow up, Marco, as I was dealing with this (I re-wrote my original code slightly to make it more/ slightly logical) my daughter has started teething…so I’ve had to deal with that!
Eventually I will have to write from scratch as I have been trying to add more functionality to the timer- things like lap times, total race time, time difference between racers etc. I’ll have to send you what I’ve done so far (more flying cubes!!) so you can take a look.
I read something above about using dictionary and lists? If you have the need for the easiness of dictionaries and the ordering power of lists, there is the python OrderedDict module.