/* |
Copyright (C) 2014 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
|
Abstract: |
|
This class simulates the monkeys in the game. It includes game logic for determining each monkey's actions and also manages the monkey's animations. |
|
*/ |
|
#import <GLKit/GLKit.h> |
#import "AAPLMonkeyCharacter.h" |
#import "AAPLGameSimulation.h" |
#import "AAPLGameLevel.h" |
#import "AAPLCoconut.h" |
#import "AAPLPlayerCharacter.h" |
#import "AAPLMathUtils.h" |
|
@interface AAPLMonkeyCharacter () { |
BOOL isIdle; |
BOOL hasCoconut; |
} |
|
@end |
|
@implementation AAPLMonkeyCharacter |
|
- (void)createAnimations |
{ |
|
self.name = @"monkey"; |
self.rightHand = [self childNodeWithName:@"Bone_R_Hand" recursively:YES]; |
|
isIdle = YES; |
hasCoconut = NO; |
|
//load and cache animations |
[self setupTauntAnimation]; |
[self setupHangAnimation]; |
[self setupGetCoconutAnimation]; |
[self setupThrowAnimation]; |
|
//-- Sequence: get -> throw |
[self chainAnimation:@"monkey_get_coconut-1" toAnimation:@"monkey_throw_coconut-1"]; |
|
// start the ball rolling with hanging in the tree. |
[self.mainSkeleton addAnimation:[self cachedAnimationForKey:@"monkey_tree_hang-1"] forKey:@"monkey_idle"]; |
} |
|
- (void)setupTauntAnimation |
{ |
CAAnimation *taunt = [self loadAndCacheAnimation:[AAPLGameSimulation pathForArtResource:@"characters/monkey/monkey_tree_hang_taunt"] |
forKey:@"monkey_tree_hang_taunt-1"]; |
|
taunt.repeatCount = 0; |
|
SCNAnimationEventBlock ackBlock = ^(CAAnimation *animation, id animatedObject, BOOL playingBackward) { |
[[AAPLGameSimulation sim] playSound:@"ack.caf"]; |
}; |
|
taunt.animationEvents = @[[SCNAnimationEvent animationEventWithKeyTime:0.0f block:ackBlock], |
[SCNAnimationEvent animationEventWithKeyTime:1.0f block:^(CAAnimation *animation, id animatedObject, BOOL playingBackward) { |
isIdle = YES; |
} |
]]; |
} |
|
- (void)setupHangAnimation |
{ |
CAAnimation *hang = [self loadAndCacheAnimation:[AAPLGameSimulation pathForArtResource:@"characters/monkey/monkey_tree_hang"] |
forKey:@"monkey_tree_hang-1"]; |
hang.repeatCount = MAXFLOAT; |
} |
|
- (void)setupGetCoconutAnimation |
{ |
SCNAnimationEventBlock pickupEventBlock = ^(CAAnimation *animation, id animatedObject, BOOL playingBackward) { |
[self.coconut removeFromParentNode]; |
self.coconut = [AAPLCoconut coconutProtoObject]; |
[self.rightHand addChildNode:self.coconut]; |
hasCoconut = YES; |
}; |
|
CAAnimation *getAnimation = [self loadAndCacheAnimation:[AAPLGameSimulation pathForArtResource:@"characters/monkey/monkey_get_coconut"] forKey:@"monkey_get_coconut-1"]; |
if (getAnimation.animationEvents == nil) { |
getAnimation.animationEvents = @[[SCNAnimationEvent animationEventWithKeyTime:0.40f block:pickupEventBlock]]; |
} |
|
getAnimation.repeatCount = 0; |
} |
|
- (void)setupThrowAnimation |
{ |
CAAnimation *throw = [self loadAndCacheAnimation:[AAPLGameSimulation pathForArtResource:@"characters/monkey/monkey_throw_coconut"] forKey:@"monkey_throw_coconut-1"]; |
throw.speed = 1.5f; |
if (throw.animationEvents == nil || throw.animationEvents.count == 0) { |
SCNAnimationEventBlock throwEventBlock = ^(CAAnimation *animation, id animatedObject, BOOL playingBackward) { |
|
if (hasCoconut) { |
SCNMatrix4 worldMtx = self.coconut.presentationNode.worldTransform; |
[self.coconut removeFromParentNode]; |
|
AAPLCoconut *node = [AAPLCoconut coconutThrowProtoObject]; |
SCNPhysicsShape *coconutPhysicsShape = [AAPLCoconut coconutPhysicsShape]; |
node.physicsBody = [SCNPhysicsBody bodyWithType:SCNPhysicsBodyTypeDynamic shape:coconutPhysicsShape]; |
node.physicsBody.restitution = 0.9; |
node.physicsBody.collisionBitMask = GameCollisionCategoryPlayer | GameCollisionCategoryGround; |
node.physicsBody.categoryBitMask = GameCollisionCategoryCoconut; |
|
node.transform = worldMtx; |
[[AAPLGameSimulation sim].rootNode addChildNode:node]; |
[[AAPLGameSimulation sim].gameLevel.coconuts addObject:node]; |
[node.physicsBody applyForce:SCNVector3Make(-200, 500, 300) impulse:YES]; |
hasCoconut = NO; |
isIdle = YES; |
} |
}; |
throw.animationEvents = @[[SCNAnimationEvent animationEventWithKeyTime:0.35f block:throwEventBlock]]; |
} |
|
throw.repeatCount = 0; |
} |
|
/*! update the Monkey and decide when to throw a coconut |
*/ |
- (void)update:(NSTimeInterval)deltaTime |
{ |
CGFloat distanceToCharacter = FLT_MAX; |
AAPLPlayerCharacter *playerCharacter = [AAPLGameSimulation sim].gameLevel.playerCharacter; |
|
SCNVector3 pos = AAPLMatrix4GetPosition(self.presentationNode.worldTransform); |
GLKVector3 myPosition = GLKVector3Make(pos.x, pos.y, pos.z); |
|
// If the player is to the left of the monkey, calculate how far away the character is. |
if (playerCharacter.position.x < myPosition.x) { |
distanceToCharacter = GLKVector3Distance(SCNVector3ToGLKVector3(playerCharacter.position), myPosition); |
} |
|
// If the character is close enough and not moving, throw a coconut. |
if (distanceToCharacter < 700) { |
if (isIdle) { |
if (playerCharacter.isRunning == YES) { |
[self.mainSkeleton addAnimation:[self cachedAnimationForKey:@"monkey_get_coconut-1"] forKey:nil]; |
isIdle = NO; |
} else { |
// taunt the player if they aren't moving. |
if (AAPLRandomPercent() <= 0.001f) { |
isIdle = NO; |
[self.mainSkeleton addAnimation:[self cachedAnimationForKey:@"monkey_tree_hang_taunt-1"] |
forKey:nil]; |
} |
} |
} |
|
} |
} |
|
@end |