Reputation: 61
I want to check if a laser beam (a line) goes through objects and at which points. Currently I'm recreating the Asteroids project by grapefrukt. For more details you check this video (https://www.youtube.com/watch?v=MeJ76z2Ncyg&t=622s).
There is a method called hitTestWithSegmentFromPoint
.. inside SceneKit but I want to achieve the effect only using Spritekit.
Edit 1: I need to know the position of the black circles (the points of intersection). The asteroid is with red color, the beam is green.
Edit 2: I was hopeful that the @Knight0fDragon solution will work.. but it didn't. Since the line appears through the box it doesn't detect the first contact correctly.. nor the second (when leaving the box)
The white circle is the contact point.. the purple arrows point to where the contact points should be. The blue circle is the ship :)
I also made when the collision is detected to make the boxes physics bodies not dynamic (stoping them from moving)
Edit 3: Closer image to see where physics bodies are using showsPhysics
Edit 4: How I make the white circle
let firstBody = contact.bodyA.categoryBitMask > contact.bodyB.categoryBitMask ? contact.bodyB : contact.bodyA
let secondBody = contact.bodyA == firstBody ? contact.bodyB : contact.bodyA
if firstBody.categoryBitMask == UInt32(1){
secondBody.isDynamic = false;
// run only once
print(contact.contactPoint);
createCircleDebug(x: contact.contactPoint.x, y: contact.contactPoint.y, radius: 5)
}
func createCircleDebug (x:CGFloat, y:CGFloat, radius:CGFloat){
let shape = SKShapeNode.init(circleOfRadius: radius);
shape.strokeColor = SKColor.white;
shape.position = CGPoint(x: x, y: y);
self.addChild(shape);
}
Edit 5: Relative line path. The circle is still off.
Edit 6: Line is now a rectangle using the new code for creating it
Edit 7: Using Spritenode instead of circle
Upvotes: 1
Views: 908
Reputation: 11866
The Apple documentation includes a guide on ray casting with SKPhysics:
Searching the World for Physics Bodies
To find the nearest physics body that the ray hits, you can use body(alongRayStart:end:)
. This is a method on SKPhysicsWorld
.
There is also enumerateBodies(alongRayStart:end:using:)
if you want to get all bodies that are hit by the ray and where they were hit. This method calls your block for each physics body that the ray touches, with the following arguments:
body
: The physics body that the ray intersected.point
: The point in scene coordinates where the ray contacted the physics body.normal
: The normal vector for the physics body at the point of contact.stop
: A pointer to a Boolean variable. Your block can set this to true to terminate the enumeration.Upvotes: 1
Reputation: 16837
Normally I would do what ford said (It was the answer I was going to provide after you answered my question), but if you need to know points of intersection I would create an SKNode
with an SKPhysicsBody(polygonFrom:)
and use contactBegins to get the intersection point
func rayCast(start:CGPoint,end:CGPoint){
let mid = CGPoint((end.x + start.x) / 2,(end.y + start.y) / 2)
let relativeStart1 = CGPoint(start.x - mid.x - 1,start.y - mid.y)
let relativeEnd1 = CGPoint(end.x - mid.x - 1,end.y - mid.y-1)
let relativeStart2 = CGPoint(start.x - mid.x + 1,start.y - mid.y)
let relativeEnd2 = CGPoint(end.x - mid.x + 1,end.y - mid.y)
let node = SKNode()
let linePath = UIBezierPath()
linePath.move(to: relativeStart1)
linePath.addLine(to: relativeEnd1)
linePath.addLine(to: relativeEnd2)
linePath.addLine(to: relativeStart2)
linePath.closeSubPath()
if let physicsBody = SKPhysicsBody(polygonFrom:linePath.cgPath){
physicsBody.categoryBitMask = 0x80000000 //whatever you want it to be
physicsBody.contactBitMask = ~physicsBody.categoryBitMask //touch everything but the line
physicsBody.collisionBitMask = 0
physicsBody.affectedByGravity = false
node.physicsBody = physicsBody
}
scene.addChild(node)
node.position = mid
}
func didBegin(_ contact: SKPhysicsContact) {
let firstBody = contact.bodyA.categeroyBitMask > contact.bodyB.categoryBitMask ? contact.bodyA : contact.bodyB
let secondBody = contact.bodyA = firstBody ? contact.bodyB : contact.bodyA
if firstBody.categoryBitMask == 0x80000000{
//use contact at this point to check intersection points
//https://developer.apple.com/documentation/spritekit/skphysicscontact
}
}
Upvotes: 0