Overlapping sprites

Hello,

I am new to Codea and game development in general. I am trying to learn by reading the forum and the built-in examples.

I have been thinking on how is the best way to test when 2 or more sprites in a scene are overlapping. I couldn’t find anything in the forum on this. Here is what I came up with.

-- Main
supportedOrientations(LANDSCAPE_ANY)

-- Use this function to perform your initial setup
function setup()
    
    objects = {
        Object(50, 50, 101, 115, "Blue"), 
        Object(500, 200, 101, 115, "Green"), 
        Object(300, 500, 101, 115, "Orange")
    }
    draggedObject = nil
end

-- This function gets called once every frame
function draw()
    background(127, 127, 127, 255)
    spriteMode(CORNER)
    
    -- loop all objects
    for _,o in pairs(objects) do
        
        -- check if the object currently being dragged overlaps another object
        if draggedObject and draggedObject.box:testOverlap(o.box) then
            pushMatrix()
            fontSize(30)
            font("MarkerFelt-Thin")
            fill(255, 255, 255, 255)
            text("Overlap", WIDTH/2, HEIGHT-50)
            popMatrix()
        end 
        
        o:draw()
    end
end

function touched(touch)
    -- check if any object is being dragged
    for i,o in ipairs(objects) do
        if o:touched(touch) then
            draggedObject = o
        end
    end
end

-- Object
Object = class()

function Object:init(x, y, w, h, name)
    self.x = x
    self.y = y
    self.w = w
    self.h = h
    self.dx = 0
    self.dy = 0
    self.name = name
    self.dragging = false
    self.highlight = false
    
    -- set box for overlap. Should be changed for gem shape
    self.box = physics.body(POLYGON, vec2(0,h), vec2(0,0), vec2(w,0), vec2(w,h))
    self.box.x = x
    self.box.y = y
end

function Object:draw()
    -- Codea does not automatically call this method
    
    pushMatrix()
    if self.dragging then -- move object based on touch
        if CurrentTouch.deltaX > 1 or CurrentTouch.deltaX < -1 then
            self.dx = self.dx + CurrentTouch.deltaX
            self.box.x = self.box.x + CurrentTouch.deltaX
        end
        
        if CurrentTouch.deltaY > 1 or CurrentTouch.deltaY < -1 then
            self.dy = self.dy + CurrentTouch.deltaY
            self.box.y = self.box.y + CurrentTouch.deltaY
        end
        
        zLevel(1) -- draw in front of other objects
    end
    
    -- "draw" bounding box for overlap test
    pushMatrix()
    translate(self.box.x, self.box.y)
    
    -- remove comments to highlight bounding box
    --stroke(0, 255, 235, 255)
    --strokeWidth(4)
    local points = self.box.points
    for j = 1,#points do
        a = points[j]
        b = points[(j % #points)+1]
        line(a.x, a.y, b.x, b.y)
    end
    popMatrix()
    
    -- draw sprite
    sprite("Planet Cute:Gem " ..  self.name, self.x + self.dx, self.y + self.dy)
    
    popMatrix()
end

function Object:touched(touch)

    -- check if object is being touched
    if touch.state == BEGAN and
       touch.x > self.x and touch.x < self.x + self.w and
       touch.y > self.y and touch.y < self.y + self.h then

        self.dragging = true
        
    elseif touch.state == ENDED then
        self.dx = 0
        self.dy = 0
        self.box.x = self.x
        self.box.y = self.y
        self.dragging = false
    end
        
    return self.dragging
end

I would appreciate any feedback on how to improve this code. I haven’t looked into meshes yet, but I guess that would be the first thing to look at to make the code scalable.

I was also wondering if the way I handled the touch events is optimal, or if there is a cleaner, more efficient way.

Thanks in advance for your help.

You could alternatively create physics bodies for the sprites, set them as sensors, then simply use body1:testOverlap(body2).

If a rectangle for each sprite provides enough accuracy (i.e. you don’t need pixel vs pixel detection) then an overlap is very easy to test. The simplest form is to test for the negative, which can be done in a single statement such as…

if (rectA.right < rectB.left) or (rectA.left > rectB.right) 
or (rectA.top < rectB.bottom) or (rectA.bottom > rectB.top) then
    return false
else
    return true
end

Hi @jongleur,

Suggestion - why not use vec3() for the x and y co-ordinates, then use the .z parameter as the sprite number. If you index them in order of writing them to the screen (using z) then the higher numbers will be on top.

I think it could be workable.

Bri_G

:slight_smile:

Thanks for your responses.

I’ll investigate the alternate methods from @Vega and @Mark.

@Bri_G, Maybe a vec2() would be more appropriate. I don’t want the sprites to be drawn in the order they are written in the code. I want the currently dragged sprite to be on top. That is why I’m using the zlevel(1) function in Object:draw() only when the object is the currently dragged object.

It would be great having something like Flash BitmapData hitTest provided by Codea API. Even if it’s not difficult to implement a similar thing in lua, I fear that it would be really inefficient

Hi All,

Just to prove it is workable, here is a bit of code to show what I suggested would work - if you can use order of sprite display as a parameter for depth.


-- Use this function to perform your initial setup
function setup()
    --
    displayMode(FULLSCREEN)
    spr = {}
    for index = 1, 20 do
        spr[index] = vec3(math.random(50,WIDTH-50),
                    math.random(50,HEIGHT-50), 
                    index)
    end
    w = spriteSize("Planet Cute:Star")  
    fontSize = 20  
    spHit = 0
    textMode(LEFT)
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(0, 0, 0, 255)
    -- This sets the line thickness
    strokeWidth(5)
    -- Do your drawing here    
    for loop = 1, 20 do
        sprite("Planet Cute:Star",spr[loop].x,spr[loop].y)
    end
    if spHit > 0 then 
        text("sprite is           ",380,959) 
        text("sprite is "..spHit,380,959)
        text("total hits "..hits,380,939)
    end
--    spHit = 0
end

function touched(touch)
    -- check current touch and see which sprite it is
    -- by checking the location and then all the sprites
    fill(220, 47, 91, 255)
    hits = 0
    for s = 1, 20 do
        checkX = CurrentTouch.x - spr[s].x
        checkY = CurrentTouch.y - spr[s].y
        if checkX < 20 and checkX > -20 then
            if checkY < 20 and checkY > -20 then
                spHit = spr[s].z
                hits = hits+1
            end
        end
    end
end

Totally unoptimised but designed to show the number of the sprite touched and the number of sprites fitting the locale criteria. Hope it helps someone.

Bri_G

:slight_smile:

Thanks @Bri_G, I’ll check it out this weekend.