SpriteKit 框架开发简单游戏

SpriteKit 框架入门 demo

第一步:创建SKNode类目
SKNode+Extra.h文件

-(void)receiveAttacker:(SKNode *)arracker contact:(SKPhysicsContact *)contact;
-(void)friendlyBumpFrom:(SKNode *)node;
   SKNode+Extra.m文件
-(void)receiveAttacker:(SKNode *)arracker contact:(SKPhysicsContact *)contact{//敌人被导弹击中调用
}
-(void)friendlyBumpFrom:(SKNode *)node{
}

第二步:使用工程创建c头文件HpysicsCategories.h文件

typedef NS_OPTIONS(uint32_t, HpysicsCategories) {
     PlayerCategory = 1<<1,//拉掩码  使用OR运算符将多个值拼在一起
     EnemyCategory  = 1<<2,
     PlayerMissileCategory = 1<<3
};

第三步:创建c头文件GrameGeometry.h文件

//返回一个新的CGvect 其中每个v元素都已经乘以m
static inline CGVector BIDVectorMultiplay(CGVector v, CGFloat m){
    return CGVectorMake(v.dx*m, v.dy*m);
}
//返回一个表示p1到p2距离的CGVect
static inline CGVector BIDVectorBetweenPoints(CGPoint p1, CGPoint p2){
    return CGVectorMake(p2.x-p1.x, p2.y-p1.y);
}
//通过勾股定理计算向量的长度返回 CGFloat
static inline CGFloat BIDVectorLength(CGVector v){
    return  sqrtf(powf(v.dx, 2.0)+powf(v.dy, 2));
}
//根据两个 坐标 ,通过勾股定理两者之间的距离
static inline CGFloat BIDPointDistance(CGPoint p1, CGPoint p2){
    return  sqrtf(powf(p2.x-p1.x, 2)-powf(p2.y-p1.y,2));
}

第4步:创建炸弹BulletNode.m

@interface BulletNode ()

@property (assign, nonatomic) CGVector thrust;//发射导弹向量

@end

@implementation BulletNode

-(instancetype)init{
    if (self = [super init]) {
        SKLabelNode  *dot = [SKLabelNode labelNodeWithFontNamed:@"Courier"];
        dot.fontColor = [SKColor blackColor];
        dot.fontSize = 40;
        dot.text = @".";
        [self addChild:dot];

        SKPhysicsBody *body = [SKPhysicsBody bodyWithCircleOfRadius:1];//创建物理特性
        body.dynamic = YES;
        body.categoryBitMask  = PlayerMissileCategory;
        body.contactTestBitMask =  EnemyCategory;
        body.collisionBitMask = EnemyCategory;
        body.mass = 0.01;

        self.physicsBody = body;
        self.name = [NSString stringWithFormat:@"bullet %p",self];
    }
    return  self;
}

+(instancetype)bulletFrom:(CGPoint)start toward:(CGPoint)destination{//物理引
    BulletNode *bullet = [[self alloc]init];
    bullet.position = start;

    CGVector movement = BIDVectorBetweenPoints(start, destination);//向量
    CGFloat magnitude =  BIDVectorLength(movement);//长度
    if (magnitude==0.0f) {
        return  nil;
    }
    CGVector scaledMovement = BIDVectorMultiplay(movement, 1/magnitude);
    CGFloat thrustMagnitude = 100.0;
    bullet.thrust = BIDVectorMultiplay(scaledMovement, thrustMagnitude);
    [bullet runAction:[SKAction playSoundFileNamed:@"shoot.wav" waitForCompletion:NO]];
    return bullet;
}
-(void)applyRecurringForce{//推动物理形体发射 每一帧调用
    [self.physicsBody applyForce:self.thrust];
}
BulletNode.h
+(instancetype)bulletFrom:(CGPoint)start toward:(CGPoint)destination;
-(void)applyRecurringForce;

第5步:创建敌人PlayRivalNode.m

