Please Help Me Make A Shattering Circle Physics Body

Hello!
I’m trying to make a circle shatter into a bunch of peices when you tap the screen, but I’m having trouble figuring out what points I need to use when creating the slices. I’m worried I might have to use some of the math functions I don’t understand like math.acos, math.cosh, math.asin, math.sinh, math.atan, math.tanh, math.fmod, and math.log (I do understand however, that cosine is adjecent / hypotenuse, sine is oposite / hypotenuse and tangent is oposite / adjecent in a triangle). Don’t blame me, I’m not in high school yet. I’ve tried learning these math functions through the internet, but that kind of math was very confusing. I’m hoping someone can help me here on the forums. I would love someone to help me write the code, but giving me some easy-to-understand tutorials on these math functions would also be great!
Here’s what I have so far:

-- Use this function to perform your initial setup
function setup()
    borders = physics.body(CHAIN,true,vec2(0,0),vec2(WIDTH,0),vec2(WIDTH,HEIGHT),vec2(0,HEIGHT))
    
    bodies = {}
    newBody(physics.body(CIRCLE, 100), WIDTH/2, HEIGHT/2, vec2(0,0))
    --newBody(physics.body(POLYGON,vec2(-100,-100),vec2(0,100),vec2(100,-100)),200,HEIGHT-100)
    
    physics.gravity(0,-700)
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- Do your drawing here
    for k,t in pairs(bodies) do
        pushMatrix()
        translate(t.b.x,t.b.y)
        if t.b.shapeType == POLYGON then
            rotate(t.b.angle)
            t.m:draw()
            elseif t.b.shapeType == CIRCLE then
            pushStyle()
            fill(0,0,255)
            noStroke()
            ellipse(0,0,t.b.radius*2)
            popStyle()
        end
        popMatrix()
    end
end

function touched(t)
    if t.state == ENDED then
        -- First we create new bodies in the shapes of the circle sliced (how??)
        
        bodies[1].b:destroy()
        table.remove(bodies,1) -- Then we remove the original circle
    end
end

function newBody(b, x, y, vel, ang, rotSpeed)
    local velocity
    if vel then
        velocity = vec2(vel.x,vel.y)
        else
        velocity = vec2(0,0)
    end
    
    b.position = vec2(x,y)
    b.linearVelocity = velocity
    b.angle = ang or 0
    b.angularVelocity = rotSpeed or 0
    b.restitution = 0.4
    
    local m = mesh()
    if b.shapeType == POLYGON then
        m.vertices = triangulate(b.points)
    end
    m:setColors(0,0,255)
    
    table.insert(bodies, {b=b,m=m})
end

Thank-you so much in advance! :smile:

The easiest way would be to have it shatter into pie-slices (pi slices? :smiley: )

Is that what you want though?

You say “shatter” which implies to me that you’d like the pieces to be more irregular and unevenly shaped.

Yes, that is what I was implying when I said shatter. But if that’s too complicated, the pie slices would work too. (and thank-you for helping out! :slight_smile: )

I’m sorry to do so but, I’ve actually changed my mind. I think the pie slices method would be a lot better because if we use the other option we will end up with peices that are very small which is not so good for the game I’ll be using this for. I think the pie slices can be made with a couple for loops, math.cos and math.sin, but I’m not sure exactly how to do it.

@Kolosso - we are happy to help solve problems, but we do ask people to make an effort first, because we are a small group.

Unless you are expecting us to write all the code for you, you will need to learn a bit about sin, cos, tan. They really aren’t very difficult, even if you aren’t in high school yet, and you need them all the time to do graphics programming.

There are stacks of tutorials on them, much better than anything we could do. Why reinvent the wheel?

Why don’t you look at all the stuff out there, try to get a basic understanding of trig functions, then come back and have a go with Codea?

Sorry, couldn’t resist. Codea’s triangulate function creates nice, spiky looking segments.


--# Main
-- Circle Shatter

