AVAEGamingExample/GameViewController.m
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
GameViewController |
*/ |
#import "AudioEngine.h" |
#import "GameView.h" |
#import "GameViewController.h" |
@interface GameViewController() <AudioEngineDelegate> { |
AudioEngine *_audioEngine; |
} |
@property (assign) GameView *gameView; |
@end |
@implementation GameViewController |
static float randFloat(float min, float max){ |
return min + ((max-min)*(rand()/(float)RAND_MAX)); |
} |
- (void)physicsWorld:(SCNPhysicsWorld *)world didEndContact:(SCNPhysicsContact *)contact |
{ |
if (contact.collisionImpulse > 0.6 /* arbitrary threshold */) { |
AVAudio3DPoint contactPoint = AVAudioMake3DPoint(contact.contactPoint.x, contact.contactPoint.y, contact.contactPoint.z); |
[_audioEngine playCollisionSoundForSCNNode:contact.nodeB position:contactPoint impulse:contact.collisionImpulse]; |
} |
} |
- (void)setupPhysicsScene:(SCNScene *)scene |
{ |
SCNNode *cube = [scene.rootNode childNodeWithName:@"Cube" recursively:YES]; |
cube.castsShadow = NO; |
SCNNode *lightNode = [scene.rootNode childNodeWithName:@"MainLight" recursively:YES]; |
lightNode.light.shadowMode = SCNShadowModeModulated; |
SCNVector3 min, max; |
[cube getBoundingBoxMin:&min max:&max]; |
float cubeSize = (max.y-min.y) * cube.scale.y; |
float wallWidth = 1; |
SCNBox *wallGeometry = [SCNBox boxWithWidth:cubeSize height:cubeSize length:wallWidth chamferRadius:0]; |
wallGeometry.firstMaterial.transparency = 0; |
float wallPosition = (cubeSize/2 + wallWidth/2); |
SCNNode *wall1 = [SCNNode node]; |
wall1.name = @"Wall"; |
wall1.geometry = wallGeometry; |
wall1.physicsBody = [SCNPhysicsBody staticBody]; |
wall1.physicsBody.contactTestBitMask = SCNPhysicsCollisionCategoryDefault; |
wall1.position = SCNVector3Make(0, 0, -wallPosition); |
[scene.rootNode addChildNode:wall1]; |
SCNNode *wall2 = [wall1 copy]; |
wall2.position = SCNVector3Make(0, 0, wallPosition); |
wall2.eulerAngles = SCNVector3Make(M_PI, 0, 0); |
[scene.rootNode addChildNode:wall2]; |
SCNNode *wall3 = [wall1 copy]; |
wall3.position = SCNVector3Make(-wallPosition, 0, 0); |
wall3.eulerAngles = SCNVector3Make(0, M_PI_2, 0); |
[scene.rootNode addChildNode:wall3]; |
SCNNode *wall4 = [wall1 copy]; |
wall4.position = SCNVector3Make(wallPosition, 0, 0); |
wall4.eulerAngles = SCNVector3Make(0, -M_PI_2, 0); |
[scene.rootNode addChildNode:wall4]; |
SCNNode *wall5 = [wall1 copy]; |
wall5.position = SCNVector3Make(0, wallPosition, 0); |
wall5.eulerAngles = SCNVector3Make(M_PI_2, 0, 0); |
[scene.rootNode addChildNode:wall5]; |
SCNNode *wall6 = [wall1 copy]; |
wall6.position = SCNVector3Make(0, -wallPosition, 0); |
wall6.eulerAngles = SCNVector3Make(-M_PI_2, 0, 0); |
[scene.rootNode addChildNode:wall6]; |
SCNNode *pointOfViewCamera = [scene.rootNode childNodeWithName:@"Camera" recursively:YES]; |
self.gameView.pointOfView = pointOfViewCamera; |
SCNNode *listener = [scene.rootNode childNodeWithName:@"listenerLight" recursively:YES]; |
listener.position = pointOfViewCamera.position; |
// setup physics callbacks |
scene.physicsWorld.contactDelegate = self; |
// turn off gravity for more fun |
//scene.physicsWorld.gravity = SCNVector3Zero; |
} |
- (void)createAndLaunchBall:(NSString*)ballID |
{ |
[SCNTransaction begin]; |
SCNNode *ball = [SCNNode node]; |
ball.name = ballID; |
ball.geometry = [SCNSphere sphereWithRadius:0.2]; |
ball.geometry.firstMaterial.diffuse.contents = @"assets.scnassets/texture.jpg"; |
ball.geometry.firstMaterial.reflective.contents = @"assets.scnassets/envmap.jpg"; |
ball.position = SCNVector3Make(0, -2, 2.5); |
ball.physicsBody = [SCNPhysicsBody dynamicBody]; |
ball.physicsBody.restitution = 1.2; //bounce! |
// create an AVAudioEngine player which will be tied to this ball |
[_audioEngine createPlayerForSCNNode:ball]; |
[self.gameView.scene.rootNode addChildNode:ball]; |
// bias the direction towards one of the side walls |
float whichWall = roundf(randFloat(0, 1)); // 0 is left and 1 is right |
float xVal = (1 - whichWall) * randFloat(-8, -3) + whichWall * randFloat(3, 8); |
// initial force |
[ball.physicsBody applyForce:SCNVector3Make(xVal, randFloat(0,5), -randFloat(5,15)) atPosition:SCNVector3Zero impulse:YES]; |
[ball.physicsBody applyTorque:SCNVector4Make(randFloat(-1,1), randFloat(-1,1), randFloat(-1,1), randFloat(-1,1)) impulse:YES]; |
[SCNTransaction commit]; |
} |
- (void)removeBall:(SCNNode *)ball |
{ |
[_audioEngine destroyPlayerForSCNNode:ball]; |
[ball removeFromParentNode]; |
} |
-(void)viewDidLoad |
{ |
[super viewDidLoad]; |
// create a new scene |
SCNScene *scene = [SCNScene sceneNamed:@"cube.scn" inDirectory:@"assets.scnassets" options:nil]; |
// find the SCNView |
for (id view in self.view.subviews) { |
if ([[view class] isSubclassOfClass:[SCNView class]]) { |
self.gameView = view; |
} |
} |
// set the scene to the view |
self.gameView.scene = scene; |
// setup the room |
[self setupPhysicsScene:scene]; |
// setup audio engine |
_audioEngine = [[AudioEngine alloc] init]; |
//make the listener position the same as the camera point of view |
[_audioEngine updateListenerPosition:AVAudioMake3DPoint(0, -2, 2.5)]; |
self.gameView.gameAudioEngine = _audioEngine; |
// create a queue that will handle adding SceneKit nodes (balls) and corresponding AVAudioEngine players |
dispatch_queue_t queue = dispatch_queue_create("DemBalls", DISPATCH_QUEUE_SERIAL); |
dispatch_async(queue, ^{ |
while (1) { |
while (_audioEngine.isRunning) { |
static int ballIndex = 0; |
// play the launch sound |
[_audioEngine playLaunchSoundAtPosition:AVAudioMake3DPoint(0, -2, 2.5) completionHandler:^{ |
// launch sound has finished scheduling |
// now create and launch a ball |
NSString *ballID = [[NSNumber numberWithInt:ballIndex] stringValue]; |
[self createAndLaunchBall:ballID]; |
++ballIndex; |
}]; |
// wait for some time before launching the next ball |
sleep(4); |
} |
} |
}); |
// configure the view |
#if TARGET_OS_IOS ||TARGET_OS_TV |
self.gameView.backgroundColor = [UIColor blackColor]; |
#else |
self.gameView.backgroundColor = [NSColor blackColor]; |
#endif |
} |
@end |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-11-03