//玩家移动
-(instancetype)init{
    if (self = [super init]) {
        self.name = [NSString stringWithFormat:@"Rival %p",self];
        [self initNodeGraph];
        [self initPhysicsBody];
    }
    return  self;
}
-(void)initNodeGraph{
    SKLabelNode *lb = [SKLabelNode labelNodeWithFontNamed:@"Courier-Bold"];
    lb.fontColor = [SKColor darkGrayColor];
    lb.fontSize = 20;
    lb.text = @"x x";
    lb.position = CGPointMake(0, 15);
   // lb.name = @"lable";
    [self addChild:lb];

    SKLabelNode *lb1 = [SKLabelNode labelNodeWithFontNamed:@"Courier-Bold"];
    lb1.fontColor = [SKColor darkGrayColor];
    lb1.fontSize = 20;
    lb1.text = @"x";
  //  lb1.position = CGPointMake(0, 15);
    // lb.name = @"lable";
    [self addChild:lb1];

    SKLabelNode *lb2 = [SKLabelNode labelNodeWithFontNamed:@"Courier-Bold"];
    lb2.fontColor = [SKColor darkGrayColor];
    lb2.fontSize = 20;
    lb2.text = @"x x";
    lb2.position = CGPointMake(0, -15);
    // lb.name = @"lable";
    [self addChild:lb2];


}
-(void)initPhysicsBody{
    SKPhysicsBody *body = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(40, 40)];
    body.affectedByGravity = YES;
    body.categoryBitMask = EnemyCategory;
    body.contactTestBitMask = EnemyCategory |  PlayerCategory;
    body.mass = 0.2;
    body.angularDamping = 0.0f;
    body.linearDamping = 0.0f;
    self.physicsBody = body;
}
-(void)receiveAttacker:(SKNode *)arracker contact:(SKPhysicsContact *)contact{//敌人被导弹击中调用
    self.physicsBody.affectedByGravity = YES;//开启重力影响

    [self runAction:[SKAction playSoundFileNamed:@"enemyHit.wav"
                               waitForCompletion:NO]];

    CGVector force = BIDVectorMultiplay(arracker.physicsBody.velocity,contact.collisionImpulse);
    CGPoint myContact = [self.scene convertPoint:contact.contactPoint toNode:self];
    [self.physicsBody applyForce:force atPoint:myContact];

    NSString *parth = [[NSBundle mainBundle] pathForResource:@"MissileExplosion" ofType:@"sks"];
    SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:parth];
    explosion.numParticlesToEmit = 20;
    explosion.position = contact.contactPoint;
    [self.scene addChild:explosion];   
}
-(void)friendlyBumpFrom:(SKNode *)node{
    self.physicsBody.affectedByGravity = YES;//是敌人受重力影响
}
PlayRivalNode.h
@interface PlayRivalNode : SKNode
@end

第6:创建玩家PlaySKNode.m

//玩家移动
-(instancetype)init{
    if (self = [super init]) {
        self.name = [NSString stringWithFormat:@"Player %p",self];
        [self initNodeGraph];
        [self initPhysicsBody];
    }
    return  self;
}
-(void)initNodeGraph{
    SKLabelNode *lb = [SKLabelNode labelNodeWithFontNamed:@"Courier"];
    lb.fontColor = [SKColor darkGrayColor];
    lb.fontSize = 40;
    lb.text = @"v";
    lb.zRotation = M_PI;
    lb.name = @"lable";
    [self addChild:lb];
}
-(void)initPhysicsBody{
    SKPhysicsBody *body = [SKPhysicsBody bodyWithRectangleOfSize:CGSizeMake(40, 40)];
    body.affectedByGravity = NO;//是否开启重力
    body.categoryBitMask = PlayerCategory;
    body.contactTestBitMask = EnemyCategory;
    body.collisionBitMask = 0;
    self.physicsBody = body;
}


//返回将来移动所用的时间
-(CGFloat)moveToWard:(CGPoint)loaction{
    [self removeActionForKey:@"movement"];//移除上一次移动
    [self removeActionForKey:@"wobbling"];
    CGFloat distance = BIDPointDistance(self.position,loaction);//
    CGFloat pixels = [UIScreen mainScreen].bounds.size.width;//移动像素
    CGFloat duration = 2.0 * distance / pixels;
    [self runAction:[SKAction moveTo:loaction duration:duration] withKey:@"movement"];

    CGFloat wobbleTime = 0.3;
    CGFloat halfWobbleTime = wobbleTime * 0.5;
    SKAction *wobbling = [SKAction sequence:@[[SKAction scaleTo:0.2 duration:halfWobbleTime],
                                              [SKAction scaleTo:1.0 duration:halfWobbleTime]]];
    NSUInteger wobbleCount = duration/wobbleTime;
    [self runAction:[SKAction repeatAction:wobbling count:wobbleCount] withKey:@"wobbling"];//轻微摆动
    return  duration;
}

