nitrio DGArt

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: 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