Collision box problem (not built in)

So I decided to make my own button/collision function which pretty much just tests if a position (x, y)are in a certain area (x,y,w,h):

function button(actX,actY,x,y,w,h)
    --test if actX and actY are in the area defined by x,y,w,h
    if (x + w/2) > actX
    and actX > (x - w/2)
    and (y + h/2) > actY
    and actY > (y - h/2) then
        --Special instruction for if actX or actY are the CurrentTouch
        if actX == CurrentTouch.x
        or actY == CurrentTouch.y then
            if CurrentTouch.state == ENDED then
                return false
            end
        end
        -- if in the area, return true, else return false
        return true
    else
        return false
    end
end

Then I made a table consisting of the spawn position, and all boxes that make up a level in the game, and a variable that states the level that loads:

levels = {
    l0 = {
        -- Spawn location for charactor
        playerspawn = {x = WIDTH/2,y = HEIGHT/2},
        -- collision is a table consisting of tables that define a collision area
        collision = {
            -- Collision box 1 (the one the charactor isn't colliding with)
            {p = false, r = 0, x = WIDTH/2, y = 6, w = WIDTH, h = 12},
            -- Collision box 2 (this box works)
            {p = true, r = 0, x = 250, y = 200, w = 200, h = 12}
        }
    },
    l1 = {
        -- this is for another level but is not used yet
        playerspawn = {x = 10,y = HEIGHT/10}
    }
}
-- this is the level that is being loaded
currentlevel = levels.l0

Then I made my charactor able to collide with all rectangles stated in currentlevel.collision (levels.l0.collision) using the button function and a for loop and drew them on screen with rect():

for k,v in pairs(currentlevel.collision) do
        -- for all the collision boxes, make a button to detect somthing
        if button(self.x,self.y,v.x,v.y,v.w,v.h) == true then
-- self.collided tells the charactor whether somthing has collided with one of the boxes or not.
            self.collided = true
        else
            self.collided = false
        end
        -- this colors the wall black or white based on a bool.
        if v.p == true then
            fill(255, 255, 255, 255)
        else
            fill(0, 0, 0, 255)
        end
        -- this draws a rectangle for each of the areas so i can see them on screen
        pushMatrix()
        rect(v.x,v.y,v.w,v.h)
        popMatrix()
    end

However, the charactor only collides with the second rectangle in the table, and not the first rectangle. self.collided doesn’t seem to get set to true when the charactor touches the first collision area. Can someone help me find where, and what the problem(s) are that are causing this?

There appears to be no trouble with the code you post.

@PlatniumFrog I’m not really following your code, so I thought I would modify something I already have that I think might help you. Hold the ipad flat and tilt it to move the green dot over the rectangles.

displayMode(FULLSCREEN)

function setup()
    rectMode(CENTER)
    boxTab={}
    for z=1,20 do
        table.insert(boxTab,box(math.random(80,WIDTH-80),
                math.random(40,HEIGHT-40),80,40))       
    end
    player=vec2(math.random(WIDTH),math.random(HEIGHT))
end

function draw()
    background(40, 40, 50)
    for a,b in pairs(boxTab) do
        b:collide(player.x,player.y)
        fill(255)
        if b.inBox then
            fill(255,0,0)
        end
        b:draw()
    end
    fill(0, 255, 59, 255)
    ellipse(player.x,player.y,10)
    player.x=player.x+Gravity.x*10
    player.y=player.y+Gravity.y*10
end

box=class()

function box:init(x,y,w,h)
    self.x=x
    self.y=y
    self.w=w
    self.h=h
    self.inBox=false
end

function box:draw()
    rect(self.x,self.y,self.w,self.h)
end

function box:collide(x,y)
    self.inBox=false
    if x>self.x-self.w/2 and x<self.x+self.w/2 and
            y>self.y-self.h/2 and y<self.y+self.h/2 then
        self.inBox=true            
    end
end

I added some comments above so hopefully it is easier to read.

Btw, I am making a 2D portal game, an I need the levels in that format so I can easily just go in and add a box when needed.

@PlatniumFrog Just to get your code to execute, I have to make a lot of assumptions as to what code needs to be added. You have self variables, but you didn’t include any class as to what or how those self variables are used or defined. It’s hard to try to figure out why someone’s code doesn’t work when a lot of code has to be added to get it to execute. What I add might not be anything close to what you have, so what works for me might not be what you have. But anyways, what I see wrong so far is in the function button. Your if statements looks like they are checking if a rect is drawn using the CENTER values, but the default rect command draws a rect using CORNER values. Maybe you have rectMode(CENTER) set, but I don’t know.

Yeah, I do have rect mode center activated. Also, here is the code:
Main:

-- Portal 2D

-- Use this function to perform your initial setup
function setup()
    print("Oh! It's you! It's been a long time!")
    touches = 0
    fa = {}
    g = 24
    Player:init()
    rectMode(CENTER)
end
function touched(touch)
    if touch.state == BEGAN then
        touches = touches + 1
    end
    if touch.state == ENDED then
        touches = touches - 1 
    end
    if touch.state == ENDED then
        fa[touch.id] = nil
    else
        fa[touch.id] = touch
    end
end
function averagetouch(axis)
    -- this function will be used to get the average position of every finger tounching the screen
        local xa = 0
        local ya = 0
        local fc = 0
    for k,v in pairs(fa) do
        fc = fc + 1
        xa = xa + v.x
        ya = ya + v.y
    end
    local xb = 0
    local yb = 0

    if CurrentTouch.state == BEGAN 
    or CurrentTouch.state == MOVING then
        xb = xa/fc
        yb = ya/fc
    end
    if axis == "x" then
        return xb
    end
    if axis == "y" then
        return yb
    end
end
function button(actX,actY,x,y,w,h)
    if (x + w/2) > actX
    and actX > (x - w/2)
    and (y + h/2) > actY
    and actY > (y - h/2) then
        if actX == CurrentTouch.x
        or actY == CurrentTouch.y then
            if CurrentTouch.state == ENDED then
                return false
            end
        end
        return true
    else
        return false
    end
end
-- This function gets called once every frame
function draw()
    background(127, 127, 127, 255)
    
    Player:draw()
end

Level:

Level = class()
levels = {
    l0 = {
        playerspawn = {x = WIDTH/2,y = HEIGHT/2},
        collision = {
            {p = false, r = 0, x = WIDTH/2, y = 45, w = 100, h = 50},
            {p = true, r = 0, x = WIDTH/2, y = 10, w = WIDTH, h = 20}
        }
    },
    l1 = {
        playerspawn = {x = 10,y = HEIGHT/10}
    }
}
function Level:draw()
    
end
currentlevel = levels.l0

Player:

Player = class()
function Player:init()
    self.x = currentlevel.playerspawn.x
    self.y = currentlevel.playerspawn.y
    self.r = 0
    self.s = 1
    self.d = "left"
    self.armx = self.y
    self.army = self.x
    self.armr = 0
    self.vx = 0
    self.vy = 1
    self.collided = false
    self.av = 0
    parameter.watch("Player.collided")
    parameter.watch("Player.av")
end
function Player:move()
    
end
function Player:arm()
    
end
function Player:draw()
    self.av = averagetouch("x")
    for k,v in pairs(currentlevel.collision) do
        if button(self.x,self.y,v.x,v.y,v.w,v.h) == true then
            if touches == 1 then
                if CurrentTouch.deltaY >= 20 then
                    self.vy = self.vy + CurrentTouch.deltaY 
                else
                    if self.x > (v.x+(v.w/2))+1 and self.x < (v.x-(v.w/2))-1 then
                        self.y = (v.y+(v.h/2))-1
                    end
                end
                
                if self.x < (v.x+(v.w/2)+5) and self.y < v.y+(v.h/2) then
                    self.vx = -(CurrentTouch.deltaX/2)
                else
                    self.vx = CurrentTouch.deltaX/2
                end
                if self.x < (v.x-(v.w/2)-5) and self.y < v.y+(v.h/2) then
                    self.vx = -(CurrentTouch.deltaX/2)
                else
                    self.vx = CurrentTouch.deltaX/2
                end
                if CurrentTouch.deltaX < 0 then
                    self.d = "right"
                end
                if CurrentTouch.deltaX > 0 then
                    self.d = "left"
                end
            end
            self.vx = self.vx - (self.vx/10)
            self.y = self.y + self.vy
            self.vy = 0
            self.collided = true
        else
            if self.vy == 0 then
                self.vy = 1
            end
            self.vy = self.vy + (self.vy/g)
            self.y = self.y - self.vy
            self.vx = self.vx - (self.vx/20)
            self.collided = false
        end
        if v.p == true then
            fill(255, 255, 255, 255)
        else
            fill(0, 0, 0, 255)
        end
        pushMatrix()
        rect(v.x,v.y,v.w,v.h)
        popMatrix()
    end
    
    self.x = self.x + self.vx
    if touches == 2 then
        if button(averagetouch("x"),averagetouch("y"),self.x - (self.s*-0.2),self.y +(self.s*46),250,250) == false then
        self.armx = (self.x -(self.s*-0.2)) + (math.cos(self.armr)*(self.s*22))
        self.army = (self.y +(self.s*46)) + (math.sin(self.armr)*(self.s*22))
        self.armr = math.atan(averagetouch("y")-self.army,averagetouch("x")-self.armx)
        if averagetouch("x") < self.x then
            self.d = "right"
        end
        if averagetouch("x") > self.x then
            self.d = "left"
        end
        end
    else
        self.armr = math.atan(-1,0)
        self.armx = (self.x -(self.s*-0.2)) + (math.cos(self.armr)*(self.s*22))
        self.army = (self.y +(self.s*46)) + (math.sin(self.armr)*(self.s*22))
    end
    ellipse()
    noSmooth()
    pushStyle()
    pushMatrix()
    translate(self.x,self.y+(32*self.s))
    rotate(self.r)
    scale(self.s)
    if self.d == "left" then
        sprite("Dropbox:ChellR")
    end
    if self.d == "right" then
        sprite("Dropbox:ChellL")
    end
    popMatrix()
    if touches == 2 then
        Player:arm()
    end
    pushMatrix()
    translate(self.armx,self.army)
    rotate(math.deg(self.armr))
    scale(self.s)
    sprite("Dropbox:ArmBR")
    popMatrix()
    popStyle()
    
end

Assets:
for “Dropbox:ChellR” in script “Player”
https://www.dropbox.com/s/pydm11kvlhdske2/ChellR.png?dl=0
For “Dropbox:ChellL” in script “Player”
https://www.dropbox.com/s/fww5mkqo7kdh3zo/ChellL.png?dl=0
For “Dropbox:ArmBR” in script “Player”
https://www.dropbox.com/s/m33byfalydtqn3b/ArmOR.png?dl=0

Controls:
Swipe left to move left
Swipe left to move left
Swipe up to jump
Use two fingers to aim the portal gun
(Portal gun will aim at the center point of the two fingers)

Notes:
Also I do need to clarify that I deleted the collided variable and just put everything that the charactor does when colliding directly into the statement since my last post. This “fixed” the problem, but it also didn’t. The charactor collides with both rectangles, but if I add more, it breaks the collision for the boxes that are already there. I was considering changing self.collided to a table that contains the boxes that the charactor is currently colliding with instead of a bool. Instead of querying every box in the level, I could pull from self.collided to see how the charactor can move.

@PlatniumFrog What are you doing with self.collided. You set it to false in Player:init then you set it to true or false in Player:draw but you don’t do anything else with it.

@PlatniumFrog I think your problem is with self.vy . In Player.draw, you loop thru currentlevel.collision and you set self.vy to 0 when there’s a collision, but you set it back to 1 when there isn’t a collision. If you have a lot of collisions defined in the table and the collision isn’t the last one, you’ll set self.vy from 0 back to 1.

I explained what happened to self.collided in the notes section in my post but I’ll explain it again, I originally had it set to true when a player collided with something, and false when the charactor wasn’t colliding. Then I had an if else statement saying what to do if the charactor had collided.

self.vy, when the charactor isn’t colliding with something, has to be set to 1 or else the gravity equation I put it throught each frame when not colliding doesn’t work.

I’ll experiment with it though. Thank you for the help.

Replace player:draw() with the code below. Tap the screen to move the charactor to your finger. This method does not use self.vy. Notice how when the charactor is touching the black collision box, self.collided doesn’t get set to true:

function Player:draw()
    self.av = averagetouch("x")
    for k,v in pairs(currentlevel.collision) do
        if button(self.x,self.y,v.x,v.y,v.w,v.h) == true then
            self.collided = true
        else
            self.collided = false
        end
        if v.p == true then
            fill(255, 255, 255, 255)
        else
            fill(0, 0, 0, 255)
        end
        pushMatrix()
        rect(v.x,v.y,v.w,v.h)
        popMatrix()
    end
    self.x = CurrentTouch.x
    self.y = CurrentTouch.y
    if touches == 2 then
        if button(averagetouch("x"),averagetouch("y"),self.x - (self.s*-0.2),self.y +(self.s*46),250,250) == false then
        self.armx = (self.x -(self.s*-0.2)) + (math.cos(self.armr)*(self.s*22))
        self.army = (self.y +(self.s*46)) + (math.sin(self.armr)*(self.s*22))
        self.armr = math.atan(averagetouch("y")-self.army,averagetouch("x")-self.armx)
        if averagetouch("x") < self.x then
            self.d = "right"
        end
        if averagetouch("x") > self.x then
            self.d = "left"
        end
        end
    else
        self.armr = math.atan(-1,0)
        self.armx = (self.x -(self.s*-0.2)) + (math.cos(self.armr)*(self.s*22))
        self.army = (self.y +(self.s*46)) + (math.sin(self.armr)*(self.s*22))
    end
    ellipse()
    noSmooth()
    pushStyle()
    pushMatrix()
    translate(self.x,self.y+(32*self.s))
    rotate(self.r)
    scale(self.s)
    if self.d == "left" then
        sprite("Dropbox:ChellR")
    end
    if self.d == "right" then
        sprite("Dropbox:ChellL")
    end
    popMatrix()
    if touches == 2 then
        Player:arm()
    end
    pushMatrix()
    translate(self.armx,self.army)
    rotate(math.deg(self.armr))
    scale(self.s)
    sprite("Dropbox:ArmBR")
    popMatrix()
    popStyle()
    
end

@PlatniumFrog Actually it does get set to true, but the false of the second compare shows. Flip the lines in collision so the second line comes first and you’ll see the black box is true, but the white one is false. self.collided should be set to false at the start of the compare and not set to false in the loop if it was set to true.

@PlatniumFrog Try this. Make these changes to the beginning of player:draw().

function Player:draw()
    self.av = averagetouch("x")
    self.collided=false                 -- change here
    for k,v in pairs(currentlevel.collision) do
        if button(self.x,self.y,v.x,v.y,v.w,v.h) == true then
            self.collided = true
        end                                   -- change here
        if v.p == true then
            fill(255, 255, 255, 255)
        else
            fill(0, 0, 0, 255)
        end
        pushMatrix()
        rect(v.x,v.y,v.w,v.h)
        popMatrix()
    end

I see, maybe you found the solution…

I tried it. It didn’t work, But I have another solution. By making self.collision a table, adding everything the charactor is touching to the table, checking if there are values in the table, and if so executing code, and then clearing the table at the end of each frame. This way since self.collided is not a bool, it wont be set to false when ever there are two or more collision boxes to evaluate.

I added more entries to the collision table and each one set self.collided to true when I was in the rectangle. If it’s not working for you, then you’re still doing something wrong. By making self.collided a table, you’re just adding more unnecessary code.

EDIT: I’m talking about the second player:draw routine you posted. If you’re trying to use self.collided in your original player:draw function, it won’t work because you’re changing the self.vx, vy, y variables each time in the loop. You need to take those updates out of the loop. Outside of the loop check self.collided and do your calculations based on if self.collided is true or false.