SpriteKit框架详细解析(三) —— 创建一个简单的2D游戏(一)

版本记录

版本号 时间
V1.0 2017.08.12

前言

SpriteKit框架使用优化的动画系统,物理模拟和事件处理支持创建基于2D精灵的游戏。接下来这几篇我们就详细的解析一下这个框架。相关代码已经传至GitHub - 刀客传奇,感兴趣的可以阅读另外几篇文章。
1. SpriteKit框架详细解析(一) —— 基本概览(一)
2. SpriteKit框架详细解析(二) —— 一个简单的动画实例(一)

SpriteKit vs. Unity

首先需要说明:

说明:本文是在Swift 4, iOS 11, Xcode 9环境和条件下开发的。

SpriteKit 和 Swift 结合在一起还是很有意思的。

  • SpriteKit:SpriteKit是在iOS上制作游戏的最佳方式之一。 它易于学习,功能强大,并得到Apple的全力支持。
  • Swift:Swift是一种易于使用的语言,特别是如果您是iOS平台的初学者。

在本文中,您将学习如何使用Apple的2D游戏框架SpriteKit创建一个简单的2D游戏 - 使用Swift!

目前SpriteKit最受欢迎的替代方案是一个名为Unity的游戏框架。 Unity最初是作为3D引擎开发的,但它也具有完全内置的2D支持。

在开始之前,先考虑一下SpriteKit或Unity是否是游戏的最佳选择。

1. SpriteKit的优点

  • It’s built right into iOS - 它内置于iOS中。无需下载额外的库或具有外部依赖性。您还可以无缝地使用其他iOS API,如iAd,应用程序内购买等,而无需依赖额外的插件。

  • It leverages your existing skills - 它利用您现有的技能。如果你已经了解Swift和iOS开发,你可以非常快速地获取SpriteKit。

  • It’s written by Apple - 它是由Apple编写的。这让您有信心在Apple的所有新产品上都能得到很好的支持。例如,您可以使用相同的SpriteKit代码使您的游戏在iOS,macOS和tvOS上顺利运行。

  • It’s free - 免费。也许是最佳理由之一!您可以免费获得SpriteKit的所有功能。 Unity确实有免费版本,但它没有Pro版本的所有功能。例如,如果要避免使用Unity启动画面,则需要升级。

2. Unity的优势

  • Cross-platform - 跨平台。 这是最重要的一个。 如果您使用SpriteKit,您将被锁定在Apple生态系统中。 使用Unity,您可以轻松地将游戏移植到Android,Windows等。

  • Visual scene designer - 视觉场景设计师。 通过单击按钮,Unity可以非常轻松地布置您的关卡并实时测试您的游戏。 SpriteKit确实有一个场景编辑器,但与Unity提供的相比,它是非常基础的。

  • Asset store -资源商店。 Unity附带内置资源商店,您可以在其中为游戏购买各种组件。 其中一些组件可以为您节省大量的开发时间!

  • More powerful - 更加强大。 通常,Unity具有比SpriteKit / Scene Kit组合更多的特性和功能。


Which Should I Choose? - 应该如何选择

现在你可能会想,“好吧,我应该选择哪个2D框架?”

答案取决于您的目标:

  • 如果您是一个完全的初学者,或者只关注Apple生态系统:使用SpriteKit - 它内置,易于学习,并且可以完成工作。
  • 如果你想要跨平台,或者有一个更复杂的游戏:使用Unity - 它更强大,更灵活。

这里我们选择SpriteKit,所以请继续阅读以开始使用此SpriteKit教程!


Getting Started - 正式进入正题

我们新建立一个类名称为GameViewController.swift,然后在viewDidLoad()最后加入下面代码:

let scene = GameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .resizeFill
skView.presentScene(scene)

GameViewController是一个普通的UIViewController,它的根视图是一个SKView,它是一个包含SpriteKit场景的视图。

在这里,您已经指示viewDidLoad()在启动时创建GameScene的新实例,其大小与视图本身相同。 将skView.showsFPS设置为true表示将显示帧速率指示符。

这是初始设置,现在是时候在屏幕上获取一些东西了!


Adding a Sprite - 添加一个Spirit

打开GameScene.swift,并在GameScene中添加如下代码:

// 1
let player = SKSpriteNode(imageNamed: "player")
  
override func didMove(to view: SKView) {
  // 2
  backgroundColor = SKColor.white
  // 3
  player.position = CGPoint(x: size.width * 0.1, y: size.height * 0.5)
  // 4
  addChild(player)
}

这就是它的作用,一步一步:

  • 1)在这里你为玩家声明一个私有常量(即忍者),这是精灵的一个例子。 如您所见,创建精灵很简单 - 只需传入要使用的图像名称即可。
  • 2)在SpriteKit中设置场景的背景颜色就像设置backgroundColor属性一样简单。 在这里你把它设置为白色。
  • 3)将精灵定位在水平方向上10%,并垂直方向居中。
  • 4)要使精灵出现在场景中,必须将其添加为场景的子画面。

Build并运行,效果如下所示:

SpriteKit框架详细解析(三) —— 创建一个简单的2D游戏(一)_第1张图片

Moving Monsters - 移动怪物

接下来,你想在场景中添加一些怪物,让你的忍者进行战斗。 为了让事情变得更有趣,你希望怪物能够移动 - 否则就不会有太大的挑战! 您将在屏幕右侧略微创建怪物并设置一个动作,告诉他们向左移动。

将以下方法添加到类中的GameScene.swift,在结束大括号之前:

func random() -> CGFloat {
  return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}

func random(min: CGFloat, max: CGFloat) -> CGFloat {
  return random() * (max - min) + min
}

func addMonster() {
  
  // Create sprite
  let monster = SKSpriteNode(imageNamed: "monster")
  
  // Determine where to spawn the monster along the Y axis
  let actualY = random(min: monster.size.height/2, max: size.height - monster.size.height/2)
  
  // Position the monster slightly off-screen along the right edge,
  // and along a random position along the Y axis as calculated above
  monster.position = CGPoint(x: size.width + monster.size.width/2, y: actualY)
  
  // Add the monster to the scene
  addChild(monster)
  
  // Determine speed of the monster
  let actualDuration = random(min: CGFloat(2.0), max: CGFloat(4.0))
  
  // Create the actions
  let actionMove = SKAction.move(to: CGPoint(x: -monster.size.width/2, y: actualY),
                                 duration: TimeInterval(actualDuration))
  let actionMoveDone = SKAction.removeFromParent()
  monster.run(SKAction.sequence([actionMove, actionMoveDone]))
}

addMonster()的第一部分应该基于你到目前为止学到的东西是有意义的:你做一些简单的计算来确定你想要创建对象的位置,设置对象的位置,并将它添加到场景中。

这里的新元素是添加动作。 SpriteKit提供了许多非常有用的内置操作,可帮助您轻松地随时间更改精灵的状态,例如移动操作,旋转操作,淡入淡出操作,动画操作等。在这里你对怪物使用三个动作:

  • SKAction.move(to:duration :):您可以使用此操作使对象在屏幕外移动到左侧。您可以指定运动需要多长时间,此处您可以在2-4秒内随机改变持续时间。
  • SKAction.removeFromParent():SpriteKit附带了一个有用的操作,可以从其父节点中删除节点,从而有效地将其从场景中删除。在这里,您可以使用此操作从不再可见的场景中移除怪物。这很重要,否则你将拥有无穷无尽的怪物供应,并最终消耗所有设备资源。
  • SKAction.sequence(_ :):序列操作允许您将按顺序执行的一系列操作链接在一起,一次一个。这样,您可以先执行move to操作,一旦完成,您将执行remove from parent操作。

注意:此代码块包含一些辅助方法,可使用arc4random()在一个范围内生成随机数。 这足以满足此游戏中简单的随机数生成需求,但如果您需要更高级的功能,请查看GameplayKit中的随机数API。

继续前行之前的最后一件事。 你需要实际调用方法来创建怪物! 为了让事情变得有趣,让我们随着时间的推移不断产生怪物。

只需将以下代码添加到didMove(to :)的末尾:

run(SKAction.repeatForever(
      SKAction.sequence([
        SKAction.run(addMonster),
        SKAction.wait(forDuration: 1.0)
        ])
    ))

在这里,您运行一系列操作来调用代码块,然后等待1秒钟。 你无休止地重复这一系列的动作。

Build并运行项目; 现在你应该看到怪物愉快地在屏幕上移动:

SpriteKit框架详细解析(三) —— 创建一个简单的2D游戏(一)_第2张图片

Shooting Projectiles - 射击弹丸

在这一点上,忍者只是有一些行动 - 所以是时候添加射击了! 有许多方法可以实现拍摄,但是对于这个游戏,你要做到这一点,当用户点击屏幕时,一个射弹从玩家向点击的方向射击。

你需要使用move to动作来实现这一点,但为了使用它你必须做一些数学运算。move to动作需要您为射弹提供目的地,但您不能仅使用触摸点,因为触摸点代表相对于玩家射击的方向。 你实际上想要保持射弹穿过触摸点直到它离开屏幕。

这是一张说明此事的图片:

SpriteKit框架详细解析(三) —— 创建一个简单的2D游戏(一)_第3张图片

