Skip to content
Tags

43. Learning from popping balloons

April 26, 2013

This post is based on a forum question, and discusses how to manage physics objects, especially how to destroy them. I’ve done it in the form of interactive debugging, showing how to fix problems and improve the result one step at a time. And, as always, there are interesting things to learn.

It was a simple enough question. A user wanted to make a very simple game, touching the screen to fire bullets from left to right, to pop coloured balloons that floated up the screen.

My bullets keep disappearing!

However, on writing code to fire bullets (and before making any balloons), the first problem was that each time he touched the screen, it created a new bullet, and the old one disappeared, so there was only ever one bullet. Oh, and they were falling to the ground rather quickly.

So here was the initial code. Can you see the problem?

function setup()
    bullet = physics.body(CIRCLE,10)
end

function touched(touch)
    x = touch.x
    y = touch.y
    if touch.state == BEGAN then --create bullet
        bullet.x,bullet.y = x,y
        bullet.linearVelocity = vec2(1000,0)
        bullet.restitution = 0
        bullet.type = DYNAMIC
    end
end

function draw()
    background(0, 0, 0, 255)
    --draw bullets
    pushStyle()
    fill(255, 255, 255, 255)
    ellipse(bullet.x,bullet.y,10)
    popStyle()
end

The problem of falling toward the ground is a gravity thing. Either you need more speed, or less gravity, but in a simple game, maybe it’s just easier to turn gravity off. So we put this line in setup

    physics.gravity(0,0)

The next problem is that there is only one bullet defined in setup. When you touch the screen, the touched function resets the x,y position etc for the bullet, but there is still only one bullet.

So we need to create a table to hold more than one bullet, and the draw function needs to draw them all. And, if they leave the screen, we need to delete them.

First, we need to add this in setup

    bullets={}

Then we need to add one line to the touched function, near the end

    function touched(touch)
    x = touch.x
    y = touch.y
    if touch.state == BEGAN then --create bullet
        bullet.x,bullet.y = x,y
        bullet.linearVelocity = vec2(1000,0)
        bullet.restitution = 0
        bullet.type = DYNAMIC
        table.insert(bullets,bullet)   -- NEW
    end
end

and some code in draw

    --ellipse(bullet.x,bullet.y,10) --previous code
    for i,b in pairs(bullets) do --draw all bullets
       ellipse(b.x,b.y,10)
    end

Hold on, that doesn’t solve the problem. We still only have one bullet at a time, which gets replaced when you touch the screen.

The problem is that when we redefine the bullet in the touched function, and then add it to the bullets table, we are adding the original bullet. Remember that for anything more complicated than a string or a number, like a table, a class, or a physics object, if you put it into a variable (like bullet), all that is stored is the memory address (or ‘pointer’) which tells Code where to find the object details. So when you add bullet to the table over and over, you are adding the same memory address to the table each time. and all of them point to the original bullet.

Clearly, we need to create a new bullet each time we touch the screen. So we’ll delete the bullet we created in setup, and do it all in touched, like so.

    function touched(touch)
    x = touch.x
    y = touch.y
    if touch.state == BEGAN then --create bullet
        local bullet = physics.body(CIRCLE,10)  -- NEW
        bullet.x,bullet.y = x,y
        bullet.linearVelocity = vec2(1000,0)
        bullet.restitution = 0
        bullet.type = DYNAMIC
        table.insert(bullets,bullet)
    end
end

Note that we make the bullet local, so that a new one is created each time.

OK, so now we have multiple bullets. The code so far is here, if you want to see.

And if you’re wondering why we don’t need to calculate new x,y positions for the bullets, that’s the magic of physics objects. You give them an initial velocity, and the physics engine automatically updates x,y all the time for you. The only problem is that physics objects are invisible, which is why you need to draw a little circle exactly on top of the invisible physics object, so the user can see it.

But we forgot something. We need to delete the bullets when they leave the screen. So we’ll modify draw…

    for i,b in pairs(bullets) do  -- i is the index (position in the table), b is the bullet
       if b.x>WIDTH or b.y>HEIGHT then --if outside screen..
           table.remove(bullets,i)  --remove the bullet from the table
       else ellipse(b.x,b.y,10) end
    end

