热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpriteKit从零开始~ConstraintsandActions

介绍继上一篇SpriteKit基础,这一篇将学习constraints和actions。这些功能用于在SpriteKit中为游戏添加动作和动画,同时限

介绍

继上一篇SpriteKit基础,这一篇将学习constraints和actions。这些功能用于在SpriteKit中为游戏添加动作和动画,同时限制游戏中节点的位置和方向。

您可以使用在本系列的第一个教程中创建的项目,或者在GitHub上下载新副本。


1.自定义Node和Scene

在向Scene中添加constraints和actions之前,我们首先需要创建一些类,以便我们代码中可以使用Node。根据iOS> Source> Cocoa Touch Class模板创建一个新类PlayerNode, 并确保它是SKSpriteNode的子类。

PlayerNode类

如果Xcode在创建类后引发错误,请在SpriteKit框架下的语句下面添加导入语句import UIKit

import UIKit
import SpriteKit

然后在PlayerNode类中声明以下三个属性。这些属性将用于限制汽车水平运动。

import UIKit
import SpriteKitenum ButtonDirection:Int {case Left = 1case Right = 2
}class PlayerNode: SKSpriteNode {var leftConstraint: SKConstraint!var middleConstraint: SKConstraint!var rightConstraint: SKConstraint!}

创建另一个Cocoa Touch类,并将其称为MainScene,使其成为SKScene的子类。

MainScene类

在顶部,为SpriteKit框架添加import语句。

import UIKit
import SpriteKit

创建这些类后,打开MainScene.sks,单击灰色背景并选择场景,打开右侧的Custom Class Inspector并将MainScene分配给Custom Class

选中汽车,并按照与场景相同的方式将PlayerNode类设置为该汽车。最后,在仍然选择汽车的情况下,打开“属性”检查器,然后将“名称”更改为Player

玩家精灵名称

现在我们已经设置了基本类,我们可以从在代码中创建一些限制开始。


 


2.Constraints

SpriteKit中的约束由该类表示,SKConstraint 用于限制特定节点的位置和方向。使用Constraints可以实现多种限制,例如,相对于scene或者相对其他nodes。限制除了适用于恒定值之外,还适用于值范围,以便可以将场景中的子画面固定在特定位置或允许它们在特定区域内移动。

PlayerNode中新增加的3个约束属性,这些约束将用于将汽车锁定在游戏的三个轨道上。

打开MainScene.swift并为type player创建一个属性PlayerNode!。此属性将存储PlayerNode的引用。

import UIKit
import SpriteKitclass MainScene: SKScene {var player: PlayerNode!}

接下来,我们重写MainScene的didMove(_:)方法:

override func didMove(to view: SKView) {super.didMove(to: view)let size = view.frame.sizeif let foundPlayer = childNode(withName: "Player") as? PlayerNode {player = foundPlayer}let center = size.width * 0.5, difference = CGFloat(70.0)player.leftCOnstraint= SKConstraint.positionX(SKRange(constantValue: center - difference))player.middleCOnstraint= SKConstraint.positionX(SKRange(constantValue: center))player.rightCOnstraint= SKConstraint.positionX(SKRange(constantValue: center + difference))player.leftConstraint.enabled = falseplayer.rightConstraint.enabled = falseplayer.cOnstraints= [player.leftConstraint, player.middleConstraint, player.rightConstraint]}

让我们逐步看一下代码。每当通过view呈现Scene时didMove(_:)方法都会被调用。当调用完super class的didMove(_:)后,我们将场景调整为与当前视图相同的大小。这样可以确保场景始终正确扩展到当前设备的屏幕尺寸和scales属性。

使用childNode(withName:)查找MainScene.sks中添加的名称为player的sprite。然后,我们将此值分配给属性player

在计算了场景的中心并指定了以外的约束之后70.0,我们创建了精灵约束。使用的SKConstraint的类方法positionX(_:),我们为玩家的精灵左,中,右的约束。此方法需要一个实例SKRange作为参数,在我们的情况下,该实例是一个具有恒定值的范围。如果要查看SpriteKit中可能的约束和范围,建议您参考类SKRangee的引用SKConstraint

我们禁用左右限制,因为我们不希望它们在游戏开始时起作用。最后,我们将这些约束分配给playerconstraints属性。

在任何模拟器或真机上编译并运行游戏。现在,您可以看到场景的大小正确,汽车位于底部的中心。

场景中居中的汽车

您可以看到,一旦我们向游戏中添加了一些运动,汽车就可以被限制在场景的水平中心,并且可以被限制在左右车道上。


 


2.Actions

SpriteKit动作由功能强大的类表示SKAction。动作使我们可以轻松地在场景中设置动画和移动精灵。它们由我们执行并由SpriteKit API计算得出,并与物理限制和模拟一起工作。

除了指定动作的作用之外,您还可以配置和编程动作的工作方式。例如,您可以暂停和继续操作或为该操作配置减弱的行为。这提供了更大程度的控制,因此您可以轻松地加快或减慢某些动作以产生一些有趣的游戏元素。

与nodes可以具有child nodes的方式相同,有三种Actions可以具有child Actions:


  • sequence actions,一组actions,一个接着一个执行
  • group actions,一组actions,同时执行
  • repeating actions,重复执行一个action,可以指定执行次数或者无限次

您可以以编程方式或在上一教程中使用的Xcode场景编辑器中创建动作。在本教程中,我们将同时使用这两种技术。

打开MainScene.sks并单击场景左下角“Animate”按钮旁边的图标以打开“Action Editor View”

Open Action Editor View

Action Editor View

然后,导航到右侧“对象库”的末尾并找到移动操作Move Action项。单击并将其拖动到“动作编辑器视图”时间轴,并将其放置在左下角,如下所示。

向时间轴添加动作

这将使动作从0:00开始播放,即在场景出现时开始播放。如果将其放在其他位置,则该操作将在时间轴顶部显示的时间间隔之后开始执行。

鼠标右键该动作,然后单击左下角的小箭头图标。将出现一个弹出窗口,单击左侧的无限按钮。这会使动作永远重复下去。

永远重复动作

 

在仍然选择动作的情况下,打开右侧的“属性”检查器,并将“ Y偏移”值更改为100

其他属性设定方面,如Start Time,该车将立即启动动画(开始时间),每1秒(持续时间),它会在X方向不移动0,而在Y方向移动100。 Timeing Funcation属性可用于逐渐开始和/或停止动作。在这种情况下,我们使用Linear,这意味着汽车始终以相同的速度行驶。

最后,测试动作,单击场景编辑器左下方的“Animate按钮。底部的工具栏将变为蓝色,汽车将开始向上移动。

Xcode场景编辑器中的操作

有了动作动作之后,就该以编程方式创建水平动作了。在此之前,我们需要添加一些逻辑,以便游戏按钮可以控制汽车。

通过选择iOS> Source> Swift File模板创建一个新文件,并将其命名为LaneStateMachine

迅捷文件模板

将以下代码添加到新文件中:

import GameplayKitclass LaneStateMachine: GKStateMachine {}class LaneState: GKState {var playerNode: PlayerNodeinit(player: PlayerNode) {playerNode = player}
}class LeftLane: LaneState {override func isValidNextState(_ stateClass: AnyClass) -> Bool {if stateClass == MiddleLane.self {return true}return false}override func didEnter(from previousState: GKState?) {playerNode.moveInDirection(direction: .Left, toLane: self)}
}class MiddleLane: LaneState {override func isValidNextState(_ stateClass: AnyClass) -> Bool {if stateClass == LeftLane.self || stateClass == RightLane.self {return true}return false}override func didEnter(from previousState: GKState?) {if previousState is LeftLane {playerNode.moveInDirection(direction: .Right, toLane: self)} else if previousState is RightLane {playerNode.moveInDirection(direction: .Left, toLane: self)}}
}class RightLane: LaneState {override func isValidNextState(_ stateClass: AnyClass) -> Bool {if stateClass == MiddleLane.self {return true}return false}override func didEnter(from previousState: GKState?) {playerNode.moveInDirection(direction: .Right, toLane: self)}
}

所有这些代码所做的就是使用新的GameplayKit框架创建一个状态机,该状态机表示游戏中三个轨迹以及它们之间的运动。如果您想更好地了解这段代码在做什么,请查看我的GameplayKit教程。

然后打开PlayerNode.swift并将以下两个方法添加到类中PlayerNode

func disableAllConstraints() {leftConstraint.enabled = falsemiddleConstraint.enabled = falserightConstraint.enabled = false}func moveInDirection(direction: ButtonDirection, toLane lane: LaneState) {disableAllConstraints()let changeInX = (direction == .Left) ? -70.0 : 70.0let rotation = (direction == .Left) ? Double.pi/4 : -Double.pi/4let duration = 0.5let moveAction = SKAction.moveBy(x: CGFloat(changeInX), y: 0.0, duration: duration)let rotateAction = SKAction.rotate(byAngle: CGFloat(rotation), duration: duration/2)rotateAction.timingMode = .easeInEaseOutlet rotateSequence = SKAction.sequence([rotateAction, rotateAction.reversed()])let moveGroup = SKAction.group([moveAction, rotateSequence])let completion = SKAction.run { () -> Void inswitch lane {case is LeftLane:self.leftConstraint.enabled = truecase is MiddleLane:self.middleConstraint.enabled = truecase is RightLane:self.rightConstraint.enabled = truedefault:break}}let sequenceAction = SKAction.sequence([moveGroup, completion])run(sequenceAction)}

该方法disableAllConstraints()是用于禁用player node上的限制的便捷方法。

在中moveInDirection(_:toLane:),我们确定汽车水平移动的方向,向左移动时为-70.0,向右移动时为+70.0。然后,我们计算出正确的角度(以弧度为单位)以使汽车在运动中旋转。注意,正数表示逆时针旋转。

指定持续时间后,我们使用classmoveByX(_:duration:)和method创建运动和旋转动作rotateByAngle(_:duration)。我们创建了一个旋转序列,以将汽车旋转回运动之前的状态。该方法会reversedAction()自动为您创建操作的逆过程。

然后,我们创建一组动作动作以执行水平移动并同时旋转。最后,我们创建一个补充动作以在执行时执行闭包。在此关闭中,我们确定汽车在哪个车道上并为该车道激活正确的限制。

打开GameViewController.swift并向类添加stateMachine类型LaneStateMachine!的属性ViewController

替换方法 viewDidLoad() 和 didPressButton(_:)类的实现GameViewController.,如下所示:

var stateMachine: LaneStateMachine!override func viewDidLoad() {super.viewDidLoad()let skView = SKView(frame: self.view.frame)let scene:MainScene = SKScene(fileNamed: "MainScene") as! MainSceneskView.presentScene(scene)view.insertSubview(skView, at: 0)let left = LeftLane(player: scene.player)let middle = MiddleLane(player: scene.player)let right = RightLane(player: scene.player)stateMachine = LaneStateMachine(states: [left, middle, right])stateMachine.enter(MiddleLane.self)}@IBAction func didPressButton(sender: UIButton) {switch sender.tag {case ButtonDirection.Left.rawValue:switch stateMachine.currentState {case is RightLane:stateMachine.enter(MiddleLane.self)case is MiddleLane:stateMachine.enter(LeftLane.self)default:break}case ButtonDirection.Right.rawValue:switch stateMachine.currentState {case is LeftLane:stateMachine.enter(MiddleLane.self)case is MiddleLane:stateMachine.enter(RightLane.self)default:break}default:break}}

在中viewDidLoad(),我们将SKView插入到索引0处添加对象,以便控制按钮可见,并且还初始化状态机。

didPressButton(_:),我们会根据按钮标签确定用户按下的按钮,并输入汽车所在的正确车道。

编译并运行游戏。同时按下屏幕底部的向左和向右按钮以使汽车行驶。您可以看到汽车转弯并朝所按按钮的方向移动。

转弯和移动车

请注意,按钮图标可能不兼容,如下所示。

不匹配的按钮

要更正此问题,请打开资产目录 (Image.xcassets),并为两个图像(向左箭头向右箭头)将“渲染模式”更改为“原始图像”

原始图像渲染模式


结论

现在,您应该有信心使用SpriteKit的限制和操作。如您所见,这些框架功能使在SpriteKit上向游戏添加动画和动作非常容易。

在本系列的下一个教程中,我们将看一下SpriteKit的相机节点,这样我们的汽车就不会出现在屏幕顶部。之后,我们将深入研究SpriteKit的物理仿真系统,重点关注物理物体和碰撞检测。

与往常一样,在下面的评论中留下您的评论和反馈。

 

 

 


推荐阅读
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 本文比较了eBPF和WebAssembly作为云原生VM的特点和应用领域。eBPF作为运行在Linux内核中的轻量级代码执行沙箱,适用于网络或安全相关的任务;而WebAssembly作为图灵完备的语言,在商业应用中具有优势。同时,介绍了WebAssembly在Linux内核中运行的尝试以及基于LLVM的云原生WebAssembly编译器WasmEdge Runtime的案例,展示了WebAssembly作为原生应用程序的潜力。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
author-avatar
-彼岸花开-hui
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有