Sprite Kit, Ad Framework

Sprite Kit
•
Sprite Kit provides a graphics rendering and animation
infrastructure that can be used to animate arbitrary textured
images, or sprites.
•
Sprite Kit also provides other functionality that is useful for
games, including basic sound playback support and physics
simulation.
•
In addition, Xcode provides built-in support for Sprite Kit so
that you can create complex special effects and texture atlases
directly in Xcode.
Sprite Kit
•
Create new SpriteKit Game project “Hello SpriteKit"
Sprite Kit
•
By default, MyScene and Spaceship.png are added to the project
- delete them.ViewController.h is simply :
#import <UIKit/UIKit.h>
#import <SpriteKit/SpriteKit.h>
@interface ViewController : UIViewController
@end
SKView
•
Remove scene from viewDidLoad :
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView *skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
}
SKScene
•
Sprite Kit content is placed in a window, just like other visual
content.
•
•
Sprite Kit content is rendered by the SKView class.
•
In order to create Hello scene, create a new class named
HelloScene and make it a subclass of the SKScene class.
The content that an SKView object renders is called a scene,
which is an SKScene object.
SKScene
•
•
In ViewController.m import HelloScene header.
In viewDidLoad method, create an instance of HelloScene and
attach it to the view :
// Get screen size
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGFloat screenHeight = screenRect.size.height;
HelloScene* hello = [[HelloScene alloc]
initWithSize:CGSizeMake(screenWidth, screenHeight)];
[skView presentScene: hello];
•
Run.
Sprite Kit
SKScene
•
When designing a Sprite Kit–based game, you design different
scene classes for each major chunk of your game interface.
•
•
This first scene will display the traditional “Hello World” text.
•
First of all, in HelloScene.m we add a property contentCreated
to track whether the scene has created a content or not :
The code that draws the scene normally resides inside the
didMoveToView: method, which is called whenever the scene
is presented in a view.
@interface HelloScene ()
@property BOOL contentCreated;
@end
didMoveToView method
•
Next we implement the scene’s didMoveToView: method.
- (void)didMoveToView: (SKView *) view
{
if (!self.contentCreated)
{
[self createSceneContents];
self.contentCreated = YES;
}
}
SKLabelNode
•
createSceneContents method sets the background colour,
scale mode and attaches a new child - SKLabelNode:
- (void)createSceneContents
{
self.backgroundColor = [SKColor blueColor];
self.scaleMode = SKSceneScaleModeAspectFit;
[self addChild: [self newHelloNode]];
}
text, font, position
•
Method newHelloNode creates and returns SKLabelNode:
- (SKLabelNode *)newHelloNode
{
SKLabelNode *helloNode =
[SKLabelNode
labelNodeWithFontNamed:@"Chalkduster"];
helloNode.text = @"Hello, World!";
helloNode.fontSize = 42;
helloNode.position = CGPointMake(
CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
return helloNode;
}
•
Run. ( Hello Sprite Kit 1 )
Sprite Kit
SKAction
•
Animations in SpriteKit are accomplished through actions
( SKAction - move, zoom, scale, pause etc. )
•
Once the action ( or sequence of actions is defined ), it is called
on specified node through runAction: method.
•
In order to access node(s) outside of method where they are
defined ( e.g. newHelloNode ) we can give them names :
helloNode.name = @"helloNode";
•
The animation in this example starts when user touches the
label.
SKAction
- (void)touchesBegan:(NSSet *) touches
withEvent:(UIEvent *)event
{
SKNode *helloNode =
[self childNodeWithName:@"helloNode"];
if (helloNode != nil)
{
helloNode.name = nil;
SKAction *moveUp =
[SKAction moveByX: 0 y: 100.0 duration: 0.5];
SKAction *zoom =
[SKAction scaleTo: 2.0 duration: 0.25];
SKAction
SKAction *pause =
[SKAction waitForDuration: 0.5];
SKAction *fadeAway =
[SKAction fadeOutWithDuration: 0.25];
SKAction *remove =
[SKAction removeFromParent];
SKAction *moveSequence =
[SKAction sequence:@[moveUp, zoom, pause,
fadeAway, remove]];
[helloNode runAction: moveSequence];
}
}
Scene Transitions
•
Sprite Kit makes it easy to transition between scenes through
the use of SKTransition
•
•
Create new class SpaseshipScene, subclass of SKScene.
Implementation is similar to HelloScene - we simply set the
background to black.
#import "SpaceshipScene.h"
@interface SpaceshipScene ()
@property BOOL contentCreated;
@end
Scene Transitions
@implementation SpaceshipScene
- (void)didMoveToView:(SKView *)view
{
if (!self.contentCreated)
{
[self createSceneContents];
self.contentCreated = YES;
}
}
- (void)createSceneContents
{
self.backgroundColor = [SKColor blackColor];
self.scaleMode = SKSceneScaleModeAspectFit;
}
@end
Scene Transitions
•
In HelloScene implementation import “SpaceshipScene.h” and
replace the call to runAction: with a new call :
[helloNode runAction: moveSequence completion:^{
SKScene *spaceshipScene = [[SpaceshipScene alloc]
initWithSize:self.size];
SKTransition *doors =
[SKTransition doorsOpenVerticalWithDuration:0.5];
[self.view presentScene:spaceshipScene
transition:doors];
}];
•
Run.
Complex Content
•
Complex content is normally built using many SKSpriteNode
objects.
•
Our spaceship is going to be built using three nodes - hull and
two lights.
•
In SpaceshipScene.m, add code to the createSceneContents
method to create the spaceship :
SKSpriteNode *spaceship = [self newSpaceship];
spaceship.position = CGPointMake(
CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame) - 150);
[self addChild:spaceship];
Complex Content
- (SKSpriteNode *)newSpaceship
{
SKSpriteNode *hull = [[SKSpriteNode alloc]
initWithColor:[SKColor grayColor]
size:CGSizeMake(64,32)];
SKAction *hover = [SKAction sequence:@[
[SKAction waitForDuration:1.0],
[SKAction moveByX:100 y:50.0 duration:1.0],
[SKAction waitForDuration:1.0],
[SKAction moveByX:-100.0 y:-50 duration:1.0]]];
[hull runAction: [SKAction repeatActionForever:hover]
];
return hull;
}
Complex Content
•
Although we could add all three sprites directly to the scene,
that isn’t the Sprite Kit way.
•
The blinking lights are part of the spaceship! If the spaceship
moves, the lights should move with it.
•
The solution is to make the spaceship node their parent, in the
same way that the scene is going to be the parent of the
spaceship.
•
The coordinates of the lights are going to be specified relative
to the parent node’s position, which is at the center of the
sprite image.
Complex Content
•
So, once the hull is created, we’ll couple of lights and set their
parent to hull :
SKSpriteNode *light1 = [self newLight];
light1.position = CGPointMake(-28.0, 6.0);
[hull addChild:light1];
SKSpriteNode *light2 = [self newLight];
light2.position = CGPointMake(28.0, 6.0);
[hull addChild:light2];
•
We also need the newLight method :
Complex Content
- (SKSpriteNode *)newLight
{
SKSpriteNode *light = [[SKSpriteNode alloc]
initWithColor:[SKColor yellowColor]
size:CGSizeMake(8,8)];
SKAction *blink = [SKAction sequence:@[
[SKAction fadeOutWithDuration:0.25],
[SKAction fadeInWithDuration:0.25]]];
SKAction *blinkForever =
[SKAction repeatActionForever:blink];
[light runAction: blinkForever];
return light;
}
Complex
Content
•
Run. ( Hello Sprite Kit 2 )
Physics
•
Sprite Kit provides a complete physics simulation which you can
use to add automatic behaviours to nodes.
•
That is, instead of executing actions on the nodes, physics is
automatically simulated on the node, causing it to move.
•
When it interacts with other nodes that are part of the physics
system, collisions are automatically calculated and performed.
•
We add a physics body to the spaseship :
hull.physicsBody =
[SKPhysicsBody bodyWithRectangleOfSize:hull.size];
Physics
•
If you run it now, the spaceship plummets through the bottom
of the screen.
•
In order to prevent the spaceship from being affected by physics
interactions, set dynamic property to NO :
hull.physicsBody.dynamic = NO;
•
Run it again and observe that spaceship is not affected by
gravity anymore.
Physics
•
Let’s create meteor shower and see how rocks interact with
our spaceship.
•
In createSceneContents, after we added ship to the scene,
we add makeRocks action which continuously spawns rocks :
SKAction *makeRocks = [SKAction sequence: @[
[SKAction performSelector:@selector(addRock)
onTarget:self],
[SKAction waitForDuration:0.10 withRange:0.15]
]];
[self runAction:
[SKAction repeatActionForever:makeRocks]];
Physics
static inline CGFloat skRandf() {
return rand() / (CGFloat) RAND_MAX;
}
static inline CGFloat skRand(CGFloat low, CGFloat high) {
return skRandf() * (high - low) + low;
}
- (void)addRock
{
SKSpriteNode *rock = [[SKSpriteNode alloc]
initWithColor:[SKColor brownColor]
size:CGSizeMake(8,8)];
rock.position = CGPointMake(
skRand(0, self.size.width), self.size.height-50);
rock.name = @"rock";
rock.physicsBody = [SKPhysicsBody
bodyWithRectangleOfSize:rock.size];
rock.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:rock];
}
Physics
•
Run. ( Hello Sprite Kit 3 )
The Archer Game
Credit: iOS 7 App Essentials
The Archer Game
SKView
Welcome
Scene
Welcome
Label Node
Archery
Scene
Archer
Sprite Node
Arrow
Sprite Node
Physics Body
Ball
Sprite Node
Physics Body
Game Over
Label Node
The Archer Game
•
Create new sprite kit based project “Archer”. In project setting,
set orientation to portrait only.
•
Delete scene and default png from project and in
ViewController delete scene reference :
- (void)viewDidLoad
{
[super viewDidLoad];
// Configure the view.
SKView * skView = (SKView *)self.view;
skView.showsFPS = YES;
skView.showsNodeCount = YES;
}
Welcome Scene
•
•
Create new class WelcomeScene subclass of SKScene.
As before, we add boolean property sceneCreated and
implement didMoveToView method :
@interface WelcomeScene ()
@property BOOL sceneCreated;
@end
Welcome Scene
@implementation WelcomeScene
- (void) didMoveToView:(SKView *)view
{
if (!self.sceneCreated)
{
self.backgroundColor = [SKColor greenColor];
self.scaleMode = SKSceneScaleModeAspectFill;
[self addChild: [self createWelcomeNode]];
self.sceneCreated = YES;
}
}
Welcome Scene
- (SKLabelNode *) createWelcomeNode
{
SKLabelNode *welcomeNode =
[SKLabelNode labelNodeWithFontNamed:@"Bradley Hand"];
welcomeNode.name = @"welcomeNode";
welcomeNode.text = @"Tap Screen to Play";
welcomeNode.fontSize = 24;
welcomeNode.fontColor = [SKColor blackColor];
welcomeNode.position =
CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
return welcomeNode;
}
Welcome Scene
•
In ViewController, viewDidLoad method, we create the
welcome scene and attach it to the view:
WelcomeScene *welcome = [[WelcomeScene alloc]
initWithSize:CGSizeMake(skView.bounds.size.width,
skView.bounds.size.height)];
[skView presentScene:welcome];
•
Don’t forget to import “WelcomeScene.h”
Welcome Scene
•
Run.
Archery Scene
•
Similar, at this point, to welcome scene. Besides boolean
property sceneCreated, we also declare score and ballCount
properties:
@interface ArcheryScene ()
@property BOOL sceneCreated;
@property int score;
@property int ballCount;
@end
Archery Scene
@implementation ArcheryScene
- (void)didMoveToView:(SKView *)view
{
if (!self.sceneCreated)
{
self.score = 0;
self.ballCount = 40;
[self initArcheryScene];
self.sceneCreated = YES;
}
}
- (void) initArcheryScene
{
self.backgroundColor = [SKColor whiteColor];
self.scaleMode = SKSceneScaleModeAspectFill;
}
Transition
•
In order to make transition from welcome scene to archery scene
we need to implement method touchesBegan:withEvent
- (void)touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event
{
SKNode *welcomeNode =
[self childNodeWithName:@"welcomeNode"];
if (welcomeNode != nil)
{
SKAction *fadeAway =
[SKAction fadeOutWithDuration:1.0];
Transition
[welcomeNode runAction:fadeAway completion:^{
SKScene *archeryScene =
[[ArcheryScene alloc]
initWithSize:self.size];
SKTransition *doors =
[SKTransition doorwayWithDuration:1.0];
[self.view presentScene:archeryScene
transition:doors];
}
];
}
}
•
•
Don’t forget to import “ArcheryScene.h” in WelcomeScene.m.
Run. ( Archer 1 )
Texture Atlas
•
Before textures can be used on a sprite node, the texture images
first need to be added to the project.
•
Textures take the form of image files and may be added
individually to the Supporting Files folder of the project.
•
For larger numbers of texture files, it is more efficient (both for
the developer and the application) to create a texture atlas.
•
In the case of the archer sprite, this will require twelve image files
to animate the loading and subsequent shooting of an arrow.
•
As such, a texture atlas will be used to store these animation
frame images.
Texture Atlas
•
To add the atlas to the project, drag and drop the
archer.atlas folder into the Supporting Files folder in
the Xcode project navigator panel:
•
Verify in Targets, Archer, Build Settings that Texture
Atlas Generation is enabled :
•
When the archer sprite is first initialized it needs to be textured
with the archer001.png file from the texture atlas.
•
This takes place in the createArcherNode method:
Texture Atlas
- (SKSpriteNode *)createArcherNode
{
SKSpriteNode *archerNode = [[SKSpriteNode alloc]
initWithImageNamed:@"archer001.png"];
archerNode.name = @"archerNode";
return archerNode;
}
•
Method is called in initArcheryScene
SKSpriteNode *archerNode = [self createArcherNode];
archerNode.position = CGPointMake(
CGRectGetMinX(self.frame)+55,
CGRectGetMidY(self.frame));
[self addChild:archerNode];
Texture Atlas
•
Run.
Animation Texture Atlas
•
In order to animate archer whenever user touches the screen,
first of all we define the private property:
@property NSArray *archerAnimation;
•
Array is then populated in initArcheryScene method:
Animation Texture Atlas
NSMutableArray *archerFrames = [NSMutableArray array];
SKTextureAtlas *archerAtlas =
[SKTextureAtlas atlasNamed:@"archer"];
for (int i = 1; i <= archerAtlas.textureNames.count; i++)
{
NSString *texture =
[NSString stringWithFormat:@"archer%03d", i];
[archerFrames addObject:
[archerAtlas textureNamed:texture]];
}
self.archerAnimation = archerFrames;
Animation Texture Atlas
•
Now that an array of textures for the animation action has been
created and initialized, code now needs to be written to perform
the animation.
•
Since this animation is triggered by a touch on the screen, this
code needs to go in the touchesBegan:withEvent method of
the ArcheryScene class.
Animation Texture Atlas
- (void) touchesBegan:(NSSet *)touches
withEvent:(UIEvent *)event
{
SKNode *archerNode =
[self childNodeWithName:@"archerNode"];
if (archerNode != nil)
{
SKAction *animate = [SKAction
animateWithTextures:self.archerAnimation
timePerFrame: 0.05];
[archerNode runAction:animate];
}
}
•
Run and observe that sprite is animated.
Shoot!
•
In order for Archer to shoot arrows, first of all, we need an
“ArrowTexture.png” in Supporting Files
•
Next we have to create the arrow node, position it to the right
of the archer sprite node and assigns it the name arrowNode.
•
A physics body is then assigned to the node, using the size of the
node itself as the boundary of the body and enabling precision
collision detection.
Shoot!
- (SKSpriteNode *) createArrowNode
{
SKSpriteNode *arrow = [[SKSpriteNode alloc]
initWithImageNamed:@"ArrowTexture.png"];
arrow.position = CGPointMake(
CGRectGetMinX(self.frame)+100,
CGRectGetMidY(self.frame));
arrow.name = @"arrowNode";
arrow.physicsBody = [SKPhysicsBody
bodyWithRectangleOfSize:arrow.frame.size];
arrow.physicsBody.usesPreciseCollisionDetection = YES;
return arrow;
}
Shoot!
•
In order to propel the arrow across the scene, a physical
force needs to be applied to it.
•
The creation and propulsion of the arrow sprite needs to
be timed to occur at the end of the archer animation
sequence.
•
This can be achieved via some minor modifications (after
the action animate) to the touchesBegan method:
Shoot!
SKAction *shootArrow = [SKAction runBlock:^{
SKNode *arrowNode = [self createArrowNode];
[self addChild:arrowNode];
[arrowNode.physicsBody
applyImpulse:CGVectorMake(35.0, 0)];
}];
SKAction *sequence = [SKAction
sequence:@[animate, shootArrow]];
[archerNode runAction:sequence];
•
Run and shoot some arrows.
Targets
•
•
•
The goal of the game is to shoot something (balls).
Copy BallTexture.png into Supporting Files folder.
Next we need the createBallNode method in our
ArcheryScene.m :
Targets
- (void) createBallNode
{
SKSpriteNode *ball = [[SKSpriteNode alloc]
initWithImageNamed:@"BallTexture.png"];
ball.position = CGPointMake(
randomBetween(200, self.size.width),
self.size.height-50);
ball.name = @"ballNode";
ball.physicsBody = [SKPhysicsBody
bodyWithCircleOfRadius:(ball.size.width/2)-7];
ball.physicsBody.usesPreciseCollisionDetection = YES;
[self addChild:ball];
}
Targets
•
createBallNode method uses random number generation
used in calculating the X coordinate of the ball sprite node:
static inline CGFloat randomFloat()
{
return rand() / (CGFloat) RAND_MAX;
}
static inline CGFloat randomBetween(CGFloat low,
CGFloat high)
{
return randomFloat() * (high - low) + low;
}
Targets
•
Next, modify the initArcheryScene method to create an
action to release a total of 40 balls at one second intervals:
SKAction *releaseBalls = [SKAction
sequence:@[[SKAction
performSelector:@selector(createBallNode)
onTarget:self],
[SKAction waitForDuration:1]
]];
[self runAction: [SKAction
repeatAction:releaseBalls
count:self.ballCount]];
Targets
•
•
Run.
Bit too much gravity. Change it in
didMoveToView method to :
self.physicsWorld.gravity =
CGVectorMake(0, -1.0);
•
Run again ( Archer 2 )
Collision Detection
•
When detecting collisions within a Sprite Kit scene, a
delegate method is called each time a collision is detected.
•
This method will only be called, however, if the colliding
nodes are configured appropriately using category bit
masks.
•
We begin by editing the ArcheryScene.m file and adding
two category bit masks at the top of the @implementation
section:
static const uint32_t arrowCategory = 0x1 << 0;
static const uint32_t ballCategory = 0x1 << 1;
Collision Detection
•
Having declared the masks, these need to be assigned to the
respective node objects when they are created within the
game ( createArrowNode and createBallNode ).
•
This is achieved by assigning the mask to the
categoryBitMask property of the physics body assigned to
the node.
arrow.physicsBody.categoryBitMask = arrowCategory;
ball.physicsBody.categoryBitMask = ballCategory;
Collision vs Contact
•
When a contact occurs, two nodes are able to touch or
even occupy the same space in a scene.
•
A collision involves contact between two nodes that cannot
occupy the same space in the scene.
•
The type of contact for which notification is required is
specified by assigning contact and collision bit masks to the
physics body of one of the node categories involved in the
contact.
arrow.physicsBody.collisionBitMask =
arrowCategory | ballCategory;
arrow.physicsBody.contactTestBitMask =
arrowCategory | ballCategory;
Contact Delegate
•
When the Sprite Kit physics system detects a collision or
contact, it needs a way to notify the application code that
such an event has occurred.
•
It does this by calling methods on the class instance that has
been registered as the contact delegate ( ArcheryScene.h ).
#import <SpriteKit/SpriteKit.h>
@interface ArcheryScene : SKScene
<SKPhysicsContactDelegate>
@end
Contact Delegate
•
In didMoveToView method we have to set the delegate :
self.physicsWorld.contactDelegate = self;
•
And implement didBeginContact in ArcheryScene.m :
- (void) didBeginContact:(SKPhysicsContact *)contact
{
SKSpriteNode *firstNode, *secondNode;
firstNode = (SKSpriteNode *)contact.bodyA.node;
secondNode = (SKSpriteNode *) contact.bodyB.node;
Contact Delegate
if ((contact.bodyA.categoryBitMask == arrowCategory)
&& (contact.bodyB.categoryBitMask == ballCategory))
{
CGPoint contactPoint = contact.contactPoint;
float contact_y = contactPoint.y;
float target_y = secondNode.position.y;
float margin = secondNode.frame.size.height/2 - 25;
if ((contact_y > (target_y - margin)) &&
(contact_y < (target_y + margin)))
{
NSLog(@"Hit");
self.score++;
}
}
}
Fixed Joint
•
•
Run it and observe the output in the console.
•
Start by dragging ArrowHitTexture.png into the Supporting
Files folder.
•
In order to “embed” the arrow into a ball, we need to create
fixed joint between two nodes ( arrow and ball ).
•
Here we’ll need the x coordinate as well:
To make thing more interesting, how would one embed
arrow into a ball when we hit the ball?
float contact_x = contactPoint.x;
Fixed Joint
if ((contact_y > (target_y - margin)) &&
(contact_y < (target_y + margin)))
{
SKPhysicsJointFixed *joint = [SKPhysicsJointFixed
jointWithBodyA:contact.bodyA
bodyB:contact.bodyB
anchor:CGPointMake(contact_x, contact_y)];
[self.physicsWorld addJoint:joint];
SKTexture *texture = [SKTexture
textureWithImageNamed:@"ArrowHitTexture"];
firstNode.texture = texture;
self.score++;
}
Game Over
•
All that now remains is to display the score to the user
when all of the balls have been released.
•
This will require a new label node and a small change to an
action sequence followed by a transition to the welcome
scene so the user can start a new game.
•
Begin by adding the method to create the label node in the
ArcheryScene.m file:
Game Over
- (SKLabelNode *) createScoreNode
{
SKLabelNode *scoreNode = [SKLabelNode
labelNodeWithFontNamed:@"Bradley Hand"];
scoreNode.name = @"scoreNode";
NSString *newScore = [NSString
stringWithFormat:@"Score: %i", self.score];
scoreNode.text = newScore;
scoreNode.fontSize = 60;
scoreNode.fontColor = [SKColor redColor];
scoreNode.position =
CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
return scoreNode;
}
Game Over
•
Next, implement the gameOver method which will display
the score label node and then transition back to the
welcome scene ( remember to import “WelcomeScene.h” ):
- (void) gameOver
{
SKLabelNode *scoreNode = [self createScoreNode];
[self addChild:scoreNode];
SKAction *fadeOut = [SKAction sequence:
@[[SKAction waitForDuration:3.0],
[SKAction fadeOutWithDuration:3.0]
]];
Game Over
SKAction *welcomeReturn = [SKAction runBlock:^{
SKTransition *transition = [SKTransition
revealWithDirection:SKTransitionDirectionDown
duration:1.0];
WelcomeScene *welcomeScene =
[[WelcomeScene alloc] initWithSize:self.size];
[self.scene.view presentScene: welcomeScene
transition:transition];
}];
SKAction *sequence = [SKAction
sequence:@[fadeOut, welcomeReturn]];
[self runAction:sequence];
}
Game Over
•
Finally, add a completion handler that calls the gameOver
method to the ball release action in the initArcheryScene
method:
[self runAction:[SKAction
repeatAction:releaseBalls
count:self.ballCount]
completion:^{
[self gameOver];
}
];
Game Over
•
Run again ( Archer 3 )
Particle Emitter
•
The Sprite Kit particle emitter is essentially a mechanism
designed to add special effects to games.
•
It comprises the SKEmitterNode class and the Particle
Emitter Editor bundled with Xcode.
•
Create new file, iOS, Resource, SpriteKit Particle File.
Particle Emitter
Particle Emitter
•
Select spark effect and save files as BurstParticle.sks
Particle Emitter
•
In method didBeginContact, we don’t need anymore
contact_x, neither the code that embeds arrow into the ball,
but we’ll need target_x:
float target_x = secondNode.position.x;
float target_y = secondNode.position.y;
float margin = secondNode.frame.size.height/2 - 25;
if ((contact_y > (target_y - margin)) &&
(contact_y < (target_y + margin)))
{
self.score++;
}
Particle Emitter
•
Create SKEmitterNode from the archive in the BurstParticle
file, remove the ball node from the scene and replace it at the
same position with the emitter:
NSString *burstPath = [[NSBundle mainBundle]
pathForResource:@“BurstParticle"
ofType:@"sks"];
SKEmitterNode *burstNode = [NSKeyedUnarchiver
unarchiveObjectWithFile:burstPath];
burstNode.position = CGPointMake(target_x, target_y);
[secondNode removeFromParent];
[self addChild:burstNode];
Particle Emitter
•
If you run it at this point, well, it does not look very good.
Select BurstParticle.sks and SKNode Inspector
•
•
Change background to white.
•
Set position range to 86
( size of the ball )
Set particles birthrate to 15 and
maximum to 8.
Particle Emitter
•
Set the color ramp to white and
Blend Mode to Alpha
•
•
Drag BallFragment.png into Support Files Folder.
Change Particle Texture
Sound Effects
•
If you want to add a sound effect, copy ballburst.mp3 into
Support Files Folder.
•
In didBeginContact method, before we replace ball with the
particle emitter, play that sound:
[self runAction:
[SKAction playSoundFileNamed:@"ballburst.mp3"
waitForCompletion:NO]
];
•
Run. ( Archer 4 )
iAd Framework
•
How to make some money out of your app?
1. Get paid by the third party to develop app.
2. Sell app in the app store.
3. Make it free and create in-app purchases.
4. Make it free and make some money from the advertisement.
•
In order to display advertisements, we need to add iAd
Framework
iAd Framework
•
•
Create new project, “iAdDemo”, single view application.
•
Add few labels and Ad BannerView from the Object Library.
Select project, targets, iAdDemo, General and add
iAd.framework:
iAd
Framework
•
Your Main.storyboard should
resemble:
iAd Framework
•
Using Assistant Editor, create an outlet for the banner:
#import <UIKit/UIKit.h>
#import <iAd/iAd.h>
@interface ViewController :
UIViewController <ADBannerViewDelegate>
@property (weak, nonatomic)
IBOutlet ADBannerView *adBanner;
@end
iAd Framework
•
The iAd framework provides several delegate methods for
managing ads.
•
These allow us to know:
1. when an ad is about to be shown or it has already been
shown
2. when a failure occurs and no more ads can be served
3. when the user taps on an ad to view it on full-screen
mode
iAd Framework
•
In viewDidLoad method we set banner’s delegate to itself and
initially hide the banner:
self.adBanner.delegate = self;
self.adBanner.alpha = 0.0;
•
The delegate methods are:
-(void)bannerViewWillLoadAd:(ADBannerView *)banner
{
NSLog(@"Ad Banner will load ad.");
}
Called when a new advertisement is about to be loaded.
Note that when this method is called the ad is not yet ready
to be displayed.
iAd Framework
-(void)bannerViewDidLoadAd:(ADBannerView *)banner{
NSLog(@"Ad Banner did load ad.");
// Show the ad banner.
[UIView animateWithDuration:0.5 animations:^{
self.adBanner.alpha = 1.0;
}];
}
Called after a new ad has been loaded and is ready to be
displayed.
Here we make the banner visible.
iAd Framework
-(BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner
willLeaveApplication:(BOOL)willLeave
{
NSLog(@"Ad Banner action is about to begin.");
return YES;
}
Called when the user taps on the banner, and the ad is going to be
displayed in a full size modal view.
iAd Framework
-(void)bannerViewActionDidFinish:(ADBannerView *)banner
{
NSLog(@"Ad Banner action did finish");
}
Called when the full-screen view with the add gets dismissed.
iAd Framework
-(void)bannerView:(ADBannerView *)banner
didFailToReceiveAdWithError:(NSError *)error
{
NSLog(@"Unable to show ads. Error: %@",
[error localizedDescription]);
// Hide the ad banner.
[UIView animateWithDuration:0.5 animations:^{
self.adBanner.alpha = 0.0;
}];
}
Called when no ads are available to be delivered to the app. Here
we hide the banner.
•
Run. ( iAdDemo )
iAd Framework
Die Ende