-(void)receiveAttacker:(SKNode *)arracker contact:(SKPhysicsContact *)contact{// 碰撞后的动作 敌人被导弹击中调用
    NSString *parth = [[NSBundle mainBundle] pathForResource:@"EnemyExplosion" ofType:@"sks"];

    [self runAction:[SKAction playSoundFileNamed:@"playerHit.wav"
                               waitForCompletion:NO]];

    SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:parth];
    explosion.numParticlesToEmit = 50;
    explosion.position = contact.contactPoint;
    [self.scene addChild:explosion];   
}
-(void)friendlyBumpFrom:(SKNode *)node{
    // self.physicsBody.affectedByGravity = YES;//是敌人受重力影响
}
PlaySKNode.h
@interface PlaySKNode : SKNode
-(CGFloat)moveToWard:(CGPoint)loaction;
@end

第7:创建开始场景StartScene.m

-(instancetype)initWithSize:(CGSize)size{
    if (self = [super initWithSize:size]) {
        self.backgroundColor = [SKColor greenColor];
        SKLabelNode *lb = [SKLabelNode labelNodeWithFontNamed:@"Courier"];
        lb.text = @"SpriteKit";
        lb.fontColor = [UIColor blackColor];
        lb.fontSize = 48;
        lb.position = CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.7);
        [self addChild:lb];

        SKLabelNode *lb1 = [SKLabelNode labelNodeWithFontNamed:@"Courier"];
        lb1.text = @"SpriteKit demo";
        lb1.fontColor = [UIColor blackColor];
        lb1.fontSize = 20;
        lb1.position = CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.3);
        [self addChild:lb1];
    }
    return self;
}

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    SKTransition *transition = [SKTransition doorwayWithDuration:1.0];
    SKScene *game = [[GameScenes alloc]initWithSize:self.frame.size];
    [self.view presentScene:game transition:transition];
    [self runAction:[SKAction playSoundFileNamed:@"gameStart.wav" waitForCompletion:NO]];
}
StartScene.h
@interface StartScene : SKScene
@end

第8:创建结束场景GameOverScene.m

-(instancetype)initWithSize:(CGSize)size{
    if (self = [super initWithSize:size]) {

        self.backgroundColor = [SKColor purpleColor];
        SKLabelNode *text = [SKLabelNode labelNodeWithFontNamed:@"Courier"];
        text.text = @"Game Over";
        text.fontColor = [SKColor whiteColor];
        text.fontSize = 50;
        text.position = CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.5);
        [self addChild:text];
    }
    return  self;
}
-(void)didMoveToView:(SKView *)view{
    [self performSelector:@selector(goToStart) withObject:nil afterDelay:3.0];
}
-(void)goToStart{
    SKTransition *transition = [SKTransition flipVerticalWithDuration:1.0];
    SKScene *game = [[StartScene alloc]initWithSize:self.frame.size];
    [self.view presentScene:game transition:transition];
}

第9 ;使用工程创建两种不同粒子
第一种:MissileExplosion.sks 如图
这里写图片描述
这里写图片描述
第二种:EnemyExplosion.sks 如图
这里写图片描述
这里写图片描述
第10:创建游戏场景GameScenes.m

#import "PlaySKNode.h"
#include "GrameGeometry.h"

#import "PlayRivalNode.h"  //添加对手
#define ARC4RANDOM_MAX  0x100000000

#import "BulletNode.h"//添加导弹

#import "SKNode+Extra.h"

#import "GameOverScene.h" //结束场景

@interface GameScenes ()<SKPhysicsContactDelegate>//碰撞检查

@property (strong, nonatomic) PlaySKNode *playNode;
@property (strong, nonatomic) SKNode *enemies;
@property (strong, nonatomic) SKNode *playerbullets;
//@property (strong, nonatomic) SKPhysicsWorld *physicsWorld;
//@property (assign, nonatomic) BOOL finished;
@end

@implementation GameScenes

+(instancetype)sceneWithSize:(CGSize)size levenNumber:(NSUInteger)levelNumber{
    return [[self alloc] initWithSize:size levenNumber:levelNumber];
}


-(instancetype)initWithSize:(CGSize)size{

    return  [self initWithSize:size levenNumber:1];
}

-(instancetype)initWithSize:(CGSize)size levenNumber:(NSUInteger)levelNumber{
    if (self = [super initWithSize:size]) {
        _levelNumber = levelNumber;
        _playerLives = 5;

        self.backgroundColor = [SKColor whiteColor];

        SKLabelNode *skLbLives = [SKLabelNode labelNodeWithFontNamed:@"Courier"];
        skLbLives.fontSize = 16.f;
        skLbLives.fontColor = [SKColor blackColor];
        skLbLives.name = @"LivesLbale";
        skLbLives.text = [NSString stringWithFormat:@"Lives: %lu",(unsigned long)_playerLives];
        skLbLives.verticalAlignmentMode = SKLabelVerticalAlignmentModeTop;
        skLbLives.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeRight;
        skLbLives.position = CGPointMake(self.frame.size.width, self.frame.size.height);
        [self addChild:skLbLives];

        SKLabelNode *skLbLives1 = [SKLabelNode labelNodeWithFontNamed:@"Courier"];
        skLbLives1.fontSize = 16.f;
        skLbLives1.fontColor = [SKColor blackColor];
        skLbLives1.name = @"LivesLbale";
        skLbLives1.text = [NSString stringWithFormat:@"Lives: %lu",(unsigned long)_levelNumber];
        skLbLives1.verticalAlignmentMode = SKLabelVerticalAlignmentModeTop;
        skLbLives1.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeLeft;
        skLbLives1.position = CGPointMake(0, self.frame.size.height);
        [self addChild:skLbLives1];

        self.playNode = [PlaySKNode node];//向场景插入玩家
        self.playNode.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetHeight(self.frame)*0.1);
        [self addChild:self.playNode];


        self.enemies = [SKNode node];
        [self addChild:self.enemies];

        [self spawnEnemies];

        self.playerbullets = [SKNode node];
        [self addChild:self.playerbullets];

       // self.physicsWorld = [[SKPhysicsWorld alloc]init];
        self.physicsWorld.gravity = CGVectorMake(0, -1);//掉落方向
        self.physicsWorld.contactDelegate = self;
    }
    return  self;
}
//添加敌人
-(void)spawnEnemies{
    NSUInteger count = log(self.levelNumber) + self.levelNumber;
    for (NSUInteger i=0; i< count; i++) {
        PlayRivalNode *enemy = [PlayRivalNode node];
        CGSize  size = self.frame.size;
        CGFloat x = (size.width *0.8*arc4random()/ARC4RANDOM_MAX)+(size.width*0.1);
        CGFloat y = (size.height *0.5 *arc4random()/ARC4RANDOM_MAX)+(size.height*0.5);
        enemy.position = CGPointMake(x, y);
        [self.enemies addChild:enemy];
    }
}
//玩家生命
-(void)setPlayerLives:(NSUInteger)playerLives{
    _playerLives = playerLives;
    SKLabelNode *lives = (id)[self childNodeWithName:@"LivesLbale"];
    lives.text = [NSString stringWithFormat:@"Lives: %lu",(unsigned long)_playerLives];
}
-(void)didMoveToView:(SKView *)view {
    /* Setup your scene here */
//    SKLabelNode *myLabel = [SKLabelNode labelNodeWithFontNamed:@"Chalkduster"];
//    
//    myLabel.text = @"Hello, World!";
//    myLabel.fontSize = 45;
//    myLabel.position = CGPointMake(CGRectGetMidX(self.frame),
//                                   CGRectGetMidY(self.frame));
//    
//    [self addChild:myLabel];


}
//物理碰撞世界
-(void)didBeginContact:(SKPhysicsContact *)contact{
    if (contact.bodyA.categoryBitMask == contact.bodyB.categoryBitMask) {
        //两个物体是同一个物理类型
        SKNode *nodeA = contact.bodyA.node;
        SKNode *nodeB = contact.bodyB.node;

        [nodeA friendlyBumpFrom:nodeB];
        [nodeB friendlyBumpFrom:nodeA];

    }else{
        SKNode *attacker = nil;
        SKNode *attackee = nil;
        if (contact.bodyA.categoryBitMask > contact.bodyB.categoryBitMask) {
            //a 攻击 b
            attacker = contact.bodyA.node;
            attackee = contact.bodyB.node;
        }else{
            // b 攻击 a
            attacker = contact.bodyB.node;
            attackee = contact.bodyA.node;
        }
        if ([attackee isKindOfClass:[PlaySKNode class]]) {
            self.playerLives --;
        }
        //处理攻击和被攻击
        if (attacker) {
            [attackee receiveAttacker:attacker contact:contact];
            [self.playerbullets removeChildrenInArray:@[attacker]];
            [self.enemies removeChildrenInArray:@[attackee]];
        }
    }
}

