image

小说:什么品种紫荆花好?作者:安平陵更新时间:2019-04-23字数:73782

开始游戏教程前,首先介绍一下SpriteKit是什么?
SpriteKit提供了一个图形渲染和动画的基础结构,你可以使用它让任意类型的纹理图片或者精灵动起来。SpriteKit使用渲染循环,利用图形硬件渲染动画的每一帧。

 
SpriteKit框架渲染每一帧的周期流程原理图

 

在iOS传统的view的系统中,view的内容被渲染一次后就将一直等待,直到需要渲染的内容发生改变(比如用户发生交互,view的迁移等)的时候,才进行下一次渲染。这主要是因为传统的view大多工作在静态环境下,并没有需要频繁改变的需求。而对于SpriteKit来说,其本身就是用来制作大多数时候是动态的游戏的,为了保证动画的流畅和场景的持续更新,在SpriteKit中view将会循环不断地重绘。

动画和渲染的进程是和SKScene对象绑定的,只有当场景被呈现时,这些渲染以及其中的action才会被执行。SKScene实例中,一个循环按执行顺序包括:

每一帧开始时,SKScene的-update:方法将被调用,参数是从开始时到调用时所经过的时间。在该方法中,我们应该实现一些游戏逻辑,包括AI,精灵行为等等,另外也可以在该方法中更新node的属性或者让node执行action
在update执行完毕后,SKScene将会开始执行所有的action。因为action是可以由开发者设定的(还记得runBlock:么),因此在这一个阶段我们也是可以执行自己的代码的。

在当前帧的action结束之后,SKScene的-didEvaluateActions将被调用,我们可以在这个方法里对结点做最后的调整或者限制,之后将进入物理引擎的计算阶段。

然后SKScene将会开始物理计算,如果在结点上添加了SKPhysicsBody的话,那么这个结点将会具有物理特性,并参与到这个阶段的计算。根据物理计算的结果,SpriteKit将会决定结点新的状态。

然后-didSimulatePhysics会被调用,这类似之前的-didEvaluateActions。这里是我们开发者能参与的最后的地方,是我们改变结点的最后机会。
一帧的最后是渲染流程,根据之前设定和计算的结果对整个呈现的场景进行绘制。完成之后,SpriteKit将开始新的一帧。

在了解了一些SpriteKit的基础概念后,就跟着iFIERO来创建一个简单的游戏作为开启游戏入门之旅吧。

此《宇宙大战 Space Battle》教程共分为三个章节系列,

(一)宇宙大战 Space Battle — 新建场景Scene、导入各个SpriteNode精灵、Particle粒子节点及建立背景音乐(你正在此处进行学习)

(二)宇宙大战 Space Battle — 创建无限循环的背景Endless、监测精灵之间的物体碰撞及物理引擎Accleroation

(三)宇宙大战 Space Battle — 各个场景SCENE之间的切换、利用UserDefaults统计分数

你将在此教程中的三个系列当中学到如下的技能:

  • SpaceBattle 宇宙大战 在此游戏中您将获得如下技能:

  • 1、LaunchScreen 学习如何设置游戏启动画面;

  • 2、Scenes 学习如何切换不同的场景 主菜单+游戏场景+游戏结束场景;

  • 3、Accleroation 利用重力加速度 让飞船左右移动;

  • 4、Endless Background 无限循环背景;

  • 5、Scene Edit 直接使用可见即所得操作;

  • 6、UserDefaults 保存游戏分数、最高分;

  • 7、Random 利用可复用的随机函数生成Enemy;

  • 8、Background Music 如何添加背景音乐;

  • 9、Particle 粒子爆炸特效;

应用以上各项SpriteKit与Swift技能,你开发出来的最终手机游戏的效果为如下所示:

 
image

一、教程开始 Getting Started

游戏开始前,请下载本教程的初始项目(http://www.ifiero.com/uploads/SpaceBattle-01Starter.zip)。本游戏是由SpriteKit框架、Swift语言,XCODE开发工具进行开发的。

1、打开XCODE(请用正式版,非Beta版),选择Create a new Xcode project,选择iOS->Game,输入Product Name(这里命名为SpaceBattle),开发语言Language选择Swfit,点击Next,工程即新建完毕

 
xcode.png
 
iosGame.png
 
03.png

2、选择Genrnal面板,因为此Space Battle游戏为竖屏游戏,所以去除勾选Deployment Info -> Device Orientation中的Landscape Left 与Landscape Right,我们不需要横屏效果

 
04.png

3、删除XCode左侧目录中的 Action.sks(暂时没有用到),修改GameScene.swift及GameViewController.swift的相关代码

 
删除Action.sks
 
修改GameScene.swift的代码
 
设置Scene的尺寸为CGSize(x:1536,y:2048)

二、可视化编辑场景 Introducing the Sprite Kit Visual Editor

1.首先需要编辑场景.sks文件,打开GameScene.sks文件,设置场景的尺寸为iPAD4:3的比例(W:1536,H:2048),并删除场景中的文字。

 

 
点击Color面板,可修改Scene的场景背景颜色

2.拖动音乐文件到导航栏navigator->SpaceBattle文件夹,勾选Copy items if needed,Added folders选择Create groups,Add to targets勾选SpaceBattle

 

 
拖动音乐文件

3.拖动游戏工程所需要的图片到Assets.xcassets文件夹

 

 
Assets.xcassets

4.资源导入后,左侧的导航栏navigator如下图所示

 

 
左侧的导航栏navigator样式

5.Assets图库中的图片尺寸分为1x,2x,3x,你只需设置1x的图片尺寸大小即可,SpriteKit会自动根据你运行的device设备尺寸(iPhoneX,iPhone,iPhone Plus,iPad)进行相应比例的调整。

 

 
Assets图库中的图片尺寸分为1x,2x,3x,只需设置1x图片即可

非常的棒,你已经学会如何导入Mac电脑中的资源文件(图片、音乐、粒子)到SpaceBattle游戏工程内中了。
那么,现在我们就来学习如何新建SpriteKit精灵节点吧!

6.选择左侧导航栏Navigator的GameScene.sks,直接拖动一个Color Sprite到场景中,选中精灵,设置Position(0,0),修改texture为BG_SpaceBattle_planet(AssetsAssets.xcassets文件夹的名称),并命名精灵节点的名称 Name为bg。

 

 
拖动一个Color Sprite到场景中
 
命名精灵节点的名称 Name为bg

7.现在你可以运行模拟器(XCode -> Product -> Run),看看你的游戏是否正确显示你刚刚建立的精灵节点。

 

 
选择device设备
 
Run运行
 
Simulator模拟器

棒棒哒! 你已学会了如何在场景中建立精灵节点及如何运行模拟器进行调试!

三、SpriteKit Physics 物理引擎

1.Spritekit提供了一个默认的物理模拟系统,用来模拟真实物理世界,可以使得编程者将注意力从力学碰撞和重力模拟的计算中解放出来,通过简单地代码来实现物理碰撞的模拟,而将注意力集中在更需要花费精力的地方。现在,让我们来学习这个系统的使用吧。

首先需要认识两个类,一个是场景scene的属性类SKPhysicsWorld,这个类基于场景,只能被修改但是不能被创建,这个类负责提供重力和检查碰撞(碰撞需要实现SKPhysicsContactDelegate代理协议),另一个就是SKPhysicsBody类,你可以对你的SKNode节点添加物理体属性,来让他们可以参与物理模拟的相关计算。

SpriteKit SKPhysicsBody类物理体的属性图表

属 性 功 能 图示
mass 它决定力是如何影响主体,以及当主体参与碰撞时它有多大的动量,以千克为单位
 
mass
friction 它决定了物体的光滑程度.取值范围为从0.0(表面光滑,物体滑动很顺畅,就像小冰块似的)到1.0(在表面滑动是,物体会很快停止) --
linearDamping 物体的线性阻尼.取值范围为0.0(速度从不衰减)到1.0(速度立即衰减).默认值为0.1该属性被用于模拟水流或者空气的阻力.
 
linearDamping
angularDamping 物体的角速度阻尼.取值范围为0.0(速度从不衰减)到1.0(速度立即衰减).默认值为0.1该属性被用于模拟水流或者空气的阻力.
 
angularDamping
restitution 描述了当物理实体从另外一个物体上弹出时,还拥有多少能量.基本上我们称之为"反弹力".它的取值介于0.0(完全不反弹)到1.0(和物体碰撞反弹是所受的力与刚开始碰撞时的力的大小相同)之间.默认值为0.2
 
restitution
density 物体的密度,以千克每立方米为单位.密度是根据单位体积的质量来定义的.密度越高,体积越大,物体也就会越重.密度的默认值为1.0 --
affectedByGravity 设置物体是否受重力的影响.所有的物体默认的情况都是受重力影响,但是开发者可以简单的吧这个标记设置为NO,使其不受重力影响.
 
affectedByGravity
allowsRotation 设置物体是否受到一个旋转力的影响,默认为YES,如果该值设置为NO,物理体将忽略施加在它身上所有的力 --
resting 设置物理体是否在休息.物理引擎对于一段时间内没有移动过的物体做了一个优化,把他们标记为"正在休息(resting)",这样,物理引擎就不需要对它们进行计算了.如果你想要手动的唤醒一个正在休息的物体,简单的把resting设置为NO即可 --
categoryBitMask 一个16进制数,定义了物体的类别.场景中每一个物理体都可以分配到超过32个不同的种类里面,每个对应位中的值
 
categoryBitMask
collisionBitMask 一个16进制数,定义哪种类别的物理体可以与之发生碰撞.当两个物体相关联的时候,就可能发生一个碰撞.这个物体的位相对于其他物体的类别做一个逻辑上的加法操作.如果结果是一个非零的值,则该物体收到碰撞的影响 --
contactTestBitMask 一个16进制数,两个物体碰撞后发出通知 didBegin可接收到通知 --
usesPreciseCollisionDetection 设置物体是否使用更精准的碰撞算法.默认情况下,除非确实有必要,Sprite Kit并不会启动精确的冲突检测,因为这样运行效率更高.但是不启动精确的冲突检测会有一个副作用,如果一个物体移动的非常快(比如一个子弹),它可能会直接穿过其他物体.如果这种情况确实发生了,你就应该尝试启动更精准的冲突检测了 --
velocity 物理体的速度矢量
 
velocity
angularVelocity 物理体的角速度.角速度是一个围绕着一个轴矢量(0.0,0.0,1.0)的速度,单位是弧度每秒
 
angularVelocity

以上图表感谢简书作者的收集整理:https://www.jianshu.com/p/4046bab3a63d

2.对SpriteKit PhysicsBody类的基础的概念了解后,我们现在就来新建player玩家飞船节点playerNode还有alien外星飞船精灵节点,并设置他们的物理属性。

 

 
新建player玩家飞船节点 属性面板中Sprite Name命名为playerNode
class GameScene: SKScene,SKPhysicsContactDelegate {
    
    private var playerNode:SKSpriteNode!  /// 玩家 宇宙飞船
    
    override func didMove(to view: SKView) {
        
        physicsWorld.gravity = CGVector(dx: 0, dy: -9.8) /// 建立物理世界 重力向下
        physicsWorld.contactDelegate = self              /// 碰撞接触代理为当前scene (GameScene)
        setupPlayer()
    }
    
    //MARK: - 玩家 宇宙飞船
    func setupPlayer(){
        playerNode = childNode(withName: "playerNode") as! SKSpriteNode
        playerNode.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "Player"), size: SKTexture(imageNamed: "Player").size())
        playerNode.physicsBody?.affectedByGravity = false // 不受物理世界的重力影响
        playerNode.physicsBody?.isDynamic = true
        playerNode.physicsBody?.categoryBitMask    = PhysicsCategory.SpaceShip /// 唯一标识
        playerNode.physicsBody?.collisionBitMask   = PhysicsCategory.None      /// 碰撞后要弹开吗
        playerNode.physicsBody?.contactTestBitMask = PhysicsCategory.Alien     /// 碰撞后发出通知
    }
    
    override func update(_ currentTime: TimeInterval) {
        // Called before each frame is rendered
    }
}

