`
sunqichao
  • 浏览: 28637 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
社区版块
存档分类
最新评论

ios游戏开发 Sprite Kit教程:初学者 3

阅读更多

注:本文译自Sprite Kit Tutorial for Beginners

目录

碰撞检测和物理特性: 概述

至此我们已经可以让炮弹任意的发射了——现在我们要让忍者利用炮弹来消灭这些怪物。下面就添加一些代码来给炮弹与怪物相交做检测。

Sprite Kit内置了一个物理引擎,这非常的棒!该物理引擎不仅可以模拟现实运动,还能进行碰撞检测。

下面我们就在游戏中使用Sprite Kit的物理引擎来检测炮弹与怪物的碰撞。首先,我们来看看需要做些神马事情:

  • 物理世界的配置。物理世界是一个模拟的空间,用来进行物理计算。默认情况下,在场景(scene)中已经创建好了一个,我们可以对其做一些属性配置,例如重力感应。
  • 为精灵(sprite)创建对应的物体(physics bodies)。在Sprite Kit中,为了碰撞检测,我们可以为每个精灵创建一个相应的形状,并设置一些属性,这就称为物体(physics body)。注意:图文的形状不一定跟精灵的外形一模一样。一般情况,这个形状都是简单的、大概的(而不用精确到像素级别)——毕竟这已经足以够大多数游戏使用了。
  • 将精灵分类。在物体(physics body)上可以设置的一个属性是category,该属性是一个位掩码(bitmask)。通过该属性可以将精灵分类。在本文的游戏中,有两个类别——一类是炮弹,另一类则是怪物。设置之后,当两种物体相互碰撞时,就可以很容易的通过类别对精灵做出相应的处理。
  • 设置一个contact(触点) delegate。还记得上面提到的物理世界吗?我们可以在物理世界上设置一个contact delegate,通过该delegate,当两个物体碰撞时,可以收到通知。收到通知后,我们可以通过代码检查物体的类别,如果是怪物和炮弹,那么就做出相应的动作!

上面大致介绍了一下游戏策略,下面就来看看如何实现!

碰撞检测和物理特性: 实现

首先在MyScene.m文件顶部添加如下两个常量:

1
2
static const uint32_t projectileCategory     =  0x1 << 0;
static const uint32_t monsterCategory        =  0x1 << 1;

上面设置了两个类别,记住需要用位(bit)的方式表达——一个用于炮弹,另一个则是怪物。

注意:看到上面的语法你可能感到奇怪。在Sprite Kit中category是一个32位整数,当做一个位掩码(bitmask)。这种表达方法比较奇特:在一个32位整数中的每一位表示一种类别(因此最多也就只能有32类)。在这里,第一位表示炮弹,下一位表示怪兽。

接着,在initWithSize中,将下面的代码添加到位置:添加player到场景涉及代码的后面。

1
2
self.physicsWorld.gravity = CGVectorMake(0,0);
self.physicsWorld.contactDelegate = self;

上面的代码将物理世界的重力感应设置为0,并将场景设置位物理世界的代理(当有两个物体碰撞时,会受到通知)。

addMonster方法中,将如下代码添加创建怪兽相关代码后面:

1
2
3
4
5
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

来看看上面代码意思:

  1. 为怪兽创建一个对应的物体。此处,物体被定义为一个与怪兽相同尺寸的矩形(这样与怪兽形状比较接近)。
  2. 将怪兽设置位dynamic。这意味着物理引擎将不再控制这个怪兽的运动——我们自己已经写好相关运动的代码了。
  3. 将categoryBitMask设置为之前定义好的monsterCategory
  4. contactTestBitMask表示与什么类型对象碰撞时,应该通知contact代理。在这里选择炮弹类型。
  5. collisionBitMask表示物理引擎需要处理的碰撞事件。在此处我们不希望炮弹和怪物被相互弹开——所以再次将其设置为0。

接着在touchesEnded:withEvent:方法中设置炮弹位置的代码后面添加如下代码。

1
2
3
4
5
6
projectile.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:projectile.size.width/2];
projectile.physicsBody.dynamic = YES;
projectile.physicsBody.categoryBitMask = projectileCategory;
projectile.physicsBody.contactTestBitMask = monsterCategory;
projectile.physicsBody.collisionBitMask = 0;
projectile.physicsBody.usesPreciseCollisionDetection = YES;

在上面的代码中跟之前的类似,只不过有些不同,我们来看看: 1. 为了更好的效果,炮弹的形状是圆形的。 2. usesPreciseCollisionDetection属性设置为YES。这对于快速移动的物体非常重要(例如炮弹),如果不这样设置的话,有可能快速移动的两个物体会直接相互穿过去,而不会检测到碰撞的发生。

接着,添加如下方法,当炮弹与怪物发生碰撞时,会被调用。注意这个方法是不会被自动调用,稍后会看到我们如何调用它。

1
2
3
4
5
- (void)projectile:(SKSpriteNode *)projectile didCollideWithMonster:(SKSpriteNode *)monster {
    NSLog(@"Hit");
    [projectile removeFromParent];
    [monster removeFromParent];
}

当怪物和炮弹发生碰撞,上面的代码会将他们从场景中移除。很简单吧!

下面该实现contact delegate方法了。将如下方法添加到文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- (void)didBeginContact:(SKPhysicsContact *)contact
{
    // 1
    SKPhysicsBody *firstBody, *secondBody;
    if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
    {
        firstBody = contact.bodyA;
        secondBody = contact.bodyB;
    }
    else
    {
        firstBody = contact.bodyB;
        secondBody = contact.bodyA;
    }
    // 2
    if ((firstBody.categoryBitMask & projectileCategory) != 0 &&
        (secondBody.categoryBitMask & monsterCategory) != 0)
    {
        [self projectile:(SKSpriteNode *) firstBody.node didCollideWithMonster:(SKSpriteNode *) secondBody.node];
    }
}

还记得之前给物理世界设置的contactDelegate吗?当两个物体发生碰撞之后,就会调用上面的方法。

在上面的方法中,可以分为两部分来理解:

  1. 该方法会传递给你发生碰撞的两个物体,但是并不一定符合特定的顺序(如炮弹在前,或者炮弹在后)。所以这里的代码是通过物体的category bit mask来对其进行排序,以便后续做出正确的判断。注意,这里的代码来自苹果提供的Adventure示例。
  2. 最后,检测一下这两个碰撞的物体是否就是炮弹和怪物,如果是的话就调用之前的方法。

最后一步,为了编译器没有警告,确保private interface 中添加一下SKPhysicsContactDelegate

1
@interface MyScene () <SKPhysicsContactDelegate>

现在编译并运行程序,可以发现,当炮弹与怪物接触时,他们就会消失!

收尾

现在,本文的游戏快完成了。接下来我们就来为游戏添加音效和音乐,以及一些简单的游戏逻辑吧。

苹果提供的Sprite Kit里面并没有音频引擎(Cocos2D中是有的),不过我们可以通过action来播放音效,并且可以使用AVFoundation播放后台音乐。

在工程中我已经准备好了一些音效和很酷的后台音乐,在本文开头已经将resources添加到工程中了,现在只需要播放它们即可!

首先在ViewController.m文件顶部添加如下import:

1
@import AVFoundation;

上面的语法是iOS 7中新的modules功能 —— 只需要使用新的关键字@import,就可以框架的头文件和库文件添加到工程中,这功能非常方便。要了解更多相关内容,请看到iOS 7 by Tutorials中的第十章内容中的:What’s New with Objective-C and Foundation。

接着添加一个新的属性和private interface:

1
2
3
@interface ViewController ()
@property (nonatomic) AVAudioPlayer * backgroundMusicPlayer;
@end

接着将下面的代码添加到viewWillLayoutSubviews方法中(在[super viewWillLayoutSubviews]后面):

1
2
3
4
5
6
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];

上面的代码会开始无限循环的播放后台音乐。

下面我们来看看如何处理音效。切换到MyScene.m文件中,并将下面这行代码添加到touchesEnded:withEvent:方法的顶部:

1
[self runAction:[SKAction playSoundFileNamed:@"pew-pew-lei.caf" waitForCompletion:NO]];

如上,一行代码就可以播放音效了,很简单吧!

下面,我们创建一个新的创建和layer,用来显示你赢了(You Win)你输了(You Lose)。用模板iOS\Cocoa Touch\Objective-C class创建一个新的文件,将其命名为GameOverScene,并让其继承自SKScene,然后点击NextCreate

接着用如下代码替换GameOverScene.h中的内容:

1
2
3
4
5
6
7
#import <SpriteKit/SpriteKit.h>
@interface GameOverScene : SKScene
-(id)initWithSize:(CGSize)size won:(BOOL)won;
@end

在上面的代码中导入了Sprite Kit头文件,并声明了一个特定的初始化方法,该方法的第一个参数用来定位显示的位置,第二个参数won用来判断用户是否赢了。

接着用下面的代码替换GameOverLayer.m中的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#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 :[";
        }
        // 3
        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];
        // 4
        [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;
}
@end

上面的代码可以分为4部分内容,我们来分别看看:

  1. 将背景色设置为白色(与主场景一样颜色)。
  2. 根据won参数,将信息设置为”You Won”或”You Lose”。
  3. 这里的代码是利用Sprite Kit将一个文本标签显示到屏幕中。如代码所示,只需要选择一个字体,并设置少量的参数即可,也非常简单。
  4. 设置并运行有个有两个action的sequence。为了看起来方便,此处我将它们放到一块(而不是为每个action创建单独的一个变量)。首先是等待3秒,然后是利用runBlockaction来运行一些代码。
  5. 演示了在Sprite Kit中如何过渡到新的场景。首先可以选择任意的一种不同的动画过渡效果,用于场景的显示,在这里选择了翻转效果(持续0.5秒)。然后是创建一个想要显示的场景,接着使用self.view的方法presentScene:transition:来显示出场景。

OK,万事俱备,只欠东风了!现在只需要在主场景中,适当的情况下加载game over scene就可以了。

首先,在MyScene.m中导入新的场景:

1
#import "GameOverScene.h"

然后,在addMonster中,用下面的代码替换最后一行在怪物上运行action的代码:

1
2
3
4
5
6
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]]];

上面创建了一个”lose action”,当怪物离开屏幕时,显示game over场景。

在这里为什么loseAction要在actionMoveDone之前运行呢? 原因在于如果将一个精灵从场景中移除了,那么它就不在处于场景的层次结构中了,也就不会有action了。所以需要过渡到lose场景之后,才能将精灵移除。不过,实际上actionMoveDone永远都不会被调用——因为此时已经过渡到新的场景中了,留在这里就是为了达到教学的目的。

现在,需要处理一下赢了的情况。在private interface中添加一个新的属性:

1
@property (nonatomic) int monstersDestroyed;

然后将如下代码添加到projectile:didCollideWithMonster:的底部:

1
2
3
4
5
6
self.monstersDestroyed++;
if (self.monstersDestroyed > 30) {
    SKTransition *reveal = [SKTransition flipHorizontalWithDuration:0.5];
    SKScene * gameOverScene = [[GameOverScene alloc] initWithSize:self.size won:YES];
    [self.view presentScene:gameOverScene transition: reveal];
}

编译并运行程序,尝试一下赢了和输了会看到的画面!

何去何从?

至此Sprite Kit教程:初学者结束!这里可以下到完整的代码

希望本文能帮助你学习Sprite Kit,并写出你自己的游戏!

如果你希望学习更多相关Sprite Kit内容,可以看看这本书:iOS Games by Tutorials。本书会告诉你需要知道的内容——从物理,到tile map,以及特定的系统,甚至是制作自己的关卡编辑器。

……Sprite Kit教程:初学者 3 结束……

分享到:
评论

相关推荐

    iOS游戏框架Sprite Kit基础教程 Swift版上册

    总之,"iOS游戏框架Sprite Kit基础教程 Swift版上册"将带你逐步走进2D游戏开发的世界,通过实例教学,让你掌握使用Sprite Kit和Swift开发游戏的核心技术。无论是初学者还是有一定经验的开发者,都能从中受益,快速...

    IOS 2D游戏开发框架 Spritekit案例

    SpriteKit是苹果公司为iOS、macOS平台设计的一款强大的2D游戏开发框架,它提供了丰富的图形渲染、物理模拟、动画和交互功能,让开发者能够轻松创建高质量的2D游戏。本篇文章将深入探讨SpriteKit的基本概念、核心组件...

    sprite kit"割绳子"游戏详解

    本教程将向初学者介绍如何利用Sprite Kit框架开发一个类似“割绳子”(Cut the Rope)的iOS游戏。首先需要明确Sprite Kit是苹果官方提供的一个用于开发iOS平台2D游戏的框架,它集成了物理引擎、图形渲染、声音播放等...

    SpriteKit物理系统Demo

    SpriteKit是苹果公司开发的游戏引擎,它为iOS、macOS和tvOS等平台提供了一套强大的2D游戏开发工具。这个“SpriteKit物理系统Demo”是开发者为了展示如何在SpriteKit环境中实现物理模拟而创建的一个实例。让我们深入...

    iOS 7 Sprite Kit学习demo1

    这个“iOS 7 Sprite Kit学习demo1”是一个非常适合初学者上手实践的例子,通过它,我们可以深入理解Sprite Kit的基本概念和关键功能。 首先,Sprite Kit的核心组件包括`SKScene`、`SKNode`、`SKEffectNode`、`...

    sprite kit 编程指南

    ### Sprite Kit编程指南:iOS游戏开发的利器 Sprite Kit是苹果公司为iOS、...无论是初学者还是经验丰富的开发者,都可以从Sprite Kit的文档和社区资源中获益,不断优化自己的游戏作品,创造出更加引人入胜的游戏体验。

    sprite kit demo

    Sprite Kit是苹果公司为iOS、macOS平台开发的一款2D游戏引擎,专为构建轻量级的游戏和互动式媒体内容而设计。...通过研究这个示例,初学者能快速掌握Sprite Kit的核心功能,并进一步提升游戏开发技能。

    IOS游戏开发入门源码 Canabalt 一款流行的跑跳iOS游戏

    3. **SpriteKit/SceneKit**:虽然《Canabalt》可能未使用这些现代游戏引擎,但了解它们是提升iOS游戏开发能力的重要步骤。SpriteKit适合2D游戏,SceneKit则用于3D游戏,它们提供了许多动画和物理模拟功能。 二、...

    GravityZ:我将使用Swift和iOS SpriteKit实现的一款正在进行中的游戏-测试SpriteKit和不同的物理等。

    《Swift与iOS SpriteKit在游戏开发中的应用——以“GravityZ”为例》 在iOS游戏开发领域,Swift语言和SpriteKit框架的结合为开发者提供了强大的工具。本文将深入探讨如何利用Swift和SpriteKit来创建一个名为...

    Swift-Example-Introduction-to-SpriteKit:制作一个最小的视频游戏作为对 SpriteKit 的简单介绍

    SpriteKit 是苹果开发的一款2D游戏引擎,专为iOS、macOS、tvOS和watchOS平台设计。这个框架提供了一套完整的工具集,用于创建高质量的动画和交互式游戏。在"Swift-Example-Introduction-to-SpriteKit"项目中,我们将...

    IOS游戏开发入门代码 HotChess 一款中国象棋的棋牌类iOS游戏

    HotChess不仅为初学者提供了一个学习iOS游戏开发的平台,也为经验丰富的开发者提供了一种实现棋类游戏逻辑的新方法。我们将讨论以下几个关键知识点: 1. **Swift编程语言**:HotChess是用Apple的Swift语言编写的,...

    swift语言IOS开发PDF

    2. **iOS游戏框架Sprite Kit基础教程——Swift版上册a.pdf** 和 **iOS游戏框架Sprite Kit基础教程——Swift版上册.pdf**:Sprite Kit是苹果提供的2D游戏开发框架,支持物理模拟、粒子系统、动画等功能。这些教程会...

    iOS游戏开发入门经典.rar

    4. ** SpriteKit**:SpriteKit是Apple提供的2D游戏开发框架,它包含动画、物理模拟、粒子效果等功能。如果你打算开发2D游戏,SpriteKit是一个很好的起点,它简化了许多游戏开发的复杂性。 5. **SceneKit**:对于3D...

    rayninja-swift-spritekit-ios:使用 Swift、SpriteKit、Xcode 6.1.1 和 iOS SDK 8.1 的简​​单游戏

    在本项目"rayninja-swift-spritekit-ios"中,开发者使用了Apple的编程语言Swift,结合SpriteKit框架和Xcode 6.1.1版本的集成开发环境(IDE),来创建一个针对iOS平台的游戏。游戏开发是移动应用开发中的一个重要领域...

    ios 游戏 game 代码

    在iOS平台上开发游戏,通常会使用苹果提供的Swift编程语言和一套名为SpriteKit的游戏开发框架。Swift是一种现代化、安全且性能强大的编程语言,适用于构建各种类型的应用程序,包括游戏。SpriteKit则是苹果专为2D...

    iOS软件开发揭密:iPhone&iPad企业应用和游戏开发

    总之,《iOS软件开发揭密》全面覆盖了iOS开发的各个方面,无论你是初学者还是有一定经验的开发者,都能从中受益。通过本书的学习,你将能够开发出功能丰富、用户体验优秀的iPhone和iPad应用及游戏,进一步提升自己的...

    iOS实战项目开发:汤姆猫小游戏

    在本项目中,"iOS实战项目开发:汤姆猫小游戏" 是一个适合初学者的实践教程,旨在通过构建一个简单的游戏应用来学习iOS开发。这个小游戏以著名的虚拟宠物"汤姆猫"为题材,提供了有趣的互动体验。以下是该项目中涉及...

    ios 苹果游戏源码

    【标题】:“ios 苹果游戏源码” 在iOS平台上开发游戏是一项技术性强且充满挑战的任务,因为苹果公司的iOS系统有着严格的开发...同时,对于初学者,通过学习和分析源码,可以快速上手iOS游戏开发,从而缩短学习曲线。

    iPhone游戏开发入门经典:也适用于iPad源码

    《iPhone游戏开发入门经典:也适用于iPad》这本书是为初学者设计的一本指南,旨在帮助读者踏入iOS游戏开发的世界。本书特别强调了对iPhone和iPad平台的兼容性,这意味着开发者可以一次性编写代码,同时覆盖这两种设备...

Global site tag (gtag.js) - Google Analytics