-(void)triggerGrameOver{
    self.finished = YES;
    NSString *parth = [[NSBundle mainBundle] pathForResource:@"EnemyExplosion" ofType:@"sks"];
    SKEmitterNode *explosion = [NSKeyedUnarchiver unarchiveObjectWithFile:parth];
    explosion.numParticlesToEmit = 200;
    explosion.position = self.playNode.position;
    [self addChild:explosion];

    [self.playNode removeFromParent];

    SKTransition *transition = [SKTransition doorsOpenVerticalWithDuration:1.0];
    SKScene *gameOver = [[GameOverScene alloc]initWithSize:self.frame.size];
    [self.view presentScene:gameOver transition:transition];
    [self runAction:[SKAction playSoundFileNamed:@"gameOver.wav" waitForCompletion:NO]];
}
-(BOOL)checkForGameOver{
    if (self.playerLives == 0) {
        [self triggerGrameOver];
        return YES;
    }
    return NO;
}
//触摸处理
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    /* Called when a touch begins */

    for (UITouch *touch in touches) {
        CGPoint location = [touch locationInNode:self];
        if (location.y<CGRectGetHeight(self.frame)*0.2) {
            CGPoint target = CGPointMake(location.x, self.playNode.position.y);
            CGFloat duration = [self.playNode moveToWard:target];
        }else{
            BulletNode *bullet = [BulletNode bulletFrom:self.playNode.position toward:location];
            if (bullet) {
                [self.playerbullets addChild:bullet];
            }
        }
    }
}
//下一个关卡
-(void)gotoNextLevel{
    self.finished = YES;
    SKLabelNode *lb = [SKLabelNode labelNodeWithFontNamed:@"Courier"];
    lb.text = @"Level Complete!";
    lb.fontColor = [SKColor blueColor];
    lb.fontSize = 32;
    lb.position = CGPointMake(self.frame.size.width*0.5, self.frame.size.height*0.5);
    [self addChild:lb];

    GameScenes *nextLevel = [[GameScenes alloc]initWithSize:self.frame.size levenNumber:self.levelNumber+1];
    nextLevel.playerLives = self.playerLives;
    [self.view presentScene:nextLevel transition:[SKTransition flipHorizontalWithDuration:1.0]];
}

-(void)updateBullets{
    NSMutableArray *bulletToRemove = [NSMutableArray array];
    for (BulletNode *bullet in self.playerbullets.children) {
        if (!CGRectContainsPoint(self.frame, bullet.position)) {//清除移动到场外的导弹

           [bulletToRemove addObject:bullet];//标记以清除的导弹
           continue;
        }
        [bullet applyRecurringForce];//推动剩下的导弹
    }
    [self.playerbullets removeChildrenInArray:bulletToRemove];

}
-(void)updateEnemies{
    NSMutableArray *enemisToRemove = [NSMutableArray array];
    for (SKNode *node in self.enemies.children) {
        if (!CGRectContainsPoint(self.frame, node.position)) {
            [enemisToRemove addObject:node];//移除敌人
            continue;
        }
    }
    if (enemisToRemove.count>0) {
        [self.enemies removeChildrenInArray:enemisToRemove];
    }
}
-(void)checkForNextLevel{
    if (self.enemies.children.count==0) {
        [self gotoNextLevel];
    }
}

-(void)update:(CFTimeInterval)currentTime {//帧更新
    if (self.finished) {
        return;
    }
    [self updateBullets];//导弹主动力
    [self updateEnemies];//刷新敌人
    if (![self checkForGameOver]) {
        [self checkForNextLevel];//管卡是否结束
    }

    /* Called before each frame is rendered */
}

GameScenes.h

@interface GameScenes : SKScene

@property (assign, nonatomic) NSUInteger levelNumber;
@property (assign, nonatomic) NSUInteger playerLives;
@property (assign, nonatomic) BOOL finished;

+(instancetype)sceneWithSize:(CGSize)size levenNumber:(NSUInteger)levelNumber;
-(instancetype)initWithSize:(CGSize)size levenNumber:(NSUInteger)levelNumber;
@end

第11:控制器加载游戏创建GameViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Configure the view.
    SKView * skView = (SKView *)self.view;
    skView.showsFPS = YES;
    skView.showsNodeCount = YES;
    /* Sprite Kit applies additional optimizations to improve rendering performance */
    skView.ignoresSiblingOrder = YES;

    StartScene *scene1 = [[StartScene alloc]initWithSize:CGSizeMake(self.view.frame.size.width, self.view.frame.size.height)];
    scene1.scaleMode = SKSceneScaleModeAspectFill;

    // Present the scene.
    [skView presentScene:scene1];
    // Create and configure the scene.
//    GameScenes *scene = [GameScenes nodeWithFileNamed:@"GameScene"];
//    scene.scaleMode = SKSceneScaleModeAspectFill;
//    
//    // Present the scene.
//    [skView presentScene:scene];
}

- (BOOL)shouldAutorotate
{
    return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

- (BOOL)prefersStatusBarHidden {
    return YES;//隐藏顶部状态栏
}

总结:
本次使用SpriteKit创建一个简单游戏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值