That seems to be working ok.

Adding balloons

The user managed this nicely, with a little class. Note the use of “or” to set default values where parameters are not provided.

Ballons = class()

function Ballons:init(x,y,r,c,lv)
    self.radius = r or math.random(25,50)
    self.ballon = physics.body(CIRCLE,self.radius)
    self.ballon.x = x or math.random(WIDTH/2,WIDTH)
    self.ballon.y = y or math.random(-HEIGHT/2,-self.radius)
    self.ballon.linearVelocity = lv or vec2(0,math.random(100,150))
    self.ballon.colr = c or color(math.random(255),math.random(255),math.random(255),math.random(100,150))
end

As an aside, the balloons are a nice pastel shade simply through using a low value for alpha, the last colour element, which makes them somewhat transparent, too.

We need to use this class to create some balloons. The user did this in setup.

    ball = {}
    for i = 1,5 do
        ball[i] = Ballons()
    end

and drew the balloons in draw. I won’t show that code, as it is very simple. The final result is here. The balloons look quite good, and the bullets bang into them, but don’t pop them yet.

Popping balloons

So now we come to the tricky part. We want to pop a balloon when a bullet hits it, and also make the bullet disappear.

There is a collide function built into Codea, that triggers every time two physics objects touch. It gives you the two objects as bodyA and bodyB. Now, how will we know what type of objects these are, and which ones they are, so we know what to delete?

The user tried using the info property of physics objects, to store information about type of object, so he could get the information directly from bodyA and bodyB, but this is quite tricky. In the end, I think a brute force approach is as good as any, like so

function collide(contact)
        for i,b in pairs(ball) do
            if b.ballon==contact.bodyA or b.ballon==contact.bodyB then
                for j,bb in pairs(bullets) do
                    if bb==contact.bodyA or bb==contact.bodyB then
                        -- DELETE BALL, BULLET
                    end
                end
            end
        end
end

So the code compares all the balls against bodyA and bodyB, and if there is a match, it compares all the bullets against bodyA and bodyB. If that matches too, then clearly a bullet has hit a balloon, and we must delete them both. We know exactly which balloon and bullet to delete, too.

But this is NOT so easy.

Deleting physics objects

To delete a bullet or balloon, we need to delete the physics objects themselves. The Codea documentation tells you that this is a two step process

    -- destroy should be used before
    -- setting a body to nil
    circle = physics.body(CIRCLE, 100)
    circle:destroy()
    circle = nil

…and our objects are also in tables, so we need to think that through.

You would imagine you can delete the physics objects in the collide function above, but if you do, you will find Codea crashes or hangs. It literally breaks. This is because until you leave the collide function, Codea hasn’t finished with bodyA and bodyB, and destroying either of them totally confuses things. (Details here, if you are interested).

So how can we delete the balloon and bullet? There is really only one place to do it, in the draw function. So we need a way of telling the draw function that there is stuff to delete.

I chose to do it by putting this line in collide

collision={i,j}  --store index number of balloon and bullet

i is the index of the colliding balloon, ie it is ball[i], and j is the index of the colliding bullet.

Then, in draw, I put this…

if collision~=nil then    --ignore if not set
        --two step process, destroy physics object, then remove from table
        --ball first
        ball[collision[1]].ballon:destroy()
        table.remove(ball,collision[1])
        --bullet
        bullets[collision[2]]:destroy()
        table.remove(bullets,collision[2])
        collision=nil  --so we don't try to do the same thing twice!
    end

note collision[1] and collision[2] are simply the “i” and “j” from the collide function, telling us which balloon and bullet to delete.

Oh, and I realised we weren’t deleting the physics objects for the bullets that flew off the screen, so I added a line in for that.

Note – if we had multiple balloons or bullets to delete, we would need a table to which we could add as many as we wanted, then the draw function could loop through them.

The final code is here, complete with a nice popping sound added by the user.

All of this just shows that quite innocent and simple sounding questions can lead you down quite interesting paths…

Advertisement

From → animation, Games, Physics

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: