Difference between revisions of "Fixture:rayCast"

(Fixing my improper use of the newin template. (Yeh yeh, I'm going to fix it in the others))
m (Examples: updated color range)
 
(8 intermediate revisions by 4 users not shown)
Line 1: Line 1:
{{newin|[[0.8.0]]|080|type=function}}
+
{{newin|[[0.8.0]]|080|type=method}}
Casts a ray against the shape of the fixture.
+
Casts a ray against the shape of the fixture and returns the surface normal vector and the line position where the ray hit. If the ray missed the shape, nil will be returned.
 +
 
 +
The ray starts on the first point of the input line and goes towards the second point of the line. The fifth argument is the maximum distance the ray is going to travel as a scale factor of the input line length.
 +
 
 +
The childIndex parameter is used to specify which child of a parent shape, such as a ChainShape, will be ray casted. For ChainShapes, the index of 1 is the first edge on the chain. Ray casting a parent shape will only test the child specified so if you want to test every shape of the parent, you must loop through all of its children.
 +
 
 +
The world position of the impact can be calculated by multiplying the line vector with the third return value and adding it to the line starting point.
 +
<source lang="lua">hitx, hity = x1 + (x2 - x1) * fraction, y1 + (y2 - y1) * fraction</source>
 +
 
 +
 
 +
{{notice|There is a bug in [[0.8.0]] where the normal vector returned by this function gets scaled by [[love.physics.getMeter]].}}
  
 
== Function ==
 
== Function ==
 
=== Synopsis ===
 
=== Synopsis ===
 
<source lang="lua">
 
<source lang="lua">
x, y, fraction = Fixture:rayCast( x1, y1, x2, y2, maxFraction, childIndex )
+
xn, yn, fraction = Fixture:rayCast( x1, y1, x2, y2, maxFraction, childIndex )
 
</source>
 
</source>
 
=== Arguments ===
 
=== Arguments ===
{{param|number|x1|The x position of the ray starting point.}}
+
{{param|number|x1|The x position of the input line starting point.}}
{{param|number|y1|The y position of the ray starting point.}}
+
{{param|number|y1|The y position of the input line starting point.}}
{{param|number|x2|The x position of the ray end point.}}
+
{{param|number|x2|The x position of the input line end point.}}
{{param|number|y1|The y position of the ray end point.}}
+
{{param|number|y2|The y position of the input line end point.}}
{{param|number|maxFraction|The maximum distance the ray is going to travel as a number from 0 to 1.}}
+
{{param|number|maxFraction|Ray length parameter.}}
 
{{param|number|childIndex (1)|The index of the child the ray gets cast against.}}
 
{{param|number|childIndex (1)|The index of the child the ray gets cast against.}}
 
=== Returns ===
 
=== Returns ===
{{param|number|x|The x position where the ray intersects with the shape.}}
+
{{param|number|xn|The x component of the normal vector of the edge where the ray hit the shape.}}
{{param|number|y|The y position where the ray intersects with the shape.}}
+
{{param|number|yn|The y component of the normal vector of the edge where the ray hit the shape.}}
{{param|number|fraction|The position on the input vector where the intersection happened as a number from 0 to 1.}}
+
{{param|number|fraction|The position on the input line where the intersection happened as a factor of the line length.}}
 +
 
 +
== Examples ==
 +
=== Casting 2 different rays against a box. ===
 +
<source lang="lua">function love.load()
 +
-- Setting this to 1 to avoid all current scaling bugs.
 +
love.physics.setMeter(1)
 +
 
 +
World = love.physics.newWorld()
 +
 
 +
Box = {}
 +
Box.Body = love.physics.newBody(World, 400, 300, "dynamic")
 +
Box.Shape = love.physics.newRectangleShape(150, 150)
 +
Box.Fixture = love.physics.newFixture(Box.Body, Box.Shape)
 +
 
 +
-- Giving the box a gentle spin.
 +
Box.Body:setAngularVelocity(0.5)
 +
 
 +
Ray1 = {
 +
point1 = {},
 +
point2 = {},
 +
}
 +
Ray1.point1.x, Ray1.point1.y = 50, 50
 +
Ray1.point2.x, Ray1.point2.y = 400, 300
 +
Ray1.scale = 1
 +
 
 +
Ray2 = {
 +
point1 = {},
 +
point2 = {},
 +
}
 +
Ray2.point1.x, Ray2.point1.y = 500, 400
 +
Ray2.point2.x, Ray2.point2.y = Ray2.point1.x + math.cos(math.pi*1.45), Ray2.point1.y + math.sin(math.pi*1.45)
 +
Ray2.scale = 150
 +
end
 +
 
 +
function love.update(dt)
 +
World:update(dt)
 +
end
 +
 
 +
function love.draw()
 +
-- Drawing the box.
 +
love.graphics.setColor(1, 1, 1)
 +
love.graphics.polygon("line", Box.Body:getWorldPoints( Box.Shape:getPoints() ))
 +
 
 +
-- Drawing the input lines of the rays and the reach of ray 2 in gray.
 +
love.graphics.setLineWidth(3)
 +
love.graphics.setColor(50/255, 50/255, 50/255)
 +
love.graphics.line(Ray2.point1.x, Ray2.point1.y, Ray2.point1.x + (Ray2.point2.x - Ray2.point1.x) * Ray2.scale, Ray2.point1.y + (Ray2.point2.y - Ray2.point1.y) * Ray2.scale)
 +
love.graphics.setColor(1, 1, 1)
 +
love.graphics.line(Ray1.point1.x, Ray1.point1.y, Ray1.point2.x, Ray1.point2.y)
 +
love.graphics.line(Ray2.point1.x, Ray2.point1.y, Ray2.point2.x, Ray2.point2.y)
 +
love.graphics.setLineWidth(1)
 +
 
 +
-- Remember that the ray cast can return nil if it hits nothing.
 +
local r1nx, r1ny, r1f = Box.Fixture:rayCast(Ray1.point1.x, Ray1.point1.y, Ray1.point2.x, Ray1.point2.y, Ray1.scale)
 +
local r2nx, r2ny, r2f = Box.Fixture:rayCast(Ray2.point1.x, Ray2.point1.y, Ray2.point2.x, Ray2.point2.y, Ray2.scale)
 +
 
 +
if r1nx then
 +
-- Calculating the world position where the ray hit.
 +
local r1HitX = Ray1.point1.x + (Ray1.point2.x - Ray1.point1.x) * r1f
 +
local r1HitY = Ray1.point1.y + (Ray1.point2.y - Ray1.point1.y) * r1f
 +
 
 +
-- Drawing the ray from the starting point to the position on the shape.
 +
love.graphics.setColor(1, 0, 0)
 +
love.graphics.line(Ray1.point1.x, Ray1.point1.y, r1HitX, r1HitY)
 +
 
 +
-- We also get the surface normal of the edge the ray hit. Here drawn in green
 +
love.graphics.setColor(0, 1, 0)
 +
love.graphics.line(r1HitX, r1HitY, r1HitX + r1nx * 25, r1HitY + r1ny * 25)
 +
end
 +
 
 +
if r2nx then
 +
-- Calculating the world position where the ray hit.
 +
local r2HitX = Ray2.point1.x + (Ray2.point2.x - Ray2.point1.x) * r2f
 +
local r2HitY = Ray2.point1.y + (Ray2.point2.y - Ray2.point1.y) * r2f
 +
 
 +
-- Drawing the ray from the starting point to the position on the shape.
 +
love.graphics.setColor(1, 0, 0)
 +
love.graphics.line(Ray2.point1.x, Ray2.point1.y, r2HitX, r2HitY)
 +
 
 +
-- We also get the surface normal of the edge the ray hit. Here drawn in green
 +
love.graphics.setColor(0, 1, 0)
 +
love.graphics.line(r2HitX, r2HitY, r2HitX + r2nx * 25, r2HitY + r2ny * 25)
 +
end
 +
end</source>
 +
 
 +
[[File:raycast_example_screenshot.png|thumb|none|Screenshot of this example]]
 +
 
 
== See Also ==
 
== See Also ==
 
* [[parent::Fixture]]
 
* [[parent::Fixture]]

Latest revision as of 09:28, 30 December 2019

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

Casts a ray against the shape of the fixture and returns the surface normal vector and the line position where the ray hit. If the ray missed the shape, nil will be returned.

The ray starts on the first point of the input line and goes towards the second point of the line. The fifth argument is the maximum distance the ray is going to travel as a scale factor of the input line length.

The childIndex parameter is used to specify which child of a parent shape, such as a ChainShape, will be ray casted. For ChainShapes, the index of 1 is the first edge on the chain. Ray casting a parent shape will only test the child specified so if you want to test every shape of the parent, you must loop through all of its children.

The world position of the impact can be calculated by multiplying the line vector with the third return value and adding it to the line starting point.

hitx, hity = x1 + (x2 - x1) * fraction, y1 + (y2 - y1) * fraction


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


Function

Synopsis

xn, yn, fraction = Fixture:rayCast( x1, y1, x2, y2, maxFraction, childIndex )

Arguments

number x1
The x position of the input line starting point.
number y1
The y position of the input line starting point.
number x2
The x position of the input line end point.
number y2
The y position of the input line end point.
number maxFraction
Ray length parameter.
number childIndex (1)
The index of the child the ray gets cast against.

Returns

number xn
The x component of the normal vector of the edge where the ray hit the shape.
number yn
The y component of the normal vector of the edge where the ray hit the shape.
number fraction
The position on the input line where the intersection happened as a factor of the line length.

Examples

Casting 2 different rays against a box.

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

	World = love.physics.newWorld()

	Box = {}
	Box.Body = love.physics.newBody(World, 400, 300, "dynamic")
	Box.Shape = love.physics.newRectangleShape(150, 150)
	Box.Fixture = love.physics.newFixture(Box.Body, Box.Shape)

	-- Giving the box a gentle spin.
	Box.Body:setAngularVelocity(0.5)

	Ray1 = {
		point1 = {},
		point2 = {},
	}
	Ray1.point1.x, Ray1.point1.y = 50, 50
	Ray1.point2.x, Ray1.point2.y = 400, 300
	Ray1.scale = 1

	Ray2 = {
		point1 = {},
		point2 = {},
	}
	Ray2.point1.x, Ray2.point1.y = 500, 400
	Ray2.point2.x, Ray2.point2.y = Ray2.point1.x + math.cos(math.pi*1.45), Ray2.point1.y + math.sin(math.pi*1.45)
	Ray2.scale = 150
end

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

function love.draw()
	-- Drawing the box.
	love.graphics.setColor(1, 1, 1)
	love.graphics.polygon("line", Box.Body:getWorldPoints( Box.Shape:getPoints() ))

	-- Drawing the input lines of the rays and the reach of ray 2 in gray.
	love.graphics.setLineWidth(3)
	love.graphics.setColor(50/255, 50/255, 50/255)
	love.graphics.line(Ray2.point1.x, Ray2.point1.y, Ray2.point1.x + (Ray2.point2.x - Ray2.point1.x) * Ray2.scale, Ray2.point1.y + (Ray2.point2.y - Ray2.point1.y) * Ray2.scale)
	love.graphics.setColor(1, 1, 1)
	love.graphics.line(Ray1.point1.x, Ray1.point1.y, Ray1.point2.x, Ray1.point2.y)
	love.graphics.line(Ray2.point1.x, Ray2.point1.y, Ray2.point2.x, Ray2.point2.y)
	love.graphics.setLineWidth(1)

	-- Remember that the ray cast can return nil if it hits nothing.
	local r1nx, r1ny, r1f = Box.Fixture:rayCast(Ray1.point1.x, Ray1.point1.y, Ray1.point2.x, Ray1.point2.y, Ray1.scale)
	local r2nx, r2ny, r2f = Box.Fixture:rayCast(Ray2.point1.x, Ray2.point1.y, Ray2.point2.x, Ray2.point2.y, Ray2.scale)

	if r1nx then
		-- Calculating the world position where the ray hit.
		local r1HitX = Ray1.point1.x + (Ray1.point2.x - Ray1.point1.x) * r1f
		local r1HitY = Ray1.point1.y + (Ray1.point2.y - Ray1.point1.y) * r1f

		-- Drawing the ray from the starting point to the position on the shape.
		love.graphics.setColor(1, 0, 0)
		love.graphics.line(Ray1.point1.x, Ray1.point1.y, r1HitX, r1HitY)

		-- We also get the surface normal of the edge the ray hit. Here drawn in green
		love.graphics.setColor(0, 1, 0)
		love.graphics.line(r1HitX, r1HitY, r1HitX + r1nx * 25, r1HitY + r1ny * 25)
	end

	if r2nx then
		-- Calculating the world position where the ray hit.
		local r2HitX = Ray2.point1.x + (Ray2.point2.x - Ray2.point1.x) * r2f
		local r2HitY = Ray2.point1.y + (Ray2.point2.y - Ray2.point1.y) * r2f

		-- Drawing the ray from the starting point to the position on the shape.
		love.graphics.setColor(1, 0, 0)
		love.graphics.line(Ray2.point1.x, Ray2.point1.y, r2HitX, r2HitY)

		-- We also get the surface normal of the edge the ray hit. Here drawn in green
		love.graphics.setColor(0, 1, 0)
		love.graphics.line(r2HitX, r2HitY, r2HitX + r2nx * 25, r2HitY + r2ny * 25)
	end
end
Screenshot of this example

See Also


Other Languages