在本教程的前两部分中,我为游戏创建了基础,并且作为(简单的) Terrain类的主要部分来构建了一个有趣的游戏竞技场,而无需花费太多精力。 另外,我还创建了一个Player类,所需的动画和允许用户控制平面的滑动手势。
游戏性-游戏的本质
好吧,到目前为止,我们还没有真正的游戏,只是一个简单的基础来展示SceneKit的基本内容。 到目前为止,我们错过的主要事情是所谓的游戏性 。 这是一个目标或游戏机制,它赋予游戏目的,并使其成为游戏。 在本教程的第3部分中,现在会发生这种情况🙂为了使它更有趣,我在场景中添加了环,我们可以在其中飞槽,每次这样做都可以给我们一点信息。 同样,我们也没有任何可能向用户显示一些信息。 在我们的案例中,我们飞过的槽数。
游戏对象
戒指
Ring类也是基于SCNNode的类。 这次我们不使用模型,而是SceneKit中可用的原语之一: SCNSphere 。 为了使其更具吸引力,我们在其上应用纹理并使其旋转。 但是让我们看一下代码:
let ringMaterial = SCNMaterial()
ringMaterial.diffuse.contents = UIImage(named: "art.scnassets/ringTexture")
ringMaterial.diffuse.wrapS = .repeat
ringMaterial.diffuse.wrapT = .repeat ringMaterial.diffuse.contentsTransform = SCNMatrix4MakeScale(3, 1, 1)
首先,我为球创建材质。 材料是使节点具有外观的方式。 我们既可以应用颜色,也可以应用图像(称为纹理)。 纹理是使对象看起来更细腻的常用方法。 SceneKit使您可以轻松使用它们,如您所见。 最后3条语句需要使它看起来更好。 我们不仅使用纹理一次,而且让其重复,所以我们得到了更立体的图案。 玩一下“ SCNMatrix4MakeScale(3,1,1)”中的参数以了解效果。
let ring = SCNTorus(ringRadius: 5.0, pipeRadius: 0.5)
ring.materials = [ringMaterial]
let ringNode = SCNNode(geometry: ring)
self.addChildNode(ringNode)
创建环后,将材质应用到节点,该节点也添加到场景中。 就这样。 有了这个,我们现在可以将戒指放在游戏场景中的任何位置。
ringNode.eulerAngles = SCNVector3(degreesToRadians(value: 90), 0, 0)
默认情况下,球体垂直放置在场景中。 为了使飞槽成为可能,我们绕环的x坐标旋转了90度。 注意:与大多数3D API(以及SceneKit)一样,使用弧度代替度数。 因此,无论何时使用,都必须将其从度转换为弧度。
let action = SCNAction.rotateBy(x: 0, y: 0, z: degreesToRadians(value: 360), duration: 3.0) ringNode.runAction(SCNAction.repeatForever(action))
为了使它更好,我让戒指旋转。 下面的代码其余部分将创建另一个节点。 我们要检查飞机是否飞过环。 因此,我们在中间放置了一个不可见的节点,并在其上附加了一个物理物体。 有关SceneKit中物理引擎及其使用方法的详细信息,请参见下文。
// Contact box
let boxMaterial = SCNMaterial()
boxMaterial.diffuse.contents = UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.0)
let box = SCNBox(width: 10.0, height: 10.0, length: 1.0, chamferRadius: 0.0)
box.materials = [boxMaterial]
let contactBox = SCNNode(geometry: box)
contactBox.name = "ring"
contactBox.physicsBody = SCNPhysicsBody(type: .kinematic, shape: nil)
contactBox.physicsBody?.categoryBitMask = Game.Physics.Categories.ring
self.addChildNode(contactBox)
平视显示器
第三部分中的第二个新类是HUD(代表Heads Up Display )。 HUD本身不是使用SceneKit制作的。 它基于SpriteKit(SceneKit的2D吊坠)。 目前,我们仅显示一个文本来显示已通过的响铃次数,但稍后我们将对其进行扩展。
class HUD {
private var _scene: SKScene!
private let _points = SKLabelNode(text: "0 RINGS")
var scene: SKScene {
get { return _scene }
}
var points: Int {
get { return 0 }
set(value) {
_points.text = String(format: "%d RINGS", value)
}
}
init(size: CGSize) {
_scene = SKScene(size: size)
_points.position = CGPoint(x: size.width/2, y: size.height-50)
_points.horizontalAlignmentMode = .center
_points.fontName = "MarkerFelt-Wide"
_points.fontSize = 30
_points.fontColor = UIColor.white
_scene.addChild(_points)
}
}
仅此而已。 我们只需要将其附加到sceneView
_sceneView.overlaySKScene = hud.scene
并将其连接到GameLevel,因此我们可以将其用作游戏逻辑的一部分。
物理
我在本教程的这一部分介绍的另一件事是物理引擎。 在这个游戏中,我们只需要它来进行碰撞检测,这在3D游戏中可能很难。 因此拥有它是一个很大的优势。 稍后,我们还将把它用于真实的物理行为。 SceneKit使这一过程变得非常简单。 实际上,我们只需要执行以下步骤。
- 将联系人代表设置为我们的场景
self.physicsWorld.contactDelegate = self
- 实现
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact)
- 向节点添加物理实体
- 设置
categoryBitMask
(请参阅GameSettings.swift ) - 设置
contactTestBitMask
以获取我们想要的节点的联系消息
委托消息很简单
func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
if let ring = contact.nodeB.parent as? Ring {
collision(withRing: ring)
}
}
当然,以后会变得更加复杂,但是到目前为止,我们只有一个玩家和一些戒指。 这样,我们不必检查更多。 因此我们知道,nodeA将始终是播放器,nodeB是一个环。 然后使用额外的方法处理碰撞处理本身
func collision(withRing ring: Ring) {
if ring.isHidden { return }
ring.isHidden = true
_player!.roll()
touchedRings += 1
_hud?.points = touchedRings
}
此屏幕快照显示了播放器和戒指的碰撞盒。 用于调试目的。
这或多或少是不言自明的。 我们通过测试isHidden()标志来检查之前是否碰过戒指。 如果不是,我们将环隐藏起来,让飞机滚动以达到很好的效果,并增加接触环的数量。 我们还将更新HUD。
现在就这样! 我希望您喜欢本教程的一部分。 下次我们将进一步改善游戏玩法,并从中做出真正的游戏 。
别忘了……再次链接到源代码
教程章节:
–第1部分–建立地形
–第2部分–创建一个真实的玩家游戏对象
–第3部分–为您的地形增添生命
–第4部分–实施游戏循环
–第5部分–使用CoreMotion平稳地飞行
–第六部分–完成游戏(缺少的部分)
–第7部分– ARKit:在您的环境中玩
–第8部分–多平台:我们在macOS上的游戏
–第9部分– tvOS:出色的游戏平台
–第10部分-高级SceneKit(有待改进)
最初于 2017年12月23日 发布在 https://rogerboesch.github.io/scenekit/tutorial/games/2017/12/23/scenekit-zerotohero-III.html 。