像素鸟曾经非常火爆,游戏简单,很有趣味性,仿写一个叫 crashy plane 的游戏,它的原理跟像素鸟是一样的,接下来用 SpriteKit 来实现它

同时推荐一个不错的学习 Swift 的网站,这个 Crashy Plane 就是从那里偷来的

目录:

开始:

0.创建项目

a.创建项目 选择 Game

b.找到 GameScene.sks,将 helloWorld 的 label删除掉,然后将宽高调整为 W:375 H:667, 锚点设置为 X:0 Y:0,重力设置为X:0 Y:-5

c.打开GameScene.swift 删除多余的代码,只留下 override func didMove(to view: SKView) ,override func update(_ currentTime: TimeInterval)和override func touchesBegan(_ touches: Set, with event: UIEvent?)

1.生成 ui

飞机

func createPlayer() {

let playerTexture = SKTexture(imageNamed: R.image.player1.name)

player = SKSpriteNode(texture: playerTexture)

player.position = CGPoint(x: frame.width / 6, y: frame.height * 0.75)

player.zPosition = 10

addChild(player)

}

复制代码天空

func createSky() {

let topSky = SKSpriteNode(color: UIColor(hue: 0.55, saturation: 0.14, brightness: 0.97, alpha: 1), size: CGSize(width: frame.width , height: 0.67 * frame.height))

topSky.anchorPoint = CGPoint(x: 0.5, y: 1)

topSky.zPosition = -40

topSky.position = CGPoint(x: frame.midX, y: frame.maxY)

let bottomSky = SKSpriteNode(color: UIColor(hue: 0.55, saturation: 0.16, brightness: 0.96, alpha: 1), size: CGSize(width: frame.width , height: 0.33 * frame.height))

bottomSky.anchorPoint = CGPoint(x: 0.5, y: 1)

bottomSky.zPosition = -40

bottomSky.position = CGPoint(x: frame.midX, y: 0.33 * frame.height)

addChild(topSky)

addChild(bottomSky)

}

复制代码背景

生成两个首位相接的背景图,方便后期无限运动的效果

func createBackground() {

let backgroundTexture = SKTexture(imageNamed: R.image.background.name)

for i in 0 ... 1 {

let background = SKSpriteNode(texture: backgroundTexture)

background.zPosition = -30

background.anchorPoint = .zero

background.position = CGPoint(x: CGFloat(i) * backgroundTexture.size().width, y: 100)

addChild(background)

}

}

复制代码地面

同背景,也是生成两个首尾相接的地面

func createGround() {

let groundTexture = SKTexture(imageNamed: R.image.ground.name)

for i in 0...1{

let ground = SKSpriteNode(texture: groundTexture)

ground.zPosition = -10

ground.position = CGPoint(x: (CGFloat(i) + 0.5) * groundTexture.size().width, y: groundTexture.size().height/2)

addChild(ground)

}

}

复制代码上下的两个石头(类似像素鸟的两个管道)

上下两个石头是一样的 texture,只是上面的石头是旋转后再镜面翻转

两个石头起始位置在屏幕右侧之外

两个石头的距离固定,位置随机

两个石头后面添加一个得分检测节点(SKNode),用于判断飞机飞过石头得分的

func createRocks() {

let rockTexture = SKTexture(imageNamed: R.image.rock.name)

let topRock = SKSpriteNode(texture: rockTexture)

topRock.zRotation = .pi

topRock.xScale = -1

topRock.zPosition = -20

let bottomRock = SKSpriteNode(texture: rockTexture)

bottomRock.zPosition = -20

let rockCollision = SKSpriteNode(color: .red, size: CGSize(width: 32, height: frame.height))

rockCollision.name = scoreDetect

addChild(topRock)

addChild(bottomRock)

addChild(rockCollision)

let xPosition = frame.width + topRock.frame.width

let max = CGFloat(frame.width/3)

let yPosition = CGFloat.random(in: -50...max)

let rockDistance: CGFloat = 70

topRock.position = CGPoint(x: xPosition, y: yPosition + topRock.size.height + rockDistance)

bottomRock.position = CGPoint(x: xPosition, y: yPosition - rockDistance)

rockCollision.position = CGPoint(x: xPosition + rockCollision.size.width * 2, y: frame.midY)

}

复制代码得分 Lable

func createScore() {

scoreLabel = SKLabelNode(fontNamed: "Optima-ExtraBlack")

scoreLabel.fontSize = 24

scoreLabel.position = CGPoint(x: frame.midX, y: frame.maxY - 60)

scoreLabel.text = "SCORE: 0"

scoreLabel.fontColor = UIColor.black

addChild(scoreLabel)

}

复制代码效果图:

2.生成动态效果

飞机只是一张图片,后面的山也没用动起来,别着急,接下来让所有的节点都动起来

飞机

素材库一共三张飞机的图片,每0.1秒改变一张图片,然后一直循环

createPlayer方法添加代码

let frame2 = SKTexture(imageNamed: R.image.player2.name)

let frame3 = SKTexture(imageNamed: R.image.player3.name)

let animation = SKAction.animate(with: [playerTexture,frame2,frame3,frame2], timePerFrame: 0.1)

let forever = SKAction.repeatForever(animation)

player.run(forever)

复制代码背景

每个 background 在20秒内移动一个 texture 宽度的距离

在0秒内把每个 background 复位

上面两个动作重复

createBackground方法每个background 添加 如下action

let move = SKAction.moveBy(x: -backgroundTexture.size().width, y: 0, duration: 20)

let reset = SKAction.moveBy(x: backgroundTexture.size().width, y: 0, duration: 0)

let sequence = SKAction.sequence([move,reset])

let forever = SKAction.repeatForever(sequence)

background.run(forever)

复制代码地面

同背景的步骤一样

let move = SKAction.moveBy(x: -groundTexture.size().width, y: 0, duration: 5)

let reset = SKAction.moveBy(x: groundTexture.size().width, y: 0, duration: 0)

let sequence = SKAction.sequence([move,reset])

let forever = SKAction.repeatForever(sequence)

ground.run(forever)

复制代码石头

6.2秒内从屏幕右侧移动到屏幕左侧

移除

添加一个新方法循环添加新的石头

let endPosition = frame.width + topRock.size.width * 2

let moveAction = SKAction.moveBy(x: -endPosition, y: 0, duration: 6.2)

let sequence = SKAction.sequence([moveAction,SKAction.removeFromParent()])

topRock.run(sequence)

bottomRock.run(sequence)

rockCollision.run(sequence)

复制代码func startRocks() {

let createRocksAction = SKAction.run { [unowned self] in

self.createRocks()

}

let sequence = SKAction.sequence([createRocksAction,SKAction.wait(forDuration: 3)])

let repeatAction = SKAction.repeatForever(sequence)

run(repeatAction)

}

复制代码

效果:

3.添加 physicsBody

画面已经动起来了,接下来我希望我的飞机可以自由落体,然后可以和石头地面发生碰撞

///给飞机添加 physicsBody

player.physicsBody = SKPhysicsBody(texture: playerTexture, size: playerTexture.size())

player.physicsBody?.contactTestBitMask = player.physicsBody!.collisionBitMask

player.physicsBody?.isDynamic = true

///给地面添加 physicsBody

ground.physicsBody = SKPhysicsBody(texture: groundTexture, size: groundTexture.size())

ground.physicsBody?.isDynamic = false

///给石头添加 physicsBody

///上面的石头要在旋转之前添加 physicsBody 要让 physicsBody 跟着图形一起翻转过去

topRock.physicsBody = SKPhysicsBody(texture: rockTexture, size: rockTexture.size())

topRock.physicsBody?.isDynamic = false

bottomRock.physicsBody = SKPhysicsBody(texture: rockTexture, size: rockTexture.size())

bottomRock.physicsBody?.isDynamic = false

rockCollision.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 32, height: frame.height))

rockCollision.physicsBody?.isDynamic = false

复制代码

4.添加交互

飞机已经可以自由落体了,接下来实现点击屏幕时,飞机向上飞起,当碰撞到红色的区域时,获得得分.

在 didMove(to view: SKView) 方法中添加如下代码

physicsWorld.contactDelegate = self

复制代码

GameScene 添加 score 属性

var score = 0 {

didSet {

scoreLabel.text = "SCORE: \(score)"

}

}

复制代码

实现SKPhysicsContactDelegate协议的didBegin(_ contact: SKPhysicsContact) 方法,判断飞机碰撞到得分判定区来加分,实际上这段应该放到碰撞里面讲,放到这里是为了更好的看飞机交互的效果

extension GameScene: SKPhysicsContactDelegate {

func didBegin(_ contact: SKPhysicsContact) {

guard let nodeA = contact.bodyA.node,let nodeB = contact.bodyB.node else { return }

if nodeA.name == scoreDetect || nodeB.name == scoreDetect {

if nodeA == player {

nodeB.removeFromParent()

}else if nodeB == player {

nodeA.removeFromParent()

}

score += 1

return

}

}

}

复制代码

每次点击屏幕时 飞机施加向上冲力

override func touchesBegan(_ touches: Set, with event: UIEvent?) {

player.physicsBody?.velocity = CGVector.zero

player.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 20))

}

复制代码

为了飞机上升和下降的过程更真实,我们根据飞机 Y 方向的速度来调整飞机头的朝向

override func update(_ currentTime: TimeInterval) {

guard player != nil else { return }

let rotate = SKAction.rotate(toAngle: player.physicsBody!.velocity.dy * 0.001, duration: 1)

player.run(rotate)

}

复制代码

5.碰撞判断

飞机的交互已经完成,接下来实现各种物体的碰撞判断(得分判断在4.添加交互已经实现)

在didBegin(_ contact: SKPhysicsContact)方法中 已经判断的碰撞到得分点的情况,那么其他的情况就是碰到了地面或者上下的两个石头,添加如下代码,当碰到地面或者石头时,销毁飞机,并添加飞机位置添加爆炸特效,同时将 scene 的 speed 设置为0,画面就会停下了

guard let explosion = SKEmitterNode(fileNamed: R.file.playerExplosionSks.name) else {return}

explosion.position = player.position

addChild(explosion)

player.removeFromParent()

speed = 0

复制代码

效果:

6.添加音效

得分时爆炸时有音效,同时游戏还要有背景音.

GameScene 添加一个 audio 节点

var backgroundMusic: SKAudioNode!

在 didMove 方法中 添加如下代码

if let url = R.file.musicM4a() {

backgroundMusic = SKAudioNode(url: url)

addChild(backgroundMusic)

}

复制代码

爆炸和得分的音效代码加到相应的位置

///得分

let sound = SKAction.playSoundFileNamed(R.file.coinWav.name, waitForCompletion: false)

run(sound)

///爆炸

let sound = SKAction.playSoundFileNamed(R.file.explosionWav.name, waitForCompletion: false)

run(sound)

复制代码

7.完善游戏周期

现在游戏已经可以玩了,但是死亡后,没有办法重新开始,接下来,我们为游戏添加 logo 和 game over 和重新开始游戏的操作

声明一个 GameState 的枚举

enum GameState {

case showingLogo

case playing

case dead

}

复制代码

GameScene 添加一个 gameState 的属性,默认值为showingLogo

var gameState = GameState.showingLogo

添加 logo 和 gameOver 的节点属性

var logo: SKSpriteNode!

var gameOver: SKSpriteNode!

复制代码

生成 logo和 gameOver

func createLogo() {

logo = SKSpriteNode(imageNamed: R.image.logo.name)

logo.position = CGPoint(x: frame.midX, y: frame.midY)

addChild(logo)

gameOver = SKSpriteNode(imageNamed: R.image.gameover.name)

gameOver.position = CGPoint(x: frame.midX, y: frame.midY)

gameOver.alpha = 0

addChild(gameOver)

}

复制代码

修改touchesBegan(_ touches: Set, with event: UIEvent?)的逻辑

当游戏处于 showingLogo 状态点击屏幕执行隐藏 logo,恢复飞机的isDynamic属性,开始生成石头,将 gameState 改为 playing

处于playing状态时给飞机增加向上冲力

处于死亡状态时 重新生成 GameScene ,这样比把所有的节点恢复到初始状态要简单的多,重新生成 Scene

override func touchesBegan(_ touches: Set, with event: UIEvent?) {

switch gameState {

case .showingLogo:

gameState = .playing

let fadeOut = SKAction.fadeOut(withDuration: 0.5)

let wait = SKAction.wait(forDuration: 0.5)

let activePlayer = SKAction.run { [weak self] in

self?.player.physicsBody?.isDynamic = true

self?.startRocks()

}

let sequence = SKAction.sequence([fadeOut,wait,activePlayer,SKAction.removeFromParent()])

logo.run(sequence)

case .playing:

player.physicsBody?.velocity = CGVector.zero

player.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 20))

case .dead:

let scene = GameScene(fileNamed: R.file.gameSceneSks.name)!

let transition = SKTransition.moveIn(with: .left, duration: 1)

self.view?.presentScene(scene, transition: transition)

}

}

复制代码

碰撞到地面和石头时 显示 gameOver,同时gameState改为 dead

gameOver.alpha = 1

gameState = .dead

复制代码

最后,别忘更改了一些细节

createPlayer方法中player.physicsBody.isDynamic要改为 false

player.physicsBody?.isDynamic = false

didMove 方法中移出 startRocks()的调用,因为生成石头是在游戏开始后

createRock方法中,得分判断区的颜色要改为透明的

let rockCollision = SKSpriteNode(color: .clear, size: CGSize(width: 32, height: frame.height))

复制代码

回到GameViewController中

把这3项设为 false

view.showsFPS = false //是否显示 FPS

view.showsNodeCount = false//是否显示节点数量

view.showsPhysics = false /// 是否显示物理区域()

复制代码

这样 一个简单的游戏就完成了,接下来就可以 enjoy your game 了

html5实现像素鸟,[Swift]SpriteKit实现类似像素鸟的小游戏 - Crashy Plane相关推荐

  1. [SpriteKit] 系统框架中Cocos2d-x制作小游戏ZombieConga

    概述 使用SpriteKit实现一个简单的游戏, 通过一个游戏来进行SpriteKit的入门, 熟练2D游戏的API, 也可以更好的结合在iOS应用中. 详细 代码下载:http://www.demo ...

  2. 像素鸟多线程java_用java Swing做的小游戏像素鸟-Go语言中文社区

    最终效果 整个项目都是基于swing实现的.窗是口将图片加载到JPanel面板,然后将面板添加到到JFrame窗口实现显示. 这个类是选择几只像素鸟的类,也是main函数里执行的方法,代码有详细的注释 ...

  3. 类似密室逃脱的小游戏

    import sys import time strength = 10 like = 10 def getone() :     flage = True     while flage :    ...

  4. html微信小游戏,白鹭HTML5游戏转微信小游戏问题集锦,你关心的都在这里

    原标题:白鹭HTML5游戏转微信小游戏问题集锦,你关心的都在这里 首先,再次强调一些微信小游戏的基础技术限制: * 不允许操作 DOM.BOM.如果必须改成小游戏相应的 API 调用方式,目前引擎会自 ...

  5. Html5小游戏 转微信小程序,白鹭H5游戏转微信小游戏问题集锦,你关心的都在这里...

    首先,再次强调一些微信小游戏的基础技术限制: * 不允许操作 DOM.BOM.如果必须改成小游戏相应的 API 调用方式,目前引擎会自动引入weapp-adapter.js 文件做兼容处理. * 不允 ...

  6. 用html5做了一个打气球小游戏

    最近初试html5,用canvas和audio做了一个打气球小游戏. demo放在SAE,网址是:[url]http://auzll.sinaapp.com/balloon/[/url] 效果如下: ...

  7. 前端案例:像素鸟小游戏(js+dom操作,完整代码,附案例素材)

    目录 一.案例效果 二.实现思路 三.完整代码+详细注释 四.案例素材 一.案例效果 二.实现思路 创建游戏背景板和小鸟,并分别设置相对定位与绝对定位: 初始化背景图的位置: 初始化小鸟的位置: 设置 ...

  8. 【Pygamex小游戏】抗疫情,居家乐——在家无聊来玩儿像素鸟闯关大冒险吧,猜猜你能闯到多少关?

    前言 大家好,我是栗子君. 疫情期间,大家更要保持乐观积极的心态,不是吗? 所以小编今天给大家分享一个使用pygame代码制作的像素鸟小游戏! 期待疫情早日结束,杨帆起飞

  9. html像素小鸟小游戏,微信小游戏-像素鸟游戏

    we_flappybird ##注意: 微信小程序绘图API中drawImage不支持图片裁剪,其实微信是支持裁剪的只是接口没有实现,可以通过修改WAService.js使其支持裁剪. 修改如下: / ...

最新文章

  1. 用伪代码模拟洗衣机的运转流程
  2. javascript 设计模式(一)
  3. MS SQL入门基础:启动与关闭服务器
  4. 云服务器 ECS 建站教程:手工部署Java Web项目
  5. 牛客 - Gaming with Mia(dp)
  6. 进入Python世界——Python基础知识
  7. 栈应用_检测成对符号是否正确使用(代码、分析、汇编)
  8. (70)Verilog HDL测试激励:复位激励2
  9. mysql 安装后大_Window下MySql 5.6 安装后内存占用很高的问题
  10. 解决U盘无法格式化的问题
  11. localhost:3000 拒绝访问解决办法
  12. bootstrap批量删除操作!
  13. linux进程僵尸问题的原因,Linux僵尸进程产生原因及避免方法
  14. 卷积神经网络中的“池化层”
  15. 我的大一C++学习笔记
  16. Data Matrix码
  17. 【十三香吗?】网易严选-苹果12商品评论数据可视化分析
  18. 离散型最值的期望计算
  19. 免费的在线白板协作工具有哪些?
  20. 如何将 Visual Paradigm 桌面客户端连接到不同的 VP Online 存储库丨使用教程

热门文章

  1. iOS 用内置浏览器Safari 打开网页
  2. mre下的控件实现(三、Button实现)
  3. 个人微信开发API协议
  4. listview嵌套listview
  5. PTA 7-119 计算阶乘和
  6. java实验八 内部类,匿名类
  7. 项目实训(三)unity游戏场景的搭建
  8. C语言 格式输入输出与字符输入输出
  9. UVa 10943 How do you add? (组合数学)
  10. 期货月收益率(期货收益率计算公式)