#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

# Collision box problem (not built in)

edited October 2016 Posts: 13

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?

Tagged:

• edited October 2016 Posts: 28

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

• Posts: 7,879

@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
``````
• Posts: 13

• edited October 2016 Posts: 13

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.

• Posts: 7,879

@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.

• edited October 2016 Posts: 13

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.

• Posts: 7,879

@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.

• Posts: 7,879

@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.

• Posts: 13

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.

• Posts: 13

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

• edited October 2016 Posts: 13

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
``````
• edited October 2016 Posts: 7,879

@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.

• Posts: 7,879

@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
``````
• Posts: 13

I see, maybe you found the solution...

• Posts: 13

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.

• edited October 2016 Posts: 7,879

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.