Playing with balls

This is the first project I have worked on in Codea. It’s a simple project that shows balls and stuff. :>


--# Main
-- Ball Test 3 (complete)

function setup()
    
    parameter.boolean("add", false) -- For adding balls
    parameter.boolean("stat",true)  -- Makes a newly made ball move
    parameter.boolean("pchange",true)   -- Set to false if you don't want to change the ball you want to move
    parameter.boolean("psize",false)    -- Set to true if you want to change the size your ball
    parameter.integer("pspeed",0,10,1)  -- The amount of force exerted on your ball is multiplied by this value + 1
    parameter.integer("size",1,100,30)  -- Sets the size of a newly made ball or your ball
    parameter.action("Stop Player Movement", function() -- Stops the movement of your ball
        for k,v in pairs(b) do
            if v.player then
                v.linearVelocity = vec2(0,0)
            end
        end
    end)
    parameter.action("Stop all ball movement", function() -- Stops the movement of all balls
        for k,v in pairs(b) do
            v.linearVelocity = vec2(0,0)
        end
    end)
    
    b = {}  -- Store all the bodies here
    
    -- Walls
    local floor = physics.body( EDGE, vec2(0, 0), vec2(WIDTH, 0))
    floor.restitution = 1.1
    
    local left = physics.body( EDGE, vec2(0, 0), vec2(0, HEIGHT))
    left.restitution =  1.1
    
    local right = physics.body( EDGE, vec2(WIDTH, 0), vec2(WIDTH, HEIGHT))
    right.restitution = 1.1
    
    local ceiling = physics.body( EDGE, vec2(0, HEIGHT), vec2(WIDTH, HEIGHT))
    ceiling.restitution = 1.1
    
    physics.gravity(0,0)    -- Tabletop physics
    physics.continuous = true   -- Balls don't pass through walls if they get too fast
    
    table.insert(b,floor)
    table.insert(b,left)
    table.insert(b,right)
    table.insert(b,ceiling)
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(40, 40, 50)
    
    -- This sets the line thickness
    strokeWidth(2)
    
    -- Do your drawing here
    for k,v in pairs(b) do  -- Draws the balls
        drawBodies(v)
        if v.player and psize then  -- Changes the size of your ball if psize
            v.radius = size
        end
    end
    
    if explode then -- Shows the last contact / Nice effects
        sprite("Space Art:Red Explosion", exp.x, exp.y, exps, exps) -- exps is the energy of the impact
    end
end

function touched(touch)
    t = vec2(touch.x, touch.y)
    if touch.state == BEGAN then
        if add then -- Makes a ball if add
            makeCirc(t.x,t.y)
        else
            for k,v in pairs(b) do
                if v.shapeType == CIRCLE and not pcheck(v) and v.player then
                    v:applyForce((t-v.position)*(pspeed+1)) -- Applies force
                end
            end
        end
    end
end

function collide(contact)
    if contact.state == BEGAN then
        exp = contact.position
        exps = contact.normalImpulse
        explode = true
        tween.delay(0.1, function()
            explode = false
        end)
        sound(DATA, "ZgNAFQBJQEBAQEBAAAAAAAAAAAC5ZIg+AABAf0BAQEBAQHJx")
    end
end

function makeCirc(x,y)
    local a = physics.body(CIRCLE,size)
    a.x, a.y = x, y
    a.restitution = 1
    a.interpolate = true
    a.player = false
    if not stat then
        a:applyForce(vec2(math.random(-1000,1000),math.random(-1000,1000)))
    end
    table.insert(b, a)
end

function drawBodies(obj)
    pushStyle()
    fill(255, 0, 0, 0)
    stroke(25, 255, 0, 165)
    if obj.shapeType == EDGE then
        line(obj.points[1][1],obj.points[1][2],
        obj.points[2][1],obj.points[2][2])
    elseif obj.shapeType == CIRCLE and obj.player then
        pushStyle()
        noStroke()
        fill(0, 255, 180, 255)
        ellipse(obj.x,obj.y,obj.radius*2)
        popStyle()
    elseif obj.shapeType == CIRCLE then
        ellipse(obj.x,obj.y,obj.radius*2)
    end
    popStyle()
end

function pcheck(obj)    -- Checks if a tap is inside a ball
    if pchange and t:dist(obj.position)<=obj.radius then
        for k,v in pairs(b) do
            v.player = false
        end
        obj.player = true
        return true
    else
        return false
    end
end

Please show ways to make it better. :slight_smile:

Nice work for a first project

--instead of 
if tx >= obj.x - obj.radius and tx <= obj.x + obj.radius 
    and ty >= obj.y - obj.radius and ty <= obj.y + obj.radius 
    and pchange == true then

--try this
--dist is the distance between two vectors (points)
if pchange and vec2(tx,ty):dist(obj)<=obj.radius then

It’s actually the 3rd version of the project. The first project only has the creation of balls and the second one is just a fancier version of the first.

I don’t understand the code you just posted.
By the way, your ebook helped me a lot :slight_smile:

Edit:
I understood it but it gives me an error.

Edit2:
Eureka! Apparently, it doesn’t work when you just put obj inside.

    if pchange and vec2(tx,ty):dist( vec2(obj.x, obj.y) )<=obj.radius then

Just updated the code.

ah, yes, I overlooked that obj wasn’t a vec2

it’s worth looking at the other vector functions in Codea, they can be very helpful. If you make your position variables vec2’s, then it makes using those functions a little easier (and your code a little tidier)

--if your position is t [=vec2(x,y)], instead of tx,ty
--and your object position is obj.pos instead of obj.x and obj.y
--and both of those are vec2, then you have
if pchange and t:dist(obj.pos) )<=obj.radius then

and your “apply force” line

v:applyForce(vec2((tx-v.x)*(pspeed+1), (ty-v.y)*(pspeed+1)))
--can be written as
v:applyForce((t-v)*(pspeed+1))

(PS thanks for the kind words about the ebook).

Actually

if tx >= obj.x - obj.radius and tx <= obj.x + obj.radius 
    and ty >= obj.y - obj.radius and ty <= obj.y + obj.radius 
    and pchange == true then

and

if pchange and vec2(tx,ty):dist(obj)<=obj.radius then

are not equivalent. (But the second is probably what you want anyway.)

Thanks @Ignatz, I looked at the touched function and noticed that I could shorten the if statements into one if statement. I also made tx and ty into a vec2 in order to shorten other parts of my code.

@LoopSpace I don’t understand why they are not equivalent, could you please explain why

They are not equivalent because my code assumes your objects are circular, whereas your original code worked for squares.

But I thought you probably wanted circles (and even if you have squares, it is close enough, since we have fat fingers that don’t touch accurately).

Thanks, I get it now. (Fat fingers :)))

I also noticed that instead of putting “== true”, you could just write the variable/function. It also works with “== false” by putting “not” before the variable/function.

if var == true then

-- Just write
if var then


-- Instead of
if var == false then

-- Just write
if not var then

That’s right.

A very good trick is using or to set defaults for variables, eg in a function like this

function ABC(a,b)
   --if a is nil or not provided, use 10
   a = a or 10 
   --if b is nil or not provided, use 0.15
   b = b or 0.15 
    return a,b
end

--then 
ABC() --> returns 10, 0.15
ABC(3) --> returns 3, 0.15
ABC(nil,2) --> returns 10, 2

@Ignatz Thanks for the info :smiley:
I also thank you very much for your ebooks, I started reading the lua ebook before I even knew about Codea and it helped me a ton. :smiley: