Difference between revisions of "World:rayCast"

m
(Added callback function infromation.)
Line 1: Line 1:
 
{{newin|[[0.8.0]]|080|type=method}}
 
{{newin|[[0.8.0]]|080|type=method}}
Casts a ray and calls a function with the fixtures that intersect it.
+
Casts a ray and calls a function with the fixtures that intersect it. You cannot make any assumptions about the order of the callbacks.
  
 +
Each time the function gets called, 6 arguments get passed to it. The first is the fixture intersecting the ray. The second and third are the coordinates of the intersection point. The fourth and fifth is the surface normal vector of the shape edge. The sixth argument is the position of the intersection on the ray as a number from 0 to 1.
 +
 +
The ray can be controlled with the return value. A positive value sets a new ray length. A value of 0 terminates the ray. If the callback function returns -1, the intersection gets ignored as if it didn't happen.
 +
 +
<source lang="lua">function callback(fixture, x, y, xn, yn, fraction)
 +
return 1
 +
end</source>
 +
 +
 +
{{notice|There is a bug in [[0.8.0]] where the normal vector passed to the callback function gets scaled by [[love.physics.getMeter]].}}
 
== Function ==
 
== Function ==
 
=== Synopsis ===
 
=== Synopsis ===
Line 7: Line 17:
 
World:rayCast( x1, y1, x2, y2, callback )
 
World:rayCast( x1, y1, x2, y2, callback )
 
</source>
 
</source>
 
 
=== Arguments ===
 
=== Arguments ===
 
{{param|number|x1|The x position of the starting point of the ray.}}
 
{{param|number|x1|The x position of the starting point of the ray.}}
Line 16: Line 25:
 
=== Returns ===
 
=== Returns ===
 
Nothing.
 
Nothing.
 +
 +
== Examples ==
 +
=== Casting a ray over some random shapes. ===
 +
<source lang="lua">function worldRayCastCallback(fixture, x, y, xn, yn, fraction)
 +
local hit = {}
 +
hit.fixture = fixture
 +
hit.x, hit.y = x, y
 +
hit.xn, hit.yn = xn, yn
 +
hit.fraction = fraction
 +
 +
table.insert(Ray.hitList, hit)
 +
 +
return 1 -- Continues with ray cast through all shapes.
 +
end
 +
 +
function createStuff()
 +
-- Cleaning up the previous stuff.
 +
for i = 1, #Terrain.Stuff do
 +
Terrain.Stuff[i].Fixture:destroy()
 +
Terrain.Stuff[i] = nil
 +
end
 +
 +
-- Generates some random shapes.
 +
for i = 1, 30 do
 +
local p = {}
 +
 +
p.x, p.y = math.random(100, 700), math.random(100, 500)
 +
local shapetype = math.random(3)
 +
if shapetype == 1 then
 +
local w, h, r = math.random() * 10 + 40, math.random() * 10 + 40, math.random() * math.pi * 2
 +
p.Shape = love.physics.newRectangleShape(p.x, p.y, w, h, r)
 +
elseif shapetype == 2 then
 +
local a = math.random() * math.pi * 2
 +
local x2, y2 = p.x + math.cos(a) * (math.random() * 30 + 20), p.y + math.sin(a) * (math.random() * 30 + 20)
 +
p.Shape = love.physics.newEdgeShape(p.x, p.y, x2, y2)
 +
else
 +
local r = math.random() * 40 + 10
 +
p.Shape = love.physics.newCircleShape(p.x, p.y, r)
 +
end
 +
 +
p.Fixture = love.physics.newFixture(Terrain.Body, p.Shape)
 +
 +
Terrain.Stuff[i] = p
 +
end
 +
end
 +
 +
function love.keypressed()
 +
createStuff()
 +
end
 +
 +
function love.load()
 +
-- Setting this to 1 to avoid all current scaling bugs.
 +
love.physics.setMeter(1)
 +
 +
-- Start out with the same random stuff each start.
 +
math.randomseed(0xfacef00d)
 +
 +
World = love.physics.newWorld()
 +
 +
Terrain = {}
 +
Terrain.Body = love.physics.newBody(World, 0, 0, "static")
 +
Terrain.Stuff = {}
 +
createStuff()
 +
 +
Ray = {
 +
x1 = 0,
 +
y1 = 0,
 +
x2 = 0,
 +
y2 = 0,
 +
hitList = {}
 +
}
 +
end
 +
 +
function love.update(dt)
 +
local now = love.timer.getMicroTime()
 +
 +
World:update(dt)
 +
 +
-- Clear fixture hit list.
 +
Ray.hitList = {}
 +
 +
-- Calculate ray position.
 +
local pos = (math.sin(now/4) + 1.2) * 0.4
 +
Ray.x2, Ray.y2 = math.cos(pos * (math.pi/2)) * 1000, math.sin(pos * (math.pi/2)) * 1000
 +
 +
-- Cast the ray and populate the hitList table.
 +
World:rayCast(Ray.x1, Ray.y1, Ray.x2, Ray.y2, worldRayCastCallback)
 +
end
 +
 +
function love.draw()
 +
-- Drawing the terrain.
 +
love.graphics.setColor(255, 255, 255)
 +
for i = 1, #Terrain.Stuff do
 +
if Terrain.Stuff[i].Shape:getType() == "polygon" then
 +
love.graphics.polygon("line", Terrain.Body:getWorldPoints( Terrain.Stuff[i].Shape:getPoints() ))
 +
elseif Terrain.Stuff[i].Shape:getType() == "edge" then
 +
love.graphics.line(Terrain.Body:getWorldPoints( Terrain.Stuff[i].Shape:getPoints() ))
 +
else
 +
local x, y = Terrain.Body:getWorldPoints(Terrain.Stuff[i].x, Terrain.Stuff[i].y)
 +
love.graphics.circle("line", x, y, Terrain.Stuff[i].Shape:getRadius())
 +
end
 +
end
 +
 +
-- Drawing the ray.
 +
love.graphics.setLineWidth(3)
 +
love.graphics.setColor(255, 255, 255, 100)
 +
love.graphics.line(Ray.x1, Ray.y1, Ray.x2, Ray.y2)
 +
love.graphics.setLineWidth(1)
 +
 +
-- Drawing the intersection points and normal vectors if there were any.
 +
for i = 1, #Ray.hitList do
 +
local hit = Ray.hitList[i]
 +
love.graphics.setColor(255, 0, 0)
 +
love.graphics.circle("line", hit.x, hit.y, 3)
 +
love.graphics.setColor(0, 255, 0)
 +
love.graphics.line(hit.x, hit.y, hit.x + hit.xn * 25, hit.y + hit.yn * 25)
 +
end
 +
end</source>
 +
 
== See Also ==
 
== See Also ==
 
* [[parent::World]]
 
* [[parent::World]]

Revision as of 11:59, 6 May 2012

Available since LÖVE 0.8.0
This method is not supported in earlier versions.

Casts a ray and calls a function with the fixtures that intersect it. You cannot make any assumptions about the order of the callbacks.

Each time the function gets called, 6 arguments get passed to it. The first is the fixture intersecting the ray. The second and third are the coordinates of the intersection point. The fourth and fifth is the surface normal vector of the shape edge. The sixth argument is the position of the intersection on the ray as a number from 0 to 1.

The ray can be controlled with the return value. A positive value sets a new ray length. A value of 0 terminates the ray. If the callback function returns -1, the intersection gets ignored as if it didn't happen.

function callback(fixture, x, y, xn, yn, fraction)
	return 1
end


O.png There is a bug in 0.8.0 where the normal vector passed to the callback function gets scaled by love.physics.getMeter.  


Function

Synopsis

World:rayCast( x1, y1, x2, y2, callback )

Arguments

number x1
The x position of the starting point of the ray.
number y1
The y position of the starting point of the ray.
number x2
The x position of the end point of the ray.
number y2
The y position of the end point of the ray.
function callback
This function gets six arguments and should return a number.

Returns

Nothing.

Examples

Casting a ray over some random shapes.

function worldRayCastCallback(fixture, x, y, xn, yn, fraction)
	local hit = {}
	hit.fixture = fixture
	hit.x, hit.y = x, y
	hit.xn, hit.yn = xn, yn
	hit.fraction = fraction

	table.insert(Ray.hitList, hit)

	return 1 -- Continues with ray cast through all shapes.
end

function createStuff()
	-- Cleaning up the previous stuff.
	for i = 1, #Terrain.Stuff do
		Terrain.Stuff[i].Fixture:destroy()
		Terrain.Stuff[i] = nil
	end

	-- Generates some random shapes.
	for i = 1, 30 do
		local p = {}

		p.x, p.y = math.random(100, 700), math.random(100, 500)
		local shapetype = math.random(3)
		if shapetype == 1 then
			local w, h, r = math.random() * 10 + 40, math.random() * 10 + 40, math.random() * math.pi * 2
			p.Shape = love.physics.newRectangleShape(p.x, p.y, w, h, r)
		elseif shapetype == 2 then
			local a = math.random() * math.pi * 2
			local x2, y2 = p.x + math.cos(a) * (math.random() * 30 + 20), p.y + math.sin(a) * (math.random() * 30 + 20)
			p.Shape = love.physics.newEdgeShape(p.x, p.y, x2, y2)
		else
			local r = math.random() * 40 + 10
			p.Shape = love.physics.newCircleShape(p.x, p.y, r)
		end

		p.Fixture = love.physics.newFixture(Terrain.Body, p.Shape)

		Terrain.Stuff[i] = p
	end
end

function love.keypressed()
	createStuff()
end

function love.load()
	-- Setting this to 1 to avoid all current scaling bugs.
	love.physics.setMeter(1)

	-- Start out with the same random stuff each start.
	math.randomseed(0xfacef00d)

	World = love.physics.newWorld()

	Terrain = {}
	Terrain.Body = love.physics.newBody(World, 0, 0, "static")
	Terrain.Stuff = {}
	createStuff()

	Ray = {
		x1 = 0,
		y1 = 0,
		x2 = 0,
		y2 = 0,
		hitList = {}
	}
end

function love.update(dt)
	local now = love.timer.getMicroTime()

	World:update(dt)

	-- Clear fixture hit list.
	Ray.hitList = {}
	
	-- Calculate ray position.
	local pos = (math.sin(now/4) + 1.2) * 0.4
	Ray.x2, Ray.y2 = math.cos(pos * (math.pi/2)) * 1000, math.sin(pos * (math.pi/2)) * 1000
	
	-- Cast the ray and populate the hitList table.
	World:rayCast(Ray.x1, Ray.y1, Ray.x2, Ray.y2, worldRayCastCallback)
end

function love.draw()
	-- Drawing the terrain.
	love.graphics.setColor(255, 255, 255)
	for i = 1, #Terrain.Stuff do
		if Terrain.Stuff[i].Shape:getType() == "polygon" then
			love.graphics.polygon("line", Terrain.Body:getWorldPoints( Terrain.Stuff[i].Shape:getPoints() ))
		elseif Terrain.Stuff[i].Shape:getType() == "edge" then
			love.graphics.line(Terrain.Body:getWorldPoints( Terrain.Stuff[i].Shape:getPoints() ))
		else
			local x, y = Terrain.Body:getWorldPoints(Terrain.Stuff[i].x, Terrain.Stuff[i].y)
			love.graphics.circle("line", x, y, Terrain.Stuff[i].Shape:getRadius())
		end
	end

	-- Drawing the ray.
	love.graphics.setLineWidth(3)
	love.graphics.setColor(255, 255, 255, 100)
	love.graphics.line(Ray.x1, Ray.y1, Ray.x2, Ray.y2)
	love.graphics.setLineWidth(1)

	-- Drawing the intersection points and normal vectors if there were any.
	for i = 1, #Ray.hitList do
		local hit = Ray.hitList[i]
		love.graphics.setColor(255, 0, 0)
		love.graphics.circle("line", hit.x, hit.y, 3)
		love.graphics.setColor(0, 255, 0)
		love.graphics.line(hit.x, hit.y, hit.x + hit.xn * 25, hit.y + hit.yn * 25)
	end
end

See Also


Other Languages