在本节中,我们将了解GameplayKit状态机,然后我们将让我们的玩家跳转并给他一些其他动画,所以扣紧并为这一知识的骑行做好准备。
下载PlayerState Machine 玩家状态机
要学习本教程,您将需要Xcode 9,您可以下载最终项目,以帮助您与自己的进度进行比较。
GameplayKit状态机
首先,我们需要了解玩家的所有不同状态,因为我们将把它们应用到我们的游戏中。
playerStateMachineLandingState
LandingState是玩家接触地面的时间。
IdleState
IdleState是玩家静止时的动画。
WalkingState
WalkingState是玩家走路时的动画。
JumpingState
JumpingState是玩家跳跃和动作的动画。
StunnedState
最后,StunnedState是玩家被陷阱或敌人触碰时的动画。
状态
正如您在上图中所注意到的那样,所有状态都是相互连接的,这意味着所有状态都以不同的方式相关。
建立
让我们创建一个新的Swift文件,你可以按Command和N来创建新文件。将出现一个新窗口,确保您在iOS平台上并选择Swift File模板并单击Next。然后,我们将被要求命名该文件。我们将其称为“playerStateMachine”,我们将其保存在我们的初始文件夹ElonGame中并点击Create。
GameplayKit
现在,我们需要研究游戏的逻辑,所以我们需要调用GameplayKit框架。GameplayKit与游戏的逻辑更相关,因为SpriteKit用于游戏的视觉部分。让我们将GameplayKit导入我们的新文档。
为玩家状态导入玩家动画和类
接下来,我们将调用所有玩家动画,稍后,我们将为PlayerState创建一个类。在能够接收状态之前必须初始化玩家。我们将添加的所有代码将在导入GameplayKit后立即生效。
我们正在使用名称characterAnimationKey重新组合所有动画。在PlayerState类中,我们将playerNode初始化为SKNode,并使他能够接收动画和动作状态。这些状态仅适用于playerNode。
跳跃状态 Jumping State 类
我们将添加一个跳跃状态类JumpingState来管理跳跃动作。在这个类中,我们需要创建两个函数。第一个isValidNextState是一个指示器,它将告诉我们当前状态是否允许转换到下一个状态。我们现在将默认返回值设置为true。第二个函数didEnter将帮助我们在玩家进入跳跃状态时执行一些动作。当他这么做的时候,我们正在给他施加75牛顿的重力,持续时间为0.1秒。此持续时间决定了玩家跳跃的速度。显然,在跳转期间,hasFinishedJumping的值设置为false。最后,我们将添加一个Timer这将有助于我们限制跳跃动作,主要是阻止玩家同时跳跃多次,试图像Flappy Bird一样在空中飞行。这将确保初始跳跃已完成,然后再次重复。一旦玩家登陆,hasFinishedJumping就变为真实。
游戏场景设置
我们需要为Player State定义一些变量,让我们在Sprite Engine之后添加声明。
didMove设置
在didMove方法内部,在joystickKnob = joystick?.childNode(withName:“knob”)之后,让我们添加将保持玩家不同状态的playerStateMachine数组。
touchesBegan
让我们去touchesBegan并在的大括号内!如果!(操纵杆?.contains(位置))!,让我们开始跳跃动作。
现在,让我们运行模拟器。除了使用操纵杆左右控制玩家之外,如果点击屏幕,玩家会通过跳跃进行响应。
玩家状态
让我们回到playerStateMachine.swift和文档的底部,让我们创建更多班的其余State我们的玩家。
着陆状态类
让我们为着陆状态创建一个新类。在这个类中,我们将添加相同的** isValidNextState 函数作为跳转类。但是,我们将使用Switch**语句作为控制流。它类似于if语句,除了它运行某个代码块,具体取决于匹配的多个值而不是true或false。我们在跳转状态类中选择了Switch语句而不是if语句,因为稍后我们会添加更多的情况。在这个课程中,我们正在检查玩家是处于着陆状态还是处于跳跃状态。如果任一条件为假,则不要进入空闲状态。
class LandingState : PlayerState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
switch stateClass {
case is LandingState.Type, is JumpingState.Type: return false
default: return true
}
}
override func didEnter(from previousState: GKState?) {
stateMachine?.enter(IdleState.self)
}
}
空闲状态类
与LandingState类一样,我们需要为IdleState创建一个类,并再次验证我们是否可以使用** isValidNextState 函数进入下一个状态。然后,我们将使用表示可应用于节点的图像的SKTexture对象声明变量纹理。作为参数,我们将应用图像玩家/ 0以使玩家在空闲状态期间保持静止。最后,我们将声明一个操作,将图像附加到我们之前选择的玩家。的动作变量被存储为懒惰避免被RAN直到必要被称为首次这是在当didEnter**功能,以及删除任何以前的动画后的功能。使用惰性属性进行声明的目的是节省处理时间并优化内存。
class IdleState : PlayerState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
switch stateClass {
case is LandingState.Type, is IdleState.Type: return false
default: return true
}
}
let textures = SKTexture(imageNamed: "player/0")
lazy var action = { SKAction.animate(with: [textures], timePerFrame: 0.1)} ()
override func didEnter(from previousState: GKState?) {
playerNode.removeAction(forKey: characterAnimationKey)
playerNode.run(action, withKey: characterAnimationKey)
}
}
行走状态类
现在,让我们定义walkingState类。其中的代码与IdleState非常相似。然而,纹理变量是一个数组,其中包含我们玩家的不同帧,因此当他走路时,他的动画就好像他的腿和手臂在现实生活中一样移动。这个动作一直在运行,直到我们另一个状态中断行走。
class WalkingState : PlayerState {
override func isValidNextState(_ stateClass: AnyClass) -> Bool {
switch stateClass {
case is LandingState.Type, is WalkingState.Type : return false
default: return true
}
}
let textures : Array<SKTexture> = (0..<6).map({ return "player/\($0)"}).map(SKTexture.init)
lazy var action = { SKAction.repeatForever(.animate(with: textures, timePerFrame: 0.1))} ()
override func didEnter(from previousState: GKState?) {
playerNode.removeAction(forKey: characterAnimationKey)
playerNode.run(action, withKey: characterAnimationKey)
}
}
眩晕状态类
我们将首先声明** StunnedState**类,但暂时将其留空。我们将在后面的部分完成。
class StunnedState : PlayerState {
}
约束跳跃
让我们回到JumpingState类来限制跳跃,这样当我们的玩家完成跳跃并降落在地面上时,他才能再次跳跃。在函数isValidNextState内部和返回true之前,添加以下条件。此外,让我们改变返回true,以返回false**。
if hasFinishedJumping && stateClass is LandingState.Type { return true }
return false
跳跃动画
在isValidNextState函数的正下方,让我们将变量纹理声明为数组类型,用于存储跳跃动画的图像。同样,我们将运行一个动作来使用这些图像为玩家设置动画,就像在行走动画中一样。
let textures : Array<SKTexture> = (0..<2).map({ return "jump/\($0)"}).map(SKTexture.init)
lazy var action = { SKAction.animate(with: textures, timePerFrame: 0.1)} ()
删除并运行操作
在下面的didEnter函数中,让我们在** hasFinishedJumping = false **上方添加remove和run操作。
playerNode.removeAction(forKey: characterAnimationKey)
playerNode.run(action, withKey: characterAnimationKey)
完成State
让我们回到GameScene.swift文档,然后查找didMove函数。在JumpingState(playerNode:player!),之后,让我们在数组** playerStateMachine中添加其余的玩家状态**。
WalkingState(playerNode: player!),
IdleState(playerNode: player!),
LandingState(playerNode: player!),
StunnedState(playerNode: player!),
默认玩家为空闲状态
现在,我们需要在游戏开始时将玩家默认为处于空闲状态。在playerStateMachine = GKStateMachine之后,添加这行代码。
playerStateMachine.enter(IdleState.self)
positivePosition
现在我们已经设置了所有玩家状态,让玩家在用户移动旋钮时进入行走状态。为此,请转到Game Loop标记下的更新功能,并在声明xPosition 之后,让我们创建一个名为 positivePosition的新变量。这将为旋钮的x位置存储正值。
let positivePosition = xPosition < 0 ? -xPosition : xPosition
if floor(positivePosition) != 0 {
playerStateMachine.enter(WalkingState.self)
} else {
playerStateMachine.enter(IdleState.self)
}
当我们声明positivePosition时,我们正在测试xPosition是否为负数。如果这是真的,那就让它变得积极。然后,我们使用floor函数将该值四舍五入为最接近的整数。如果最终结果不为0,表示旋钮不在操纵杆的中心,请让玩家走动动画。否则,让他进入空闲状态。
设置行走状态
如果您运行应用程序并点击屏幕,您将看到当我们的玩家跳跃时,他会进入跳跃动画。然而,即使他登陆后,他仍然处于跳跃状态。为了解决这个问题,我们需要修改行走状态,原因是我们还没有应用与地面的碰撞。让我们回到playerStateMachine.swift文件,并在JumpingState类,注释掉这种情况下 ,如果hasFinishedJumping && stateClass是LandingState.Type {返回true} 。同样,让我们改变返回false以返回true。由于我们尚未应用碰撞,因此行走和跳跃状态现在发生冲突。
// if hasFinishedJumping && stateClass is LandingState.Type { return true }
return true
现在再次运行模拟器并执行跳转。这次,当我们左右移动操纵杆时,我们的玩家实际上正在行走。此外,由于我们刚刚删除约束,他可以连续跳转我们垃圾邮件的次数。如果我们放开旋钮,玩家将停止所有的行走和跳跃。
结论
在本节中,我们了解了GKStateMachine,为我们的玩家分配了不同的状态,并对何时进入和退出这些状态应用了某些条件。最重要的是,我们为它们添加了动画并应用它们。
网友评论