问题
I can't get the didBeginContact method to fire, I have been trying for a while and I can't spot the error, could use a fresh set of eyes:
- (void)viewDidLoad {
[super viewDidLoad];
self.lastRender = nil;
self.accelX = 0.0;
self.accelY = 0.0;
self.accelZ = 0.0;
self.isLooping = TRUE;
self.tripWire = TRUE;
self.lastPaddleNode = [[SCNNode alloc] init];
self.paddleNode = [[SCNNode alloc] init];
SCNPlane* paddlePlane = [SCNPlane planeWithWidth:0.067056 height:0.138176];
self.paddleNode.geometry = paddlePlane;
self.paddleNode.geometry.firstMaterial.diffuse.contents = [UIColor colorWithRed:133.0/255.0f green:158.0/255.0f blue:122.0/255.0f alpha:0.8];
self.paddleNode.simdTransform = matrix_identity_float4x4;
self.paddleNode.name = @"paddle";
SCNPhysicsBody* paddlePhys = [[SCNPhysicsBody alloc] init];
paddlePhys.type = SCNPhysicsBodyTypeDynamic;
paddlePhys.physicsShape = [SCNPhysicsShape shapeWithGeometry:paddlePlane options:nil];
//paddlePhys.affectedByGravity = FALSE;
paddlePhys.categoryBitMask = SCNPhysicsCollisionCategoryAll;
paddlePhys.collisionBitMask = SCNPhysicsCollisionCategoryAll;
paddlePhys.contactTestBitMask = SCNPhysicsCollisionCategoryAll;
paddlePhys.usesDefaultMomentOfInertia = TRUE;
self.paddleNode.physicsBody = paddlePhys;
SCNNode* ball = [[SCNNode alloc] init];
SCNPhysicsBody* phys = [[SCNPhysicsBody alloc] init];
phys.type = SCNPhysicsBodyTypeDynamic;
SCNSphere* sphere = [SCNSphere sphereWithRadius:0.067f];
phys.physicsShape = [SCNPhysicsShape shapeWithGeometry:sphere options:nil];
ball.geometry = sphere;
ball.geometry.firstMaterial.diffuse.contents = [UIColor yellowColor];
//phys.affectedByGravity = FALSE;
phys.categoryBitMask = SCNPhysicsCollisionCategoryAll;
phys.collisionBitMask = SCNPhysicsCollisionCategoryAll;
phys.contactTestBitMask = SCNPhysicsCollisionCategoryAll;
phys.usesDefaultMomentOfInertia = TRUE;
phys.velocityFactor = SCNVector3Make(10.0, 10.0, 10.0);
ball.physicsBody = phys;
ball.physicsBody.continuousCollisionDetectionThreshold = 0.134f;
ball.simdPosition = simd_make_float3(0.0, 0.27, -0.27);
ball.name = @"ball";
//phys.velocityFactor = SCNVector3Make(10, 10, 10);
[self.sceneView setPreferredFramesPerSecond:60];
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
// Set the view's delegate
self.sceneView.delegate = self;
// Show statistics such as fps and timing information
self.sceneView.showsStatistics = YES;
//phys.velocityFactor = SCNVector3Make(10, 10, 10);
// Create a new scene
SCNScene* scene = [[SCNScene alloc] init];
scene.physicsWorld.contactDelegate = self;
self.sceneView.scene = scene;
[locationManager startUpdatingHeading];
while(!(self.locationManager.heading.trueHeading <= 360 && self.locationManager.heading.trueHeading >= 359)) {
NSLog(@"heading: %f", self.locationManager.heading.trueHeading);
if (self.locationManager.heading.trueHeading <= 360 && self.locationManager.heading.trueHeading >= 359) {
break;
}
}
[self.sceneView.scene.rootNode addChildNode:self.paddleNode];
[self.sceneView.scene.rootNode addChildNode:ball];
[self startUpdates];
}
All I am trying to do write now is the delegate method to fire when the two nodes in my game collide:
- (void)physicsWorld:(SCNPhysicsWorld *)world didBeginContact:(SCNPhysicsContact *)contact {
NSLog(@"in didBeginContact");
CollisionCategory contactMask =
contact.nodeA.physicsBody.categoryBitMask | contact.nodeB.physicsBody.categoryBitMask;
// first, sort out what kind of collision
if (contactMask == (CollisionCategoryPaddle | CollisionCategoryBall)) {
// next, sort out which body is the missile and which is the rocket
// and do something about it
if (contact.nodeA.physicsBody.categoryBitMask == CollisionCategoryPaddle) {
NSLog(@"nodeA is paddle!");
//[self hitRocket:contact.nodeB withMissile:contact.nodeA];
} else {
NSLog(@"nodeB is paddle!");
//[self hitRocket:contact.nodeA withMissile:contact.nodeB];
}
}
}
Note
I am aware that I am using SCNPhysicsCollisionCategoryAll and then checking for a different kind of bitmask in the method. That is not my problem, my problem is before that, I never see this log statement, which is the first line in my didBeginContact delegate method:
NSLog(@"in didBeginContact");
Why isn't my method firing? I think I set the delegate correctly. Thanks.
UPDATE
My viewDidLoad looks like this now, per commenter's suggestion:
- (void)viewDidLoad {
[super viewDidLoad];
// Create a new scene
SCNScene* scene = [[SCNScene alloc] init];
scene.physicsWorld.contactDelegate = self;
self.sceneView.scene = scene;
// Set the view's delegate
self.sceneView.delegate = self;
SCNPlane* paddlePlane = [SCNPlane planeWithWidth:0.067056 height:0.138176];
self.paddleNode = [SCNNode nodeWithGeometry:paddlePlane];
self.paddleNode.geometry.firstMaterial.diffuse.contents = [UIColor colorWithRed:133.0/255.0f green:158.0/255.0f blue:122.0/255.0f alpha:0.8];
self.paddleNode.simdTransform = matrix_identity_float4x4;
self.paddleNode.name = @"paddle";
[self.sceneView.scene.rootNode addChildNode:self.paddleNode];
SCNPhysicsBody* paddlePhys = [[SCNPhysicsBody alloc] init];
paddlePhys.type = SCNPhysicsBodyTypeDynamic;
paddlePhys.physicsShape = [SCNPhysicsShape shapeWithGeometry:self.paddleNode.geometry options:@{SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeBoundingBox}];
//paddlePhys.affectedByGravity = FALSE;
paddlePhys.categoryBitMask = SCNPhysicsCollisionCategoryAll;
paddlePhys.collisionBitMask = SCNPhysicsCollisionCategoryAll;
paddlePhys.contactTestBitMask = SCNPhysicsCollisionCategoryAll;
//paddlePhys.usesDefaultMomentOfInertia = TRUE;
self.paddleNode.physicsBody = paddlePhys;
SCNNode* ball = [[SCNNode alloc] init];
[self.sceneView.scene.rootNode addChildNode:ball];
SCNPhysicsBody* phys = [[SCNPhysicsBody alloc] init];
phys.type = SCNPhysicsBodyTypeDynamic;
SCNSphere* sphere = [SCNSphere sphereWithRadius:0.067f];
phys.physicsShape = [SCNPhysicsShape shapeWithGeometry:sphere options:@{SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeConvexHull}];
ball.geometry = sphere;
ball.geometry.firstMaterial.diffuse.contents = [UIColor yellowColor];
//phys.affectedByGravity = FALSE;
phys.categoryBitMask = SCNPhysicsCollisionCategoryAll;
phys.collisionBitMask = SCNPhysicsCollisionCategoryAll;
phys.contactTestBitMask = SCNPhysicsCollisionCategoryAll;
phys.usesDefaultMomentOfInertia = TRUE;
phys.velocityFactor = SCNVector3Make(10.0, 10.0, 10.0);
ball.physicsBody = phys;
ball.physicsBody.continuousCollisionDetectionThreshold = 0.134f;
ball.simdPosition = simd_make_float3(0.0, 0.27, -0.27);
ball.name = @"ball";
//phys.velocityFactor = SCNVector3Make(10, 10, 10);
// Show statistics such as fps and timing information
self.sceneView.showsStatistics = YES;
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingHeading];
while(!(self.locationManager.heading.trueHeading <= 360 && self.locationManager.heading.trueHeading >= 358)) {
NSLog(@"heading: %f", self.locationManager.heading.trueHeading);
if (self.locationManager.heading.trueHeading <= 360 && self.locationManager.heading.trueHeading >= 358) {
break;
}
}
[self.sceneView setPreferredFramesPerSecond:60];
self.lastRender = nil;
self.accelX = 0.0;
self.accelY = 0.0;
self.accelZ = 0.0;
self.isLooping = TRUE;
self.tripWire = TRUE;
self.lastPaddleNode = [[SCNNode alloc] init];
[self startUpdates];
}
I do agree with the commenter that this is probably a problem with how things are ordered here, I just can't see what it is...helllppp, I'm droowwnnninng...
UPDATE
I changed my bitwise masks as follows, I didn't use a custom enum type because I only have two objects, so I think I can just use ...All and ...Default, but it isn't working like this. I will try custom enum type when I understand the bitwise AND operation, if someone could explain to me the left handed/right handed part, and how to do the operation?
//Paddle
paddlePhys.categoryBitMask = SCNPhysicsCollisionCategoryDefault;
paddlePhys.collisionBitMask = SCNPhysicsCollisionCategoryAll;
paddlePhys.contactTestBitMask = SCNPhysicsCollisionCategoryAll;
...
//Ball
phys.categoryBitMask = SCNPhysicsCollisionCategoryAll;
phys.collisionBitMask = SCNPhysicsCollisionCategoryDefault;
phys.contactTestBitMask = SCNPhysicsCollisionCategoryDefault;
Should this work? I also have my paddle set like this now:
paddlePhys.type = SCNPhysicsBodyTypeKinematic;
paddlePhys.physicsShape = [SCNPhysicsShape shapeWithGeometry:self.paddleNode.geometry options:@{SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeBoundingBox}];
My ball is this:
phys.type = SCNPhysicsBodyTypeDynamic;
phys.physicsShape = [SCNPhysicsShape shapeWithGeometry:sphere options:@{SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeConvexHull}];
All together right now I have:
- (void)viewDidLoad {
[super viewDidLoad];
self.sceneView.delegate = self;
// Show statistics such as fps and timing information
self.sceneView.showsStatistics = YES;
SCNScene* scene = [[SCNScene alloc] init];
// Create a new scene
SCNPlane* paddlePlane = [SCNPlane planeWithWidth:0.067056 height:0.138176];
paddlePlane.firstMaterial.doubleSided = YES;
self.paddleNode = [SCNNode nodeWithGeometry:paddlePlane];
self.paddleNode.geometry.firstMaterial.diffuse.contents = [UIColor colorWithRed:133.0/255.0f green:158.0/255.0f blue:122.0/255.0f alpha:0.8];
self.paddleNode.simdTransform = matrix_identity_float4x4;
self.paddleNode.name = @"paddle";
SCNPhysicsBody* paddlePhys = [[SCNPhysicsBody alloc] init];
paddlePhys.type = SCNPhysicsBodyTypeKinematic;
paddlePhys.physicsShape = [SCNPhysicsShape shapeWithGeometry:self.paddleNode.geometry options:@{SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeBoundingBox}];
//paddlePhys.affectedByGravity = FALSE;
paddlePhys.categoryBitMask = SCNPhysicsCollisionCategoryDefault;
paddlePhys.collisionBitMask = SCNPhysicsCollisionCategoryAll;
paddlePhys.contactTestBitMask = SCNPhysicsCollisionCategoryAll;
//paddlePhys.usesDefaultMomentOfInertia = TRUE;
self.paddleNode.physicsBody = paddlePhys;
self.paddleNode.simdPivot = [self makeTranslationMatrix:0 ty: 0 tz:0.1];
SCNNode* ball = [[SCNNode alloc] init];
SCNPhysicsBody* phys = [[SCNPhysicsBody alloc] init];
SCNSphere* sphere = [SCNSphere sphereWithRadius:0.067f];
phys.type = SCNPhysicsBodyTypeDynamic;
phys.physicsShape = [SCNPhysicsShape shapeWithGeometry:sphere options:@{SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeConvexHull}];
ball.geometry = sphere;
ball.geometry.firstMaterial.diffuse.contents = [UIColor yellowColor];
//phys.affectedByGravity = FALSE;
phys.categoryBitMask = SCNPhysicsCollisionCategoryAll;
phys.collisionBitMask = SCNPhysicsCollisionCategoryDefault;
phys.contactTestBitMask = SCNPhysicsCollisionCategoryDefault;
phys.usesDefaultMomentOfInertia = TRUE;
phys.velocityFactor = SCNVector3Make(10.0, 10.0, 10.0);
ball.physicsBody = phys;
ball.physicsBody.continuousCollisionDetectionThreshold = 0.134f;
ball.simdPosition = simd_make_float3(0.0, 0.27, -0.27);
ball.name = @"ball";
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingHeading];
while(!(self.locationManager.heading.trueHeading <= 360 && self.locationManager.heading.trueHeading >= 358)) {
NSLog(@"heading: %f", self.locationManager.heading.trueHeading);
if (self.locationManager.heading.trueHeading <= 360 && self.locationManager.heading.trueHeading >= 358) {
break;
}
}
[self.sceneView setPreferredFramesPerSecond:60];
self.lastRender = nil;
self.accelX = 0.0;
self.accelY = 0.0;
self.accelZ = 0.0;
self.isLooping = TRUE;
self.tripWire = TRUE;
self.lastPaddleNode = [[SCNNode alloc] init];
[scene.rootNode addChildNode:ball];
[scene.rootNode addChildNode:self.paddleNode];
scene.physicsWorld.contactDelegate = self;
self.sceneView.scene = scene;
[self startUpdates];
}
I am also wondering if the problem might be in the block for the CMDeviceMotion events, here is that:
- (void)startUpdates {
//dispatch_queue_t myQueue = dispatch_queue_create("com.eamon.corona_pong", DISPATCH_QUEUE_SERIAL);
// Determine the update interval.
NSTimeInterval updateInterval = 1.0/60.0;
// Create a CMMotionManager object.
CMMotionManager *mManager = [(AppDelegate *)[[UIApplication sharedApplication] delegate] sharedManager];
if ([mManager isDeviceMotionAvailable] == YES) {
[mManager setDeviceMotionUpdateInterval:updateInterval];
// do something appropriate here
if ([CMMotionManager availableAttitudeReferenceFrames] & CMAttitudeReferenceFrameXTrueNorthZVertical) {
[mManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical toQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
//[self handleMotionWrapper:motion];
[SCNTransaction begin];
[SCNTransaction setAnimationDuration:0];
//simd_float4x4 rotateY = [self makeYRotationMatrix:270.0];
//simd_float4x4 rotate = [self makeXRotationMatrix:270.0];
simd_float4x4 cameraTrans = self.sceneView.session.currentFrame.camera.transform;
simd_float4x4 temp = simd_mul(cameraTrans, [self makeYRotationMatrix:270.0]);
//simd_float4x4 rotated = simd_mul(temp, [self makeYRotationMatrix:135]);
simd_float4x4 transMatrixMotion = [self makeTranslationMatrix:self.lastPaddleNode.simdPosition.x ty:self.lastPaddleNode.simdPosition.y - 0.1 tz:self.lastPaddleNode.simdPosition.z];
self.paddleNode.simdPosition = simd_make_float3(simd_mul(simd_make_float4(temp.columns[3][0], temp.columns[3][1], temp.columns[3][2], 1.0), transMatrixMotion));
self.paddleNode.simdTransform = temp;
[SCNTransaction commit];
self.lastPaddleNode = self.paddleNode;
self.tripWire = FALSE;
}];
}
}
}
I'm am still working out the motion code, but I figure the collision should work, even though I don't have the paddle oriented exactly how I want yet (I just need to rotate it 90 degrees around the y-axis, but it is proving to be annoying, but shouldn't matter for collision detection, right?)
Also, what about SCNPlane, I also made my SCNPlane (the paddle) double-sided with the doubleSided attribute of the firstMaterial attribute but it did not help, maybe I need to use a shallow SCNBox? I have tried it before, but maybe some of the changes I have made would mean it would work if I make my paddle and SCNBox instead of a double-sided SCNPlane?
THANKS!
UPDATE I thought I would share my latest attempt, doesn't work but I think it might be closer, might be easier to spot the problem:
typedef NS_OPTIONS(NSUInteger, CollisionCategory)
{
CollisionCategoryPaddle = 1 << 0,
CollisionCategoryBall = 1 << 1,
};
- (void)viewDidLoad {
[super viewDidLoad];
self.sceneView.delegate = self;
// Show statistics such as fps and timing information
self.sceneView.showsStatistics = YES;
SCNScene* scene = [[SCNScene alloc] init];
self.sceneView.scene = scene;
self.sceneView.scene.physicsWorld.contactDelegate = self;
// Create a new scene
SCNBox* paddlePlane = [SCNBox boxWithWidth:0.067056 height:0.138176 length:0.01 chamferRadius:0.0];
paddlePlane.firstMaterial.doubleSided = YES;
self.paddleNode = [SCNNode nodeWithGeometry:paddlePlane];
self.paddleNode.geometry.firstMaterial.diffuse.contents = [UIColor colorWithRed:133.0/255.0f green:158.0/255.0f blue:122.0/255.0f alpha:0.8];
self.paddleNode.simdTransform = matrix_identity_float4x4;
self.paddleNode.name = @"paddle";
[self.sceneView.scene.rootNode addChildNode:self.paddleNode];
SCNPhysicsBody* paddlePhys = [[SCNPhysicsBody alloc] init];
paddlePhys.type = SCNPhysicsBodyTypeKinematic;
paddlePhys.physicsShape = [SCNPhysicsShape shapeWithGeometry:self.paddleNode.geometry options:nil];
//paddlePhys.affectedByGravity = FALSE;
paddlePhys.categoryBitMask = CollisionCategoryPaddle;
paddlePhys.collisionBitMask = CollisionCategoryBall;
paddlePhys.contactTestBitMask = CollisionCategoryBall;
//paddlePhys.usesDefaultMomentOfInertia = TRUE;
self.paddleNode.physicsBody = paddlePhys;
SCNPhysicsBody* phys = [[SCNPhysicsBody alloc] init];
SCNSphere* sphere = [SCNSphere sphereWithRadius:0.067f];
self.ballNode = [[SCNNode alloc] init];
self.ballNode.name = @"ball";
self.ballNode.geometry = sphere;
self.ballNode.geometry.firstMaterial.diffuse.contents = [UIColor yellowColor];
//self.ballNode.physicsBody.continuousCollisionDetectionThreshold = (CGFloat)0.134f;
self.ballNode.simdPosition = simd_make_float3(0.0, 0.27, -0.27);
[self.sceneView.scene.rootNode addChildNode:self.ballNode];
phys.type = SCNPhysicsBodyTypeDynamic;
phys.physicsShape = [SCNPhysicsShape shapeWithGeometry:self.ballNode.geometry options:nil];
phys.categoryBitMask = CollisionCategoryBall;
phys.collisionBitMask = CollisionCategoryPaddle;
phys.contactTestBitMask = CollisionCategoryPaddle;
//phys.usesDefaultMomentOfInertia = TRUE;
//phys.velocityFactor = SCNVector3Make(10.0, 10.0, 10.0);
self.ballNode.physicsBody = phys;
//phys.affectedByGravity = FALSE
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingHeading];
while(!(self.locationManager.heading.trueHeading <= 360 && self.locationManager.heading.trueHeading >= 358)) {
NSLog(@"heading: %f", self.locationManager.heading.trueHeading);
if (self.locationManager.heading.trueHeading <= 360 && self.locationManager.heading.trueHeading >= 358) {
break;
}
}
[self.sceneView setPreferredFramesPerSecond:60];
self.lastRender = nil;
self.accelX = 0.0;
self.accelY = 0.0;
self.accelZ = 0.0;
self.isLooping = TRUE;
self.tripWire = TRUE;
self.lastPaddleNode = [[SCNNode alloc] init];
[self startUpdates];
}
I tried this way too, althought I'm not sure if I am using it right:
phys.physicsShape = [SCNPhysicsShape shapeWithGeometry:self.ballNode.geometry options:@{SCNHitTestOptionCategoryBitMask:[NSNumber numberWithUnsignedInt:CollisionCategoryBall], SCNPhysicsTestCollisionBitMaskKey:[NSNumber numberWithUnsignedInt:CollisionCategoryPaddle]}];
回答1:
1.) Add the
sceneto thesceneView2.) Declare the nodes you will be using for the objects you will be giving physics too.
3.) Make necessary modifications to instantiated objects (color, etc.).
--ATTN STEP 4 is VERY IMPORTANT--
4.) Only after above steps (1, 2, and 3) do you add the
physicsBodydetails to the object! (a.k.anode.physicsBody.xxx)
- Add the nodes to the scene.
In my opinion the way that SceneKit handles this part of things is ridiculous and needs revision. Too many hours spent in a situation where an SCNNode that hasn't even been added to scene yet, is causing problems because of the order your are instantiating it's attributes :(
来源:https://stackoverflow.com/questions/61315179/didbegincontact-delegate-method-not-firing-for-arkit-collision-detection