How to end the round in a building destroyer game.

I really could not think of a good title for my issue, so I hope I can explain it better.

So, I've been working a building-destroyer game as a huge testing ground to learn the BGE. Think of the game as a 3D Angry Birds, with a cannon instead of birds, and no pigs. The issue I'm running into is in trying to figure out when to consider the player's round "finished" so I can post their score. 
You have 3 cannonballs, so the best way I can think to implement a round finish is by first waiting until all 3 cannonballs have been fired. Then, I begin measuring the velocities of every component of a building. When a component's velocity falls below 1-ish, I add a score to a "win trigger" integer value. When that value hits a certain number (say 100) the round finishes. 
Essentially, I'm counting each component as they come to a full stop. When all of them have, the round ends. However, I want to have players be able to use less cannonballs to get a better score. The problem is, if I don't use the last cannonball as a marker, I won't know if the player is done firing yet. 
Is there a better way to implement round ending in a game like this? Should I use a timer somehow? I tried think of how Angry Birds did it, but they were able to use the pigs' deaths, or lack of deaths, as markers for when a player's round was over. I can't. 

I didn’t include the .blend file, because I highly doubt you would find it particularly useful, as it’s a huge unorganized slew of tests and ideas I have for the BGE. I did, however, post a picture just to show you what “components” I’m talking about. That tower is made up of a bunch of rigid body bricks, similar to KEVA planks.



Any ideas on a solution would be very, very much appreciated. :smiley:

It sounds like you need a more well defined definition of what it means to destroy a building. Then your logic to end the round is simply:
“if allBuildingsDestroyed() or outOfAmmo()”.

It looks like your building is just a collection of bricks put together. Perhaps a building is destroyed when you have caused some percentage of the bricks to be knocked from the building?

Count how many buildings that are destroyed and to end the round. Use message sensors and message actuators to do it.

I’m guessing that you want to figure out the amount of damage done to a single building?

The way I see it, you need to count components that are no longer in their right place (their original starting position in the structure).

So, given a list of components, you do this just before the first shot is fired:


for c in components:
    c["start_pos"] = c.worldPosition.copy()

To check how many bricks are out of place:


count = sum( [1 for c in components if c["start_pos"] != c.worldPosition] )

if count > count_win:
    end_level()

Or something along those lines.

Thanks for all the replies!

@Goran: I didn’t know there was a start position property on objects. That should work great. Two questions though: first, just how accurate is the “start_pos” property, because Blender’s physics simulation causes the bricks to shake a bit. This is why I used a range instead of 0 to make a limit on the velocity. Secondly, is it possible to add every object in a group to a list in Python, because I would rather not individually add each brick to the ‘components’ list.

I posted a reply earlier, don’t know what happened to it…
Goran, your solution should work perfectly, but is there any way I can get all the brick objects into the ‘components’ list without manually filling a list? They are all named “Cube.xxx” up to .111, and they’re in a group. Is there a way I could use a loop to put them in a list, or ideally, can I just put the entire group in a list? Similar to “bpy.data.groups” but in the BGE.

There is no “start_pos” property, but you can crate arbitrary properties on objects, and that’s what I did in the example above.

Accuracy is the same as for worldPosition itself - the full spectrum provided by a 32-bit float. And yes, that is likely to be problematic, because even if you destroy one small portion of the building, that’s very likely to move other components, ever so slightly, but it would be enough to make them effectively “out of place”.

To get around that, you can subtract one vector from another, and then check the magnitude of the resulting vector:


out_range = 0.2
deltas = [c.worldPosition - c["start_pos"] for c in components]
count = sum( [1 for d in deltas if d.magnitude > out_range] )

To get a list of components, if there’s nothing else named Cube.whatever, you can just do this:


components = [x for x in scene.objects if x.name[:4] == "Cube"]

If there’s a whole bunch of other objects that start with “Cube”, and the numbers really matter:


def is_component(o):
    sp = o.name.split('.')
    if len(sp) == 2:
        name, num = sp
        if num.isdigit():
            return name == "Cube" and int(num) <= 111
        
components = [o for o in scene.objects if is_component(o)]

Hope that helps.

Or you could always be lazy and just say ‘if X% of the bricks are below Y height, then win().’ or whatever.

Ha, yeah Goran, somehow I completely missed the first half of your post, where you created the “start_pos” property. Thanks for the tips on getting all the components in a list… I really need to read up more on string indexing. I have one question though: why do you put an ‘x’ at the beginning of “components = [x for x…”? I never really do commands inside of variable assignments, so I don’t understand that syntax.
@dragonspammer: xD Yeah I could do that, but then there would be no difference between one player’s score and another, as long as they won. I want a score-based reward system.
Thanks for all the suggestions guys.