function setup()
    --unique points describing circle
    local noPoints = 24 --must be a multiple of 3
    local points = {}
    local radius = 100
    local angleStep = (math.pi * 2) / noPoints
    for i = 1,noPoints do
        local angle = (i-1) * angleStep
        points[i] = vec2(radius * math.sin(angle), radius * math.cos(angle))
    end
    
    local verts = triangulate(points) --triangulate points
    bodies = {}
    local col = color(0, 235, 255, 255)
    local pos = vec2(WIDTH/2, HEIGHT/2) --position of circle
    print("triangles:", #verts / 3)
    for i = 1, #verts, 3 do
        local a = verts[i] * 1 -- *1 to make a new copy of the vec
        local b = verts[i+1] * 1
        local c = verts[i+2] * 1
        local centre = (a+b+c)/3
        a = a - centre
        b = b - centre
        c = c - centre
        table.insert(bodies, Triangle(centre, pos, col, a, b, c))
    end
end

function draw()
    background(40, 40, 50)
    for i = 1, #bodies do
        bodies[i]:draw()
    end
    
end

function touched(t)
     for i = 1, #bodies do
        bodies[i]:touched()
    end   
end


--# Triangle
Triangle = class()

local function randomRange(n)
    return (math.random() - 0.5) * n * 2
end

function Triangle:init(segmentPos, circlePos, col, a,b,c)
    self.body = physics.body(POLYGON, a, b, c)
    self.body.position = segmentPos + circlePos
    self.body.gravityScale = 0 --not affected by gravity. yet...
    self.body.interpolate = true
    self.mesh = mesh()
    local mag = 1.01 --make mesh slightly bigger to cover cracks. Or, smaller, 0.99 if you want cracks
    self.mesh.vertices = {a * mag,b * mag,c * mag}
    self.mesh:setColors(col)
    --the vector that the segment will explode along is its normalized segment position, plus anti-gravity, plus noise
    self.escapeVelocity = (segmentPos:normalize() + vec2(randomRange(0.5),1 + math.random() * 0.4)) * 3
end

function Triangle:draw()
    pushMatrix()
    translate(self.body.x, self.body.y)
    rotate(self.body.angle)
    self.mesh:draw()
    popMatrix()
end

function Triangle:touched()
    self.body.gravityScale = 1
    self.body:applyLinearImpulse(self.escapeVelocity)
    self.body:applyAngularImpulse(randomRange(10))
end

@yojimbo2000, Thank-you so much! Like I said, I have changed my mind so I may not use the code you provided in my game (and I’m sorry about it) but I will still try to learn from it. I bet there are many techniques in your code.

@Ignatz, Sorry for causing any problems. I have looked up cosine, sine and tangent tutorials on the internet and I’m currently trying my best to split the circle into even pie slices with them.

Well, look at the first for loop in my setup function for your pi slices.

I looked at this code in your setup function, and after quite a few experiments, I think I finally understand how it works!!:

    local noPoints = 24 --must be a multiple of 3
    local points = {}
    local radius = 100
    local angleStep = (math.pi * 2) / noPoints
    for i = 1,noPoints do
        local angle = (i-1) * angleStep
        points[i] = vec2(radius * math.sin(angle), radius * math.cos(angle))
    end

Thanks, @yojimbo2000!

@Kolosso Thought I’d give a try at the exploding circle. Tap the screen to explode it. Do a restart to see it again.

displayMode(FULLSCREEN)

function setup()
    e1=physics.body(EDGE,vec2(0,0),vec2(0,HEIGHT))
    e2=physics.body(EDGE,vec2(WIDTH,0),vec2(WIDTH,HEIGHT))
    e3=physics.body(EDGE,vec2(0,0),vec2(WIDTH,0))
    e4=physics.body(EDGE,vec2(0,HEIGHT),vec2(WIDTH,HEIGHT))
    ba={}
    for x=1,16 do
          table.insert(ba,ball(WIDTH/2,HEIGHT/2))    
    end
    physics.continuous=true
end

function draw()
    background(40, 40, 50)
    stroke(255)
    strokeWidth(2)
    noFill()
    if not hide then
        ellipse(WIDTH/2,HEIGHT/2,40)
    else
        for a,b in pairs(ba) do
            b:draw()
        end
    end
end

function touched(t)
    if t.state==BEGAN and not hide then
        hide=true
        for z=1,#ba do
            ba[z].a.type=DYNAMIC
            ba[z].a.linearVelocity=vec2(math.random(-300,300),math.random(-300,300))
        end        
    end
end

ball=class()

function ball:init(x,y)
    self.a=physics.body(POLYGON,
        vec2(math.random(-10,0),math.random(10)),
        vec2(math.random(-10,0),math.random(-10,0)),
        vec2(math.random(10),math.random(-10,0)),
        vec2(math.random(10),math.random(10)))
    self.a.gravityScale=0
    self.a.x=x
    self.a.y=y
    self.a.restitution=.8
    self.a.type=STATIC
end

function ball:draw()
    stroke(255)
    strokeWidth(2)
    pushMatrix()
    translate(self.a.x,self.a.y)
    rotate(self.a.angle)
    p=self.a.points
    for z=2,#p do
        line(p[z-1].x,p[z-1].y,p[z].x,p[z].y)        
    end
    line(p[1].x,p[1].y,p[#p].x,p[#p].y)
    popMatrix()
end

I did it! :smiley:
It took a lot of time and effort and it probably isn’t the most efficient way, but I finally figured out how to make a circle split into a bunch of peices!:

-- Use this function to perform your initial setup
displayMode(FULLSCREEN)
function setup()
    borders = physics.body(CHAIN,true,vec2(0,0),vec2(WIDTH,0),vec2(WIDTH,HEIGHT),vec2(0,HEIGHT))
    
    bodies = {}
    newBody(physics.body(CIRCLE, 100), WIDTH/2, HEIGHT/6*5, vec2(0,0))
    --newBody(physics.body(POLYGON,vec2(-100,-100),vec2(0,100),vec2(100,-100)),200,HEIGHT-100)
    
    physics.gravity(0,-700)
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- Do your drawing here
    for k,t in pairs(bodies) do
        pushMatrix()
        translate(t.b.x,t.b.y)
        if t.b.shapeType == POLYGON then
            rotate(t.b.angle)
            t.m:draw()
            elseif t.b.shapeType == CIRCLE then
            pushStyle()
            fill(0,0,255)
            noStroke()
            ellipse(0,0,t.b.radius * 2)
            popStyle()
        end
        popMatrix()
    end
end

function touched(t)
    if t.state == ENDED then
        -- First we create new bodies in the shapes of a sliced circle
        slicedCircle(bodies[1].b.radius, bodies[1].b.x, bodies[1].b.y, bodies[1].b.linearVelocity * 1, 500)
        
        bodies[1].b:destroy()
        bodies[1] = nil -- Then we remove the original circle
    end
end

function newBody(b, x, y, vel, ang, rotSpeed)
    local velocity
    if vel then
        velocity = vec2(vel.x,vel.y)
        else
        velocity = vec2(0,0)
    end
    
    b.position = vec2(x,y)
    b.linearVelocity = velocity
    b.angle = ang or 0
    b.angularVelocity = rotSpeed or 0
    b.restitution = 0.4
    
    local m = mesh()
    if b.shapeType == POLYGON then
        m.vertices = triangulate(b.points)
    end
    m:setColors(0,0,255)
    
    table.insert(bodies, {b=b,m=m})
end

function slicedCircle(radius, x, y, velocity, explo, p, groups)
    local p = p or 24
    local groups = groups or 2
    local circle = circlePoints(radius, p)
    
    local next = 0
    for i=1, math.ceil(#circle / groups) do
        local points = {vec2(0,0)}
        for g=1, groups+1 do
            next = next + 1
            if circle[next+(i-1)] then
                table.insert(points,circle[next+(i-1)]*1)
                elseif points[#points] ~= circle[1] then
                table.insert(points,circle[1]*1)
            end
        end
        next = next - 2
        local b = physics.body(POLYGON, table.unpack(points))
        local vel = vec2(math.sin(i) * explo + velocity.x, math.cos(i) * explo + velocity.y)
        newBody(b, x, y, vel)
    end
end

function circlePoints(radius,numPoints)
    local points = {}
    local fullCircle = math.pi * 2
    local angleStep = fullCircle / numPoints
    for i=1, numPoints do
        local angle = (i-1) * angleStep
        points[i] = vec2(math.sin(angle) * radius, math.cos(angle) * radius)
    end
    return points
end

I made a function called slicedCircle(radius, x, y, velocity, explo, p, groups) that makes a bunch of polygon physics bodies in shapes of a sliced circle. The parameter radius determines the radius of the circle, x and y is for location of the sliced circle, velocity and explo are used to determine the velocity of the peices, p is for the amount of points you want the circle to have and groups is used to determine how many points (the more points, the more round) each slice has. If groups is not a multiple of p, the slice just left of the top of the circle will be a different fraction of the whole circle and this is fine in my opinion. I’m just pointing it out incase someone might want to use these functions.

Thank-you so much @dave and @yojimbo2000 for writing examples. They really helped. :smile:
And thank-you, @ignatz for the advice. :smile: