Shootting Game

838 views
645 views

Published on

Sprite Kit Case - Shooting Game

Published in: Entertainment & Humor
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
838
On SlideShare
0
From Embeds
0
Number of Embeds
14
Actions
Shares
0
Downloads
15
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

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 ?

×