Page 1 of 1

beginContact physics callback doesn't trigger at random

Posted: Thu Dec 24, 2015 11:48 pm
by xingped
So I'm trying to make a basic Breakout type game and it seems like my beginContact physics callback doesn't trigger at random. I'm sure there's a reason, but I can't find it. Did I set up my physics objects wrong? Or am I handling moving the paddle wrong? Ignore the fact that the actual physics code in my beginContact function is wrong. I know it's wrong.

The first debug printout doesn't execute sometimes though, which is how I know the function isn't even getting called. The "ball" will phase through both the paddle and the walls at random or collide at a point that isn't right on the paddle object. Any thoughts? My full code is below.

The code is all self-contained and no external files are needed. Left/right arrow keys to move the paddle and space bar to set the ball's velocity to move straight down. Fiddle with it a little and eventually you'll see the ball phase right through the paddle and/or walls.

Code: Select all

function love.load()

    -- One meter is 32px in physics engine
	love.physics.setMeter( 32 )

	-- Create physics world with no gravity
	world = love.physics.newWorld(0,0,true)

	-- Create walls
	wallPos = {{0,0,800,10},{790,0,10,600},{0,590,800,10},{0,0,10,600}}
	walls = {}

	for i = 1, #wallPos, 1 do
		walls[i] = {
			body = love.physics.newBody(world,wallPos[i][1],wallPos[i][2],"static"),
			shape = love.physics.newRectangleShape(0,0,wallPos[i][3],wallPos[i][4])
		}
		walls[i].fixture = love.physics.newFixture(walls[i].body, walls[i].shape)
		walls[i].body:setMassData(walls[i].shape:computeMass(1))
	end

	-- Create blocks
	

	-- Create paddle at (300, 500) dynamic
	paddle = love.physics.newBody(world,300,500,"dynamic")
	paddle_shape = love.physics.newRectangleShape(0, 0, 200, 10)
	paddle_fixture = love.physics.newFixture(paddle, paddle_shape)
	paddle_fixture:setUserData("Paddle")
	paddle:setMassData(paddle_shape:computeMass(1))

	-- Load the image of the ball
	--ball = love.graphics.newImage("love-ball.png")

	-- Create a Body for the circle
	circle = love.physics.newBody(world, 400, 200, "dynamic")

	-- Attach a shape to the body
	circle_shape = love.physics.newCircleShape(0,0,32)

	-- Create a fixture between body and shape
	circle_fixture = love.physics.newFixture(circle, circle_shape)
	circle_fixture:setUserData("Ball")

	-- Calculate the mass of the body based on attached shapes for realistic simulation
	circle:setMassData(circle_shape:computeMass(1))

	-- Set collision callbacks
	world:setCallbacks(beginContact,endContact)
end

function love.update(dt)
	world:update(dt)
end

function love.draw()
	for i = 1, #walls, 1 do
		topLeftX, topLeftY, botRightX, botRightY = walls[i].fixture:getBoundingBox()
		love.graphics.rectangle("line", walls[i].body:getX(), walls[i].body:getY(), botRightX-topLeftX, botRightY-topLeftY)
	end

	love.graphics.rectangle("line", 1, 1, 798, 10)
	love.graphics.rectangle("fill", paddle:getX(), paddle:getY(), 200, 10)
	love.graphics.circle("line", circle:getX(), circle:getY(), circle_shape:getRadius())
end

function love.keypressed(key)
	if key == "escape" then
		love.event.quit()
	elseif key == "left" then
	    paddle:setLinearVelocity(-150,0)
	elseif key == "right" then
		paddle:setLinearVelocity(150,0)
	elseif key == " " then
		circle:setLinearVelocity(0,150)
	end
end

function love.keyreleased(key)
	if key == "left" or key == "right" then
		if love.keyboard.isDown("left") then
			paddle:setLinearVelocity(-150,0)
		elseif love.keyboard.isDown("right") then
			paddle:setLinearVelocity(150,0)
		else
			paddle:setLinearVelocity(0,0)
		end
	end
end

function beginContact(a, b, c)
	print(a, b, c:getNormal())

	-- Reflective bounce on collision
	local nX, nY = c:getNormal()
	if a:getUserData() == "Ball" then
		local aBody = a:getBody()
		local aVelX, aVelY = aBody:getLinearVelocity()
		aVelX = aVelX * math.cos(nX)
		aVelY = aVelY * math.sin(ny)

		-- Add paddle's X velocity to the ball
		if b:getUserData() == "Paddle" then
			aVelX = aVelX + b:getBody():getLinearVelocity()
		end

		aBody:setLinearVelocity(aVelX, aVelY)
	end

	if b:getUserData() == "Ball" then
		local bBody = b:getBody()
		local bVelX, bVelY = bBody:getLinearVelocity()
		bVelX = bVelX * math.cos(nX)
		bVelY = bVelY * math.sin(nY)

		-- Add paddle's X velocity to the ball
		if a:getUserData() == "Paddle" then
			bVelX = bVelX + a:getBody():getLinearVelocity()
		end

		bBody:setLinearVelocity(bVelX, bVelY)
	end
end

function endContact(a, b, c)
	if a:getUserData() == "Block" then
		--delete block
	end

	if b:getUserData() == "Block" then
		--delete block
	end
end

Edit: Fixed it! I just want to thank ThatTreeOverThere for pointing out my error. I was using

Code: Select all

love.graphics.rectangle("fill", paddle:getX(), paddle:getY(), 200, 10)
instead of what I should have been using:

Code: Select all

love.graphics.polygon("line", paddle:getWorldPoints(paddle_shape:getPoints()))
In essence, I was not drawing the paddle where it actually was. The getX() and getY() of the body was referencing the center of the body, but when you draw, the origin is the top-left of the body. So I was drawing a rectangle starting at the center of the body, but the body was actually positioned up and to the left of where I was drawing it. Glad to have that one figured out!