SpriteKit之第一个项目

1 名词概念

SpriteKit:提供一个图形渲染和动画的基础,可以有效的利用图形硬件来渲染动画的帧,而无须开发人员编写绘图指令,可以专注的解决更高层次的设计问题。

Sprite:精灵,在游戏里的背景,人物,物品等都是精灵。

SKView:动画和渲染由SKView执行,需要在一个窗口中放置该视图,然后渲染内容。

Scenes:场景,游戏中的内容会被组织成场景,由SKScene对象表示。包含了精灵和其它需要渲染的内容。一个游戏,可能需要创建一个或多个SKScene类或其子类。

SKNode:节点,实际上SKScene是SKNode的子类,场景对象是一个节点对象的根节点,决定子类哪个内容被绘制以及渲染。所有节点对象都是响应者,可以继承任何节点来创建接收用户输入的新类。

纹理:用来渲染精灵的共享图像,比如需要多个相同图像时,使用纹理来直接创建,而无需再次重新读取图形文件。

actions:动作,能够让场景内容动起来,每一个动作都是一个对象,由SKAction类表示。

2 开始

知道上方概念和大概作用后,我们开始写第一个项目,使用的是Xcode8.0和Swift3.0。

1.创建一个空项目

如下图,创建一个Single View Application,然后创建一个名词为Game的项目:
可能有人会问,为什么不选Game?我的回答是,没有写过游戏的朋友最好别创建Game模板,因为会自动给你添加很多东西很多方法,第一次看会觉得莫名其妙的。

屏幕快照 2017-01-24 上午11.40.42.png

2.项目修改

创建好后,项目需要做一些修改:
1.在ViewController.swift里,添加:

import SpriteKit

2.在Main.storyboard里,修改当前VC的View为SKView。如下:

屏幕快照 2017-01-24 上午11.46.07.png

3.修改ViewController.swift如下:

import UIKit
import SpriteKit

class ViewController: UIViewController {
    
    var spriteView: SKView!

    override func viewDidLoad() {
        super.viewDidLoad()
        
        spriteView = view as! SKView
        
        spriteView.showsDrawCount = true//使用多少绘画,越少越好
        spriteView.showsNodeCount = true//节点个数
        spriteView.showsFPS = true//FPS开启
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

此时,运行程序,就可以看到初步的效果了。如下:

屏幕快照 2017-01-24 上午11.52.46.png

3.编写场景代码

1.创建一个场景类,HelloScene继承自SKScene
2.HelloScene内导入SpriteKit
3.添加如下代码:

import UIKit
import SpriteKit

class HelloScene: SKScene {

    var contentCreated = false//标记场景是否已经创建
    
    override func didMove(to view: SKView) {//每当场景要被呈现时,会调用该方法,并且只在第一次调用
        if !contentCreated {
            createSceneContents()
            contentCreated = true
        }
    }
    
    func createSceneContents() {//自定义的创建场景内容的方法
        backgroundColor = SKColor.blue//背景,SKColor不是一个类,是一个宏,iOS上是UIColor,OS X上是NSColor。
        scaleMode = .aspectFit//缩放模式
        addChild(newHelloNode())
    }
    
    func newHelloNode() -> SKLabelNode {//创建一个label
        let helloNode = SKLabelNode(fontNamed: "Chalkduster")
        helloNode.text = "你好!"
        helloNode.fontSize = 42
        helloNode.position = CGPoint(x: frame.width/2, y: frame.height/2)
        return helloNode
    }
}

4.回到ViewController.swift中,添加present代码,呈现场景视图:

 override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        let hello = HelloScene(size: CGSize(width: view.frame.width, height: view.frame.height))
        spriteView.presentScene(hello)
    }

运行,就可以看到结果了。

屏幕快照 2017-01-24 下午2.11.45.png

3 使用Action动作

上面做出了一个静态的界面,我们可以通过Action动作将场景内的东西具有动作。我们创建一个action对象来描述想要的改变,然后告诉一个节点运行,当场景渲染时,动作会被执行。

给项目添加一个功能,当点击场景时,文字淡出,背景颜色变化。

在HelloScene场景类的newHelloNode()方法中,给节点添加名称,方便使用时查找:

helloNode.name = "LabelNode"

然后重写touchesBegan方法:

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let helloNode = childNode(withName: "LabelNode")
        if let node = helloNode {
            node.name = ""//防止按压事件重复触发,HelloNode被重复动作
            let moveUp = SKAction.moveBy(x: 0, y: 100, duration: 0.5)//上移
            let zoom = SKAction.scale(to: 2.0, duration: 0.25)//放大
            let pause = SKAction.wait(forDuration: 0.5)//暂停
            let fade = SKAction.fadeIn(withDuration: 0.25)//消失
            let remove = SKAction.removeFromParent()//移除
            let colorChange = SKAction.colorize(with: .orange, colorBlendFactor: 1, duration: 0.5)//改变背景色
            let moveSequence = SKAction.sequence([moveUp, zoom, pause, fade, remove])
            //动作执行
            node.run(moveSequence)
            self.run(colorChange)
        }
    }

由于所有节点都是UIResponder的子类,所以可以直接使用touchBegan来添加交互内容。

添加好后,运行,如图:

2017-01-24 15_05_19.gif

4 场景切换

场景之间的切换使用SpriteKit会非常容易。

1.创建新的ship场景继承于SKScene类,添加如下代码:

import UIKit
import SpriteKit

class ShipScene: SKScene {
    
    var contentCreated = false
    
    override func didMove(to view: SKView) {
        if !contentCreated {
            createScreated()
            contentCreated = true
        }
    }
    
    func createScreated() {
        backgroundColor = .black
        scaleMode = .aspectFit
    }

}

2.在HelloScene类中,修改touchesBegan方法中,node.run方法,如下:

node.run(moveSequence) {
                let ship = ShipScene(size: self.size)
                let transition = SKTransition.doorsOpenVertical(withDuration: 2)//动画类型
                self.view?.presentScene(ship, transition: transition)//呈现ship场景后,hello场景会被丢弃
            }

上面代码的作用是,当action执行完成后,切换场景。

运行,最终效果如图:

2017-01-24 15_28_50.gif

5 构造复杂的内容

新的场景没有任何内容,我可以使用很多个SKSpriteNode对象,创建出很复杂的内容。

我们创建出一个模拟的飞船,有两道亮光。
在ShipScene类的createScreated方法中添加飞船,代码如下:

func createScreated() {
        backgroundColor = .black
        scaleMode = .aspectFit
        
        let ship = newShip()
        ship.position = CGPoint(x: frame.width/2, y: frame.height/2)
        addChild(ship)
    }
    
    func newShip() -> SKSpriteNode {
        //添加飞船
        let node = SKSpriteNode(color: .green, size: CGSize(width: 100, height: 50))
        let hover = SKAction.sequence([SKAction.wait(forDuration: 1),
                                       SKAction.moveBy(x: 100, y: 50, duration: 1),
                                       SKAction.wait(forDuration: 1),
                                       SKAction.moveBy(x: -100, y: 150, duration: 1)])
        node.run(hover)
        
        //飞船添加亮光,亮光添加到飞船上后,飞船移动后,亮光也会一起移动,旋转等也是。
        let lingt1 = newLight()
        lingt1.position = CGPoint(x: 0, y: 30)
        node.addChild(lingt1)
        
        let lingt2 = newLight()
        lingt2.position = CGPoint(x: 0, y: -30)
        node.addChild(lingt2)
        
        return node
    }
    
    func newLight() -> SKSpriteNode {
        let lingt = SKSpriteNode(color: .white, size: CGSize(width: 50, height: 2))
        let blink = SKAction.sequence([SKAction.fadeOut(withDuration: 0.25),
                                       SKAction.fadeIn(withDuration: 0.25)])
        let blinkForever = SKAction.repeatForever(blink)//一直重复动作
        lingt.run(blinkForever)
        return lingt
    }

添加好后,运行,如图:

2017-01-24 16_01_42.gif

6 添加节点之间的交互

通常,游戏开始后,节点互相之间是可以进行交互的,SpriteKit提供了一个完整的物理模拟,可以添加行为到节点,我们在飞船上方添加掉落的石块来演示。

1.给飞船添加物理体

//给飞船添加物理体
        node.physicsBody = SKPhysicsBody(rectangleOf: node.size)
        node.physicsBody?.isDynamic = false//防止非常受物理交互影响,开启后,飞船的速度不会受物理碰撞影响

2.当前场景添加产生岩石的action

 //产生岩石
        let makeRocks = SKAction.sequence([SKAction.perform(#selector(addRock), onTarget: self),
                                           SKAction.wait(forDuration: 0.1, withRange: 0.15)])
        run(SKAction.repeatForever(makeRocks))

3.实现addRock方法:

//添加岩石
    func addRock() {
        let rock = SKSpriteNode(color: .white, size: CGSize(width: 10, height: 10))
        rock.position = CGPoint(x: frame.width*2/3, y: frame.height)
        rock.name = "rock"
        rock.physicsBody = SKPhysicsBody(rectangleOf: rock.size)
        rock.physicsBody?.usesPreciseCollisionDetection = true
        addChild(rock)
    }
    
    //岩石离开屏幕后,移除
    override func didSimulatePhysics() {
        enumerateChildNodes(withName: "rock") { (node, _) in
            if node.position.y < 0 {
                node.removeFromParent()
            }
        }
    }

添加完毕后,运行如图:

2017-01-24 16_48_44.gif

结语

这是使用SpriteKit的第一步,知道了很多基础的东西,比如画面是如何呈现,界面的层级关系,如何跳转等等。这只是一个开始,后面还有很多需要学的。

本文代码可以在我的github上面找到(Game01):https://github.com/flywo/SwiftGame

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 写在前面: 游戏开发菜鸟,本帅哥也是第一次研究SpriteKit,有很多都不懂,另外本文转自王巍老师的博客点击进入...
    CoderZNB阅读 4,187评论 4 19
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 由于很多小伙伴要demo我就不一一发了,直接丢在github上自己下载吧:https://github.com/s...
    FKSky阅读 23,357评论 27 99
  • 十月一号做的沙盘实验 这是四个跳舞的女孩子 这一周讨论了一个让我感触很深的话题——优秀 老师问我,觉得这四个女孩子...
    小猫咪不吃大鲤鱼阅读 268评论 0 0
  • 豆乳布丁,这是一个既熟悉又新鲜的东西。熟悉是因为它是一个布丁,不熟悉是因为它是豆乳制作的,而不是传统的奶油或者乳酪...
    Caphintty图图阅读 196评论 0 1