随机生成alien精灵节点

  //MARK: -  生成随机Alien
    @objc func spawnAlien() {
        // 1 or 2
        let i = Int(CGFloat(arc4random()).truncatingRemainder(dividingBy: 2) + 1)
        
        let imageName = "Enemy0(i)"
        let alien  = SKSpriteNode(imageNamed: imageName)
        alien.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        alien.zPosition   = 1
        alien.name = "Alien"
        var xPosition:CGFloat = 0.0
        // 生成随机的x-Axis轴的位置
        xPosition = CGFloat.random(min: -self.frame.size.width+alien.size.width, max: self.frame.size.width - alien.size.width)
        alien.position = CGPoint(x: xPosition, y: self.frame.size.height + alien.size.height * 2)
        self.addChild(alien)
        // 物理体 PhysicsBody
        alien.physicsBody = SKPhysicsBody(circleOfRadius: alien.size.width / 2)  /// 设置物理身体
        alien.physicsBody?.affectedByGravity = false /// 不受重力影响,自定义飞船移动速度;
        alien.physicsBody?.categoryBitMask   = PhysicsCategory.Alien /// 1.设置唯一属性
        alien.physicsBody?.contactTestBitMask = PhysicsCategory.BulletBlue | PhysicsCategory.SpaceShip /// 2.和哪些节点Node发生碰撞后发出通知
        alien.physicsBody?.collisionBitMask   = PhysicsCategory.None /// 3.碰撞后是否弹开
        
        let duration   = CGFloat.random(min: CGFloat(1.0), max: CGFloat(3.8))  ///随机函数 返回二个数之间的随机数
        let actionDown = SKAction.move(to: CGPoint(x: xPosition, y: -self.frame.size.height), duration: TimeInterval(duration))
        alien.run(SKAction.sequence([actionDown,
                                     SKAction.run({
                                        alien.removeFromParent() // 移除节点;
                                     })]))
        
    }

