DGArt Scenekit Playgrounds mini Game Tutorial Complete
Complete mini Game : iPadOS
The example Objects was modeled using DGArt and used in the Playground app.
Import the .scn file into the Playgrounds app using the following steps:
- Obtain the .scn file here for practicing the exercise mentioned below.
- Download BGAsset.zip
- Download PolyPlane.zip
- Download Star.zip
- In the Playgrounds app, tap on "Insert from..." in the side menu.
- Navigate to File Locations, and select .scn.
- You will see .scn appear under Resources.
Edit the ContentView code as shown below:
// Sample provide from www.nitrio.com for DGArt iPad
import SwiftUI
import SceneKit
struct ContentView: View {
var body: some View {
VStack {
GameViewControllerRepresentable()
VStack {
Text("DGArt Mini Game")
}.padding(10)
}
}
}
struct GameViewControllerRepresentable: UIViewControllerRepresentable {
func makeUIViewController (context: Context) -> GameViewController {
GameViewController ()
}
func updateUIViewController(_ uiViewController: GameViewController, context: Context) {
}
}
Edit the GameViewController code as shown below:
// Sample provide from www.nitrio.com for DGArt iPad
import UIKit
import SceneKit
public class GameViewController: UIViewController, SCNPhysicsContactDelegate {
private var scene: SCNScene!
private var sceneView = SCNView()
private var spriteScene: OverlayScene!
private var screenSize = CGSize(width: 500, height: 1200)
private var point: CGPoint = .zero
private var prepoint: CGPoint = .zero
private var plane:SCNNode = SCNNode()
public override func viewDidLoad() {
super.viewDidLoad ()
setup()
}
func setup () {
sceneView.frame = CGRect (origin: .zero, size: screenSize)
scene = SCNScene(named: "BGAsset.scn")
// set scene background color
scene?.background.contents = UIColor.init(red: 0.1, green: 0.1, blue: 0.2, alpha: 1.0)
//gen ground
let Ground = scene?.rootNode.childNode(withName: "Ground", recursively: true)
// position and scale ground
Ground?.position = SCNVector3(x: 0, y: -3, z: 0)
Ground?.scale = SCNVector3(x: 5, y: 5, z: 1)
//gen house
let House = scene?.rootNode.childNode(withName: "House", recursively: true)
// position and scale House
House?.position = SCNVector3(x: 0, y: 0, z: -20)
House?.scale = SCNVector3(x: 0.8, y: 0.8, z: 0.8)
// animate House
House?.runAction(SCNAction.wait(duration: 2)){
self.genHouseLocation(House!)
}
// gen and animate Trees
scene?.rootNode.childNodes.filter({ $0.name!.contains("Tree") }).forEach({
self.genTreeLocation($0)
})
// load aeroplane
let scene2 = SCNScene(named: "PolyPlane.scn")
plane = (scene2?.rootNode.childNode(withName: "Plane", recursively: true))!
plane.scale = SCNVector3(x: 0.4, y: 0.4, z: 0.4)
plane.eulerAngles = SCNVector3(x: 0, y: .pi , z: 0)
plane.position = SCNVector3(x: 0, y: 0, z: 0)
// add a name for collision detection
plane.name = "hero"
scene?.rootNode.addChildNode(plane)
// setup physicsBody
let Ball = SCNSphere(radius: 0.8)
plane.physicsBody = SCNPhysicsBody(type: .static, shape: .init(geometry: Ball))
plane.physicsBody?.categoryBitMask = 1
plane.physicsBody?.collisionBitMask = 2
plane.physicsBody?.contactTestBitMask = 1
// add and animate propeller
let propeller = scene?.rootNode.childNode(withName: "Propeller", recursively: true)
propeller?.runAction(SCNAction.repeatForever(SCNAction.rotateBy(x: 0, y: 0, z: 30, duration: 1)))
// load star
let scene3 = SCNScene(named: "Star.scn")
let star = (scene3?.rootNode.childNode(withName: "Star", recursively: true))!
star.position = SCNVector3(x: 0, y: -10, z: -5)
star.scale = SCNVector3(x: 0.4, y: 0.4, z: 0.4)
// setup physicsBody for star
let boxs = SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0)
star.physicsBody = SCNPhysicsBody(type: .static, shape: .init(geometry: boxs))
star.physicsBody?.categoryBitMask = 2
star.physicsBody?.collisionBitMask = 1
star.physicsBody?.contactTestBitMask = 2
// clone star
let star1 = star.clone()
scene?.rootNode.addChildNode(star1)
let star2 = star.clone()
scene?.rootNode.addChildNode(star2)
let star3 = star.clone()
scene?.rootNode.addChildNode(star3)
let star4 = star.clone()
scene?.rootNode.addChildNode(star4)
// gen and animate Stars
scene?.rootNode.childNodes.filter({ $0.name!.contains("Star") }).forEach({
self.genStarLocation($0)
})
// Add Light
let myLight = SCNNode()
myLight.light = SCNLight()
myLight.position = SCNVector3(x: -5, y: 10, z: 10)
myLight.look(at: SCNVector3(x: 0, y: 0, z: 0))
myLight.light?.intensity = 800
myLight.light?.type = SCNLight.LightType.directional
myLight.light?.color = UIColor.white
myLight.light?.castsShadow = true
scene?.rootNode.addChildNode(myLight)
// Add Camera
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
cameraNode.camera?.usesOrthographicProjection = true
cameraNode.camera?.orthographicScale = 8
cameraNode.position = SCNVector3(x: 0, y: 10, z: 15)
cameraNode.look(at: SCNVector3(x: 0, y: 0, z: 0))
sceneView.scene = scene
sceneView.pointOfView = cameraNode
sceneView.autoenablesDefaultLighting = true
scene?.physicsWorld.contactDelegate = self
// Add overlay for score display
spriteScene = OverlayScene(size: screenSize)
sceneView.overlaySKScene = spriteScene
//sceneView.showsStatistics = true
//sceneView.allowsCameraControl = true
//sceneView.debugOptions = [.showWireframe, .showBoundingBoxes, .showPhysicsShapes]
view.addSubview(sceneView)
}
// move aeroplane to touch position
func movePlane(_ snode: SCNNode) {
snode.position = SCNVector3(x: Float(prepoint.x), y: 0, z: Float(prepoint.y))
snode.runAction(SCNAction.move(to: SCNVector3(x: Float(point.x), y: 0, z: Float(point.y)), duration: 0.3))
}
// animate and reposition house after move from -z to z
func genHouseLocation(_ snode: SCNNode) {
let rand1 = randomNum(min: -3, max: 3)
let moveAct = SCNAction.sequence([
SCNAction.move(to: SCNVector3(rand1, -3.0, -25.0), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, -3.0, 20.0), duration: 6.0),
SCNAction.move(to: SCNVector3(rand1, -10.0, 20.0), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, -10.0, -25.0), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, -3.0, -25.0), duration: 0.2),
])
let randomRot = Float.random(in: -180...180)
snode.eulerAngles = SCNVector3(x: 0, y: randomRot, z: 0)
snode.runAction(moveAct, completionHandler: {
self.genHouseLocation(snode)
})
}
// animate and reposition trees after move from -z to z
func genTreeLocation(_ snode: SCNNode) {
let rand1 = randomNum(min: -4, max: 4)
let rand2 = randomNum(min: -5, max: 5)
let moveAct = SCNAction.sequence([
SCNAction.move(to: SCNVector3(rand1, -3.0, -25.0+rand2), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, -3.0, 20.0+rand2), duration: 6.0),
SCNAction.move(to: SCNVector3(rand1, -10.0, 20.0+rand2), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, -10.0, -25.0+rand2), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, -3.0, -25.0+rand2), duration: 0.2),
])
snode.runAction(moveAct, completionHandler: {
self.genTreeLocation(snode)
})
}
// animate and reposition stars after move from -z to z
func genStarLocation(_ snode: SCNNode) {
let rand1 = randomNum(min: -3, max: 3)
let rand2 = randomNum(min: -5, max: 5)
let randTime = randomNum(min: 0, max: 5)
let moveAct = SCNAction.sequence([
SCNAction.move(to: SCNVector3(rand1, 0.0, -25.0+rand2), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, 0.0, 20.0+rand2), duration: 6.0),
SCNAction.move(to: SCNVector3(rand1, -10.0, 20.0+rand2), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, -10.0, -25.0+rand2), duration: 0.2),
SCNAction.move(to: SCNVector3(rand1, 0.0, -25.0+rand2), duration: 0.2),
SCNAction.wait(duration: randTime),
])
snode.isHidden = false
snode.runAction(moveAct, completionHandler: {
self.genStarLocation(snode)
})
}
func randomNum(min:Int, max:Int) -> CGFloat {
let randomPosition = Int.random(in: min...max)
return CGFloat(randomPosition)
}
public override func touchesBegan(_ touches: Set<UITouch>, with
event: UIEvent?) {
guard let touchLocation = touches.first?.location (in: view) else { return }
prepoint = point
let pointo = touchLocation
let pointx = screenSize.width/2 - pointo.x
let pointy = screenSize.height/2 - pointo.y
point = CGPoint(x: -pointx/50.0, y: -pointy/50.0)
// move aeroplane to touch position
movePlane(plane)
}
public func physicsWorld(_ world: SCNPhysicsWorld, didBegin contact: SCNPhysicsContact) {
//print("didBegin!")
let firstNode = contact.nodeA
let secondNode = contact.nodeB
//print(firstNode.name! + " hit to " + secondNode.name!)
// if star contact with hero, score ++
if secondNode.name! == "hero"{
firstNode.isHidden = true
spriteScene.score += 1
}
if firstNode.name! == "hero"{
secondNode.isHidden = true
spriteScene.score += 1
}
}
public func physicsWorld(_ world: SCNPhysicsWorld, didUpdate: SCNPhysicsContact) {
//print("didUpdate!")
}
public func physicsWorld(_ world: SCNPhysicsWorld, didEnd: SCNPhysicsContact) {
//print("didEnd!")
}
}
Edit the OverlayScene code as shown below:
// Sample provide from www.nitrio.com for DGArt iPad
// Overlay SpriteKit on SceneKit
import UIKit
import SpriteKit
class OverlayScene: SKScene {
var scoreNode: SKLabelNode!
var score = 0 {
didSet {
self.scoreNode.text = "Score: \(self.score)"
}
}
override init(size: CGSize) {
super.init(size: size)
self.backgroundColor = UIColor.clear
self.isUserInteractionEnabled = false
self.scoreNode = SKLabelNode(text: "Score: 0")
self.scoreNode.fontName = "Verdana"
self.scoreNode.fontColor = UIColor.white
self.scoreNode.fontSize = 32
self.scoreNode.position = CGPoint(x: 100, y: size.height - 100)
self.addChild(self.scoreNode)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Try and play the mini game appear beside.
Alternatively, you can obtain the playground file here for exercise mentioned above. Download miniGameComplete.swiftpm.zip