Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Shootting Game

1,219 views

Published on

Sprite Kit Case - Shooting Game

Published in: Entertainment & Humor
  • Be the first to comment

Shootting Game

  1. 1. Sprite Kit - Case Study Michael Pan
  2. 2. For what Storybook! 2D game
  3. 3. Storybook https://www.youtube.com/watch?v=r_3TJtK1PjU
  4. 4. About Game 2D - Corona! 3D - Unity
  5. 5. What we will build http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners
  6. 6. Create a Sprite Kit Project Shooter
  7. 7. Make it landscape only
  8. 8. Run the project
  9. 9. Download the resource http://cdn3.raywenderlich.com/wp-content/uploads/2015/01/ SpriteKitSimpleGameResources.zip
  10. 10. Drag resource into project
  11. 11. MyScene.m #import "MyScene.h"! ! @interface MyScene ()! @property (nonatomic) SKSpriteNode * player;! @end! ! @implementation MyScene! ! -(id)initWithSize:(CGSize)size { ! if (self = [super initWithSize:size]) {! ! NSLog(@"Size: %@", NSStringFromCGSize(size));! ! self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];! self.player = [SKSpriteNode spriteNodeWithImageNamed:@"player"];! self.player.position = CGPointMake(100, 100);! [self addChild:self.player];! }! return self;! }! @end
  12. 12. Run
  13. 13. Check the log ??
  14. 14. why http://stackoverflow.com/questions/9539676/uiviewcontroller-returns-invalid- frame
  15. 15. Modify the ViewController.m - (void)viewDidLoad! {! [super viewDidLoad];! ! // Configure the view.! SKView * skView = (SKView *)self.view;! skView.showsFPS = YES;! skView.showsNodeCount = YES;! ! // Create and configure the scene.! SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];! scene.scaleMode = SKSceneScaleModeAspectFill;! ! // Present the scene.! [skView presentScene:scene];! }
  16. 16. viewDidAppear -(void) viewDidAppear:(BOOL)animated{! [super viewDidAppear:animated];! SKView * skView = (SKView *)self.view;! ! skView.showsFPS = YES;! skView.showsNodeCount = YES;! ! // Create and configure the scene.! SKScene * scene = [MyScene sceneWithSize:skView.bounds.size];! scene.scaleMode = SKSceneScaleModeAspectFill;! ! // Present the scene.! [skView presentScene:scene];! }
  17. 17. Good result
  18. 18. What we learned SKScene SKSpriteNode self.player = [SKSpriteNode spriteNodeWithImageNamed:@"player"];! self.player.position = CGPointMake(100, 100);! [self addChild:self.player];
  19. 19. Class relationship SKNode SKEffectNode SKScene SKSpriteNode - (void)addChild:(SKNode *)node;
  20. 20. Position SKScene (0,0) (100,100)
  21. 21. Put SpriteNode on scene - right-down?? SKScene (0,0) (100,100)
  22. 22. Put SpriteNode on scene - left-up ?? SKScene (0,0) (100,100)
  23. 23. Put SpriteNode on scene - center ?? SKScene (0,0) (100,100)
  24. 24. Anchor point (0,0) (1,0) (0,1) (1,1) (0.5,0.5) default
  25. 25. Test - position (0,0) with default Anchor Point self.player = [SKSpriteNode spriteNodeWithImageNamed:@"player"];! self.player.position = CGPointMake(0, 0);! [self addChild:self.player];
  26. 26. Test - position (0,0) with default Anchor Point(0,0) self.player = [SKSpriteNode spriteNodeWithImageNamed:@"player"];! self.player.position = CGPointMake(0, 0);! self.player.anchorPoint = CGPointMake(0, 0);! [self addChild:self.player];
  27. 27. Add enemy in MyScene.m (1) - (void)addMonster {! // Create sprite! SKSpriteNode * monster = [SKSpriteNode spriteNodeWithImageNamed:@"monster"];!  ! // Determine where to spawn the monster along the Y axis! int minY = monster.size.height / 2;! int maxY = self.frame.size.height - monster.size.height / 2;! int rangeY = maxY - minY;! int actualY = (arc4random() % rangeY) + minY;! ! monster.position = CGPointMake(self.frame.size.width + monster.size.width/2, actualY);! [self addChild:monster];! }
  28. 28. Add enemy in MyScene.m (2) - (void)addMonster {!  ! //…! // Determine speed of the monster! int minDuration = 2.0;! int maxDuration = 4.0;! int rangeDuration = maxDuration - minDuration;! int actualDuration = (arc4random() % rangeDuration) + minDuration;!  ! // Create the actions! SKAction * actionMove = [SKAction moveTo:CGPointMake(-monster.size.width/2, actualY) duration:actualDuration];! SKAction * actionMoveDone = [SKAction removeFromParent];! [monster runAction:[SKAction sequence:@[actionMove, actionMoveDone]]];!  ! }
  29. 29. What we learned SKAction! moveTo:duration:! removeFromParent! sequence:
  30. 30. Class view NSObject SKNode SKSpriteNode SKAction - (void)runAction:(SKAction *)action; UIResponder
  31. 31. - (void)update:(NSTimeInterval)currentTime every 1/60 second will be called automatically
  32. 32. Stabilise the time interval @interface MyScene ()! @property (nonatomic) SKSpriteNode * player;! @property (nonatomic) NSTimeInterval lastSpawnTimeInterval;! @property (nonatomic) NSTimeInterval lastUpdateTimeInterval;! @end
  33. 33. Codes in update: - (void)update:(NSTimeInterval)currentTime {! CFTimeInterval timeSinceLast = currentTime - self.lastUpdateTimeInterval;! self.lastUpdateTimeInterval = currentTime;! if (timeSinceLast > 1) {! timeSinceLast = 1.0 / 60.0;! self.lastUpdateTimeInterval = currentTime;! }! [self updateWithTimeSinceLastUpdate:timeSinceLast];! ! }
  34. 34. Every second add a enemy - (void)updateWithTimeSinceLastUpdate: (CFTimeInterval)timeSinceLast {!  ! self.lastSpawnTimeInterval += timeSinceLast;! if (self.lastSpawnTimeInterval > 1) {! self.lastSpawnTimeInterval = 0;! [self addMonster];! }! }!
  35. 35. Run the app
  36. 36. Throw projectile touch
  37. 37. Throw projectile - vector (spriteX, spriteY) offsetX offsetY (touchX, touchY) offsetX = touchX - spriteX offsetY = touchY - spriteY
  38. 38. Define helper function - offset CGPoint subPoint(CGPoint a, CGPoint b ){! CGPoint subPoint = CGPointMake(a.x - b.x, a.y - b.y);! return subPoint;! }
  39. 39. Unit value of vector offsetX = touchX - spriteX offsetY = touchY - spriteY unitValue = sqrt(offsetX^2 + offsetY^2) Normalised vector = (offsetX / unitValue , offsetY / unitValue) offsetX offsetY unitValue
  40. 40. Define helper function - normalised offset CGPoint normalisedPoint(CGPoint offset){! CGFloat nValue = sqrtf(offset.x*offset.x + offset.y*offset.y);! CGPoint nPoint = CGPointMake(offset.x/nValue, offset.y/nValue);! return nPoint;! }
  41. 41. Get touch event - add projectile -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{! ! UITouch * touch = [touches anyObject];! ! // get the location in the scene! CGPoint location = [touch locationInNode:self] ;! ! SKSpriteNode * projectile = [SKSpriteNode spriteNodeWithImageNamed:@"projectile"];! projectile.position = self.player.position;! ! CGPoint offset = subPoint(location, projectile.position);! ! if (offset.x <= 0) return;! ! [self addChild:projectile];! }
  42. 42. Multiply vector - helper function CGPoint multiplyVector(CGPoint vector, CGFloat amount){! CGPoint newVec = CGPointMake(vector.x*amount, vector.y*amount);! return newVec;! }
  43. 43. Calculate projectile destination (offsetX, offsetY) (offsetX/unitValue, offsetY/unitValue) newVec = multiplyVector(offset, 1000) (player.x+newVec.x, player.y+newVec.y)
  44. 44. Add point with offset CGPoint addOffset(CGPoint a, CGPoint offset){! CGPoint newVec = CGPointMake(a.x+offset.x, a.y+offset.y);! return newVec;! }
  45. 45. Get touch event - cal projectile destination -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{! ! // ! ! CGPoint direction = normalisedPoint(offset);! ! ! ! CGPoint shootOffset = multiplyVector(direction, 1000);! ! ! CGPoint realDest = addOffset(projectile.position, shootOffset);! ! ! float velocity = 480.0/1.0;! ! float realMoveDuration = self.size.width / velocity;! ! SKAction * actionMove = [SKAction moveTo:realDest duration:realMoveDuration];! ! SKAction * actionMoveDone = [SKAction removeFromParent];! ! [projectile runAction:[SKAction sequence:@[actionMove, actionMoveDone]]];! }
  46. 46. Run
  47. 47. Rotate the projectile float velocity = 480.0/1.0;! float realMoveDuration = self.size.width / velocity;! SKAction * actionMove = [SKAction moveTo:realDest duration:realMoveDuration];! SKAction * actionMoveDone = [SKAction removeFromParent];! SKAction * sequence = [SKAction sequence:@[actionMove, actionMoveDone]];! ! SKAction * rotate = [SKAction rotateByAngle:4*M_PI duration:0.5];! SKAction * forever = [SKAction repeatActionForever:rotate];! SKAction * group = [SKAction group:@[forever,sequence]];! [projectile runAction:group];
  48. 48. Run
  49. 49. Collision Detection
  50. 50. Use the power of Physic Engine Set gravity! ! Set contact delegate self.physicsWorld.gravity = CGVectorMake(0,0); self.physicsWorld.contactDelegate = self;
  51. 51. @interface MyScene ()<SKPhysicsContactDelegate>! @property (nonatomic) SKSpriteNode * player;! @property (nonatomic) NSTimeInterval lastSpawnTimeInterval;! @property (nonatomic) NSTimeInterval lastUpdateTimeInterval;! @end SKPhysicsContactDelegate
  52. 52. physicsWorld @interface SKScene : SKEffectNode! ! @property (SK_NONATOMIC_IOSONLY, readonly) SKPhysicsWorld *physicsWorld;! ! @end
  53. 53. physicsBody monster body! ! projectile body monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:monster.size]; projectile.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:projectile.size.width/2];
  54. 54. physics attributes // movable! monster.physicsBody.dynamic = YES;! ! // like ID! monster.physicsBody.categoryBitMask = monsterCategory;! ! // which ID will be contact! monster.physicsBody.contactTestBitMask = projectileCategory;! ! // can be contact or not! monster.physicsBody.collisionBitMask = 0
  55. 55. Two category ids static const uint32_t projectileCategory = 0x1 << 0;! static const uint32_t monsterCategory = 0x1 << 1;
  56. 56. addMonster - (void)addMonster { ! ! // ignore …! // physic! monster.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:monster.size]; // 1! monster.physicsBody.dynamic = YES; // 2! monster.physicsBody.categoryBitMask = monsterCategory; // 3! monster.physicsBody.contactTestBitMask = projectileCategory; // 4! monster.physicsBody.collisionBitMask = 0; // 5! }
  57. 57. touchesBegan:withEvent: projectile.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:projectile.size.width/2];! projectile.physicsBody.dynamic = YES;! projectile.physicsBody.categoryBitMask = projectileCategory;! projectile.physicsBody.contactTestBitMask = monsterCategory;! projectile.physicsBody.collisionBitMask = 0;
  58. 58. SKPhysicsContactDelegate - impl - (void)didBeginContact:(SKPhysicsContact *)contact! {! SKPhysicsBody *firstBody, *secondBody;! ! if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)! {! firstBody = contact.bodyA;! secondBody = contact.bodyB;! }! else! {! firstBody = contact.bodyB;! secondBody = contact.bodyA;! }! }
  59. 59. SKPhysicsContactDelegate - impl(2) - (void)didBeginContact:(SKPhysicsContact *)contact! {! if ((firstBody.categoryBitMask & projectileCategory) != 0 &&! (secondBody.categoryBitMask & monsterCategory) != 0){! ! [self projectile:(SKSpriteNode *) firstBody.node didCollideWithMonster:(SKSpriteNode *) secondBody.node];! }! }
  60. 60. Dismiss collided objects - (void)projectile:(SKSpriteNode *)projectile didCollideWithMonster:(SKSpriteNode *)monster {! NSLog(@"Hit");! [projectile removeFromParent];! [monster removeFromParent];! }
  61. 61. Run
  62. 62. About Music
  63. 63. Background Music - ViewController.m #import <AVFoundation/AVFoundation.h>! ! @interface ViewController ()! @property (nonatomic) AVAudioPlayer * backgroundMusicPlayer;! @end! ! @implementation ViewController! -(void) viewDidAppear:(BOOL)animated{! ! NSError *error;! ! NSURL * backgroundMusicURL = [[NSBundle mainBundle] URLForResource:@"background- music-aac" withExtension:@"caf"];! ! self.backgroundMusicPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:backgroundMusicURL error:&error];! ! self.backgroundMusicPlayer.numberOfLoops = -1;! ! [self.backgroundMusicPlayer prepareToPlay];! ! [self.backgroundMusicPlayer play];! }! @end
  64. 64. sound effect - touchesBegan:withEvent: -(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{! ! [self runAction:[SKAction playSoundFileNamed:@"pew-pew-lei.caf" waitForCompletion:NO]];! ! }
  65. 65. Run
  66. 66. Change Scene
  67. 67. Create a new Scene
  68. 68. GameOverScene.h #import <SpriteKit/SpriteKit.h>! ! @interface GameOverScene : SKScene! -(id)initWithSize:(CGSize)size won:(BOOL)won;! @end
  69. 69. GameOverScene.m #import "GameOverScene.h"! #import "MyScene.h"! @implementation GameOverScene! ! -(id)initWithSize:(CGSize)size won:(BOOL)won {! if (self = [super initWithSize:size]) {! ! // 1! self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];! ! // 2! NSString * message;! if (won) {! message = @"You Won!";! } else {! message = @"You Lose :[";! }! ! ! // ignore … ! ! }! ! return self;! }! @end
  70. 70. GameOverScene.m - Label -(id)initWithSize:(CGSize)size won:(BOOL)won {! if (self = [super initWithSize:size]) {! ! ! // ignored! ! ! SKLabelNode *label = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];! label.text = message;! label.fontSize = 40;! label.fontColor = [SKColor blackColor];! label.position = CGPointMake(self.size.width/2, self.size.height/2);! [self addChild:label];! ! ! // ignored! ! }! ! return self;! }
  71. 71. GameOverScene.m - Another Scene -(id)initWithSize:(CGSize)size won:(BOOL)won {! if (self = [super initWithSize:size]) {! ! ! // ignored! ! ! [self runAction:! [SKAction sequence:@[! [SKAction waitForDuration:3.0],! [SKAction runBlock:^{! // 5! SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];! SKScene * myScene = [[MyScene alloc] initWithSize:self.size];! [self.view presentScene:myScene transition: reveal];! }]! ]]! ];! ! ! }! ! return self;! }
  72. 72. Show GameOverScene - MyScene.m #import "GameOverScene.h"! ! - (void)addMonster {! ! SKAction * loseAction = [SKAction runBlock:^{! ! ! SKTransition *reveal = [SKTransition flipHorizontalWithDuration: 0.5];! ! ! SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:NO];! ! ! [self.view presentScene:gameOverScene transition: reveal];! ! }];! ! [monster runAction:[SKAction sequence:@[actionMove, loseAction, actionMoveDone]]];! }
  73. 73. Show Win @interface MyScene ()<SKPhysicsContactDelegate>! @property (nonatomic) int monstersDestroyed;! @end - (void)projectile:(SKSpriteNode *)projectile didCollideWithMonster: (SKSpriteNode *)monster {! self.monstersDestroyed++;! if (self.monstersDestroyed > 5) {! SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];! SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:YES];! [self.view presentScene:gameOverScene transition: reveal];! }! }
  74. 74. Question ?

×