CGFloat.random 拓展函数 返回二个数之间的随机数

import CoreGraphics
import SpriteKit

public extension CGFloat {
    
    #if !(arch(x86_64) || arch(arm64))
    func sqrt(a: CGFloat) -> CGFloat {
    return CGFloat(sqrtf(Float(a)))
    }
    #endif
    
    public static func random() -> CGFloat {
        return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
    }
    
    public static func random(min: CGFloat, max: CGFloat) -> CGFloat {
        assert(min < max)
        return CGFloat.random() * (max - min) + min
    }
    
}

在override func didMove(to view: SKView) {}内应用Timer.scheduledTimer间隔0.5秒生成Alien

// spawnAlien()
        Timer.scheduledTimer(timeInterval: TimeInterval(0.5), target: self, selector: #selector(GameScene.spawnAlien), userInfo: nil, repeats: true)

现在Command+R 运行工程下(或选择XCODE -> Product-> Run),你在模拟器中应可以看到源源不断的alien外星飞船正向下俯冲

 
alien外星飞船正向下俯冲

3.生成子弹及粒子效果

 // MARK: - 生成子弹; 点击屏幕后才发射
    func spawnBulletAndFire(){
        /// 子弹
        let bulletNode = SKSpriteNode(imageNamed: "BulletBlue")
        bulletNode.position.x = playerNode.position.x
        // 子弹的Y轴位置 因为playNode的AnchorPoit位于飞船中心 所以子弹发射时的瞬间位置位于飞船正中心,要加上飞船的半径,位于枪口;
        bulletNode.position.y = playerNode.position.y + playerNode.size.height / 2
        bulletNode.zPosition = 1
        self.addChild(bulletNode)
        bulletNode.physicsBody = SKPhysicsBody(circleOfRadius: bulletNode.size.width / 2)
        bulletNode.physicsBody?.affectedByGravity = false // 子弹不受重力影响;
        bulletNode.physicsBody?.categoryBitMask   =  PhysicsCategory.BulletBlue
        bulletNode.physicsBody?.contactTestBitMask = PhysicsCategory.Alien
        bulletNode.physicsBody?.collisionBitMask  = PhysicsCategory.None
        bulletNode.physicsBody?.usesPreciseCollisionDetection = true ///子弹飞速运动,设置探测精细碰撞
        
        /// 把子弹往上移出屏幕
        let moveTo = CGPoint(x: playerNode.position.x, y: playerNode.position.y + self.frame.size.height)
        
        /*
         * 粒子效果
         * 1.新建一个SKNODE => trailNode
         * 2.新建粒子效果SKEmitterNode,设置tragetNode = trailNode
         * 3.子弹加上emitterNode
         */
        let trailNode = SKNode()
        trailNode.zPosition = 1
        trailNode.name = "trail"
        addChild(trailNode)
        
        let emitterNode = SKEmitterNode(fileNamed: "ShootTrailBlue")! // particles文件夹存放粒子效果
        emitterNode.targetNode = trailNode  /// 设置粒子效果的目标为trailNode => 跟随新建的trailNode
        bulletNode.addChild(emitterNode)    /// 在子弹节点Node加上粒子效果;
        
        bulletNode.run(SKAction.sequence([
            SKAction.move(to: moveTo, duration: TimeInterval(0.5)),
            SKAction.run({
                bulletNode.removeFromParent() /// 移除 子弹bulltedNode
                trailNode.removeFromParent()  /// 移除 trailNode
            })]))
    }

我们将在第二章节学习飞船子弹的发射以及粒子效果的知识

四、到此,此章节就接近尾声了

我们已经学会了很多技能,包括如何新建工程,如何建立Sprite精灵节点,还有如何应用SpriteKit Physics物理引擎。你可以在此下载此章节的工程完整代码。(http://www.iFIERO.com/uploads/SpaceBattle-01final.zip)

五、更多内容

在下一章节当中,(二)宇宙大战 Space Battle — 创建无限循环的背景Endless、监测精灵之间的物体碰撞及物理引擎Accleroation,我们将学习如何监测SpriteKit Physics物理之间碰撞,如何销毁对象,如何监测屏幕Scene的点击事件以及物理引擎Accleroation的相关知识。

请注意,此《宇宙大战 Space Battle》教程共分为三个章节系列:

(一)宇宙大战 Space Battle — 新建场景Scene、导入各个SpriteNode精灵、Particle粒子节点及建立背景音乐(你正在此处进行学习)

(二)宇宙大战 Space Battle — 创建无限循环的背景Endless、监测精灵之间的物体碰撞及物理引擎Accleroation

(三)宇宙大战 Space Battle — 各个场景SCENE之间的切换、利用UserDefaults统计分数

更多游戏教学:iFIERO.COM -- 开源手机游戏教程网,让手机游戏开发变得简单!

 

 
iFiero.com

当前文章:http://leetaemin.cn/content/2019-01/26/content_41382.html

发布时间:2019-04-23 16:49:18

2017年八角金盘小苗的行情 国内大型红枫产地,年产量全国第一 四川有种植黄金柳的基地吗? 水杉树2月份可以栽植吗? 【视频】5公分6公分苦楝树发货中 5厘米花石榴苗木哪里有? 【园艺师分享】哪种马蹄金种植方法发芽率最好 草牧场补播什么牧草? 什么牧草产量高? 刺柳种子什么时候可以播种?

59183 58427 62972 69208 98417 59755 46958 93576 46562 73248 24218 37833 77411 39744 17228 51028 38265 28267 69817 64004 29908 12200 58680

我要说两句: (0人参与)

发布