如您所见,您有一个由原点到触点的x和y偏移创建的小三角形。 你只需要制作一个具有相同比例的大三角形 - 而且你知道你希望其中一个端点离开屏幕。

要运行这些计算,如果您有一些基本矢量数学例程(比如添加和减去矢量的方法),这确实很有帮助。 但是,SpriteKit默认没有,所以你必须自己编写。

幸运的是,由于Swift运算符重载的强大功能,它们非常容易编写。 在GameScene类之前,将这些函数添加到文件的顶部:

func +(left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x + right.x, y: left.y + right.y)
}

func -(left: CGPoint, right: CGPoint) -> CGPoint {
  return CGPoint(x: left.x - right.x, y: left.y - right.y)
}

func *(point: CGPoint, scalar: CGFloat) -> CGPoint {
  return CGPoint(x: point.x * scalar, y: point.y * scalar)
}

func /(point: CGPoint, scalar: CGFloat) -> CGPoint {
  return CGPoint(x: point.x / scalar, y: point.y / scalar)
}

#if !(arch(x86_64) || arch(arm64))
  func sqrt(a: CGFloat) -> CGFloat {
    return CGFloat(sqrtf(Float(a)))
  }
#endif

extension CGPoint {
  func length() -> CGFloat {
    return sqrt(x*x + y*y)
  }
  
  func normalized() -> CGPoint {
    return self / length()
  }
}

这些是一些矢量数学函数的标准实现。 如果您对此处发生的事情感到困惑或对矢量数学不熟悉,请查看此vector math explanation。

接下来,在关闭大括号之前,再向GameScene类添加一个新方法:

override func touchesEnded(_ touches: Set, with event: UIEvent?) {
  // 1 - Choose one of the touches to work with
  guard let touch = touches.first else {
    return
  }
  let touchLocation = touch.location(in: self)
  
  // 2 - Set up initial location of projectile
  let projectile = SKSpriteNode(imageNamed: "projectile")
  projectile.position = player.position
  
  // 3 - Determine offset of location to projectile
  let offset = touchLocation - projectile.position
  
  // 4 - Bail out if you are shooting down or backwards
  if offset.x < 0 { return }
  
  // 5 - OK to add now - you've double checked position
  addChild(projectile)
  
  // 6 - Get the direction of where to shoot
  let direction = offset.normalized()
  
  // 7 - Make it shoot far enough to be guaranteed off screen
  let shootAmount = direction * 1000
  
  // 8 - Add the shoot amount to the current position
  let realDest = shootAmount + projectile.position
  
  // 9 - Create the actions
  let actionMove = SKAction.move(to: realDest, duration: 2.0)
  let actionMoveDone = SKAction.removeFromParent()
  projectile.run(SKAction.sequence([actionMove, actionMoveDone]))
}

这里做了很多事情,一步一步看一下。

  • 1)关于SpriteKit的一个很酷的事情是它在UITouch上包含一个带有location(in :)previousLocation(in :)方法的类别。这些可以让您在SKNode的坐标系中找到触摸的坐标。在这种情况下,您可以使用它来找出触摸在场景坐标系中的位置。
  • 2)然后你创建一个射弹并将其放置在玩家开始的位置。请注意,你还没有把它添加到场景中,因为你必须先做一些检查 - 这个游戏不允许忍者向后射击。
  • 3)然后从触摸位置减去射弹的当前位置,以获得从当前位置到触摸位置的矢量。
  • 4)如果X值小于0,则表示玩家正在尝试向后射击。这个游戏是不允许的(真正的忍者不回头!),所以return。
  • 5)否则,可以将射弹添加到场景中。
  • 6)通过调用normalized()将偏移量转换为单位向量(长度为1)。这将使得在同一方向上制作具有固定长度的矢量变得容易,因为1 * length = length
  • 7)将单位矢量乘以您要设计的方向1000,为什么是1000?它肯定足够长,可以超越屏幕边缘。 :]
  • 8)将射击量添加到当前位置,以获得最终应该在屏幕上显示的位置。
  • 9)最后,创建move(to:,duration :)removeFromParent()动作,就像你之前为怪物做的那样。

Build并运行。现在,你的忍者应该能够对即将到来的怪物开火!

SpriteKit框架详细解析(三) —— 创建一个简单的2D游戏(一)_第4张图片

后记

本篇主要讲述了创建一个简单的2D游戏,感兴趣的给个赞或者关注~~~

SpriteKit框架详细解析(三) —— 创建一个简单的2D游戏(一)_第5张图片

你可能感兴趣的:(SpriteKit框架详细解析(三) —— 创建一个简单的2D游戏(一))