swl
swl

Reputation: 129

Converting UIKit to SceneKit

Trouble converting program from UIKit to SceneKit. Biggest difficulty for me is understanding how delegate file, Tile, synched with array, Board, is set up with SceneKit. It is a simple project. A screenshot: https://i.sstatic.net/CZWkj.jpg. It displays a 3 x 5 array. User taps an item and it becomes highlighted. Then tap another item, it becomes highlighted, previous item, unhighlighted.

Here is the UIKit project composed of 3 files:

VIEWCONTROLLER

import UIKit

struct BoardLoc {
    var x: Int
    var y: Int
}

class ViewController: UIViewController, TileDelegate {

    var tile: Tile!

    override func viewDidLoad() {
        super.viewDidLoad()

        let scene = Board()
        tile.tileDelegate = self
        tile.board = scene
    }

    func getTileAtLoc(tile: Tile, _ boardLoc: BoardLoc) {
        tile.boardLoc = boardLoc
    }  
}

BOARD

import Foundation

class Board {
    var board: Array<Array<String>> = Array(count:3, repeatedValue:Array(count:5, repeatedValue:"foo"))

    func putTileAt(boardLoc: BoardLoc) -> String {
        return board[boardLoc.x][boardLoc.y]
    }  
}

TILE

import UIKit

protocol TileDelegate {
    func getTileAtLoc(tile: Tile, _ boardLoc: BoardLoc)
}

class Tile: UIView {
    var boardLoc: BoardLoc?
    var board: Board?
    var tileDelegate: TileDelegate?

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        addGestureRecognizer(UITapGestureRecognizer(target: self, action:"handleTap:"))
    }

    override func drawRect(rect: CGRect) {
        for x in 0...2 {
            for y in 0...4 {

                let context = UIGraphicsGetCurrentContext()
                let red = UIColor.redColor().CGColor
                let orange = UIColor.orangeColor().CGColor
                let bigCircle = CGRectMake(CGFloat(106 * x),CGFloat(106 * y), 106, 106)
                let smallCircle = CGRectMake(CGFloat(106 * x) + 3, CGFloat(106 * y) + 3, 100, 100)

                if (boardLoc != nil && boardLoc!.x == x && boardLoc!.y == y) {
                    CGContextSetFillColorWithColor(context, red)
                    CGContextFillEllipseInRect(context, bigCircle)
                }

                if board!.putTileAt(BoardLoc(x: x, y: y)) == "foo" {
                    CGContextSetFillColorWithColor(context, orange)
                    CGContextFillEllipseInRect(context, smallCircle)
                }
            }
        }
    }

    func handleTap(gestureRecognizer: UIGestureRecognizer) {
        let point = gestureRecognizer.locationInView(self)
        let boardLoc = BoardLoc(x: Int(point.x) / 106, y: Int(point.y) / 106)
        tileDelegate!.getTileAtLoc(self, boardLoc)
        setNeedsDisplay()
    }
}

Upvotes: 4

Views: 1344

Answers (2)

swl
swl

Reputation: 129

import SceneKit

class GameViewController: UIViewController {

struct BoardLoc {
    var x: Int
    var y: Int
}

enum Type {
    case Yellow
    case Orange
}

var boardArray: Array<Array<Type>> = []

override func viewDidLoad() {
    super.viewDidLoad()

    for x in 0...2 {
        boardArray.append(Array(count:5, repeatedValue:Type.Orange))
        for y in 0...4 {
            boardArray[x][y] = Type.Orange
        }
    }

    let scene = SCNScene(named: "art.scnassets/balls8.dae")
    let scnView = self.view as SCNView
    scnView.scene = scene

    scnView.autoenablesDefaultLighting = true

    let taps = NSMutableArray()
    let tap = UITapGestureRecognizer(target: self, action: "handleTap:")
    taps.addObject(tap)
    scnView.gestureRecognizers = taps
}

func handleTap(gestureRecognizer: UIGestureRecognizer) {
    let scnView = view as SCNView
    let point = gestureRecognizer.locationInView(scnView)
    if let hitResults = scnView.hitTest(point, options: nil) {
        if hitResults.count > 0 {

            let result: AnyObject! = hitResults[0]

            if !result.node!.name!.hasPrefix("Orange") {
                return
            }

            let tapLoc = BoardLoc(x: Int(point.x) / 106, y: Int(point.y) / 106)

            boardArray[tapLoc.x][tapLoc.y] = Type.Yellow



            for col in 0...2 {
                for row in 0...4 {
                    var yellowBall = scnView.scene!.rootNode.childNodeWithName("Yellow", recursively: true)
                    var secxtorX = Float(col) * 16.5 - 16
                    var sectorY = 34 - (Float(row) * 16.5)
                    if boardArray[col][row] == Type.Yellow {
                        yellowBall!.runAction(SCNAction.moveTo(SCNVector3(x: secxtorX, y: sectorY, z: 25), duration: 0.01))
                        boardArray[tapLoc.x][tapLoc.y] = Type.Orange
                    }
                }
            }
        }
    }
}
}

Upvotes: 1

Kazuki Sakamoto
Kazuki Sakamoto

Reputation: 13999

First of all, I recommend you to read Apple SceneKit document and some tutorials.

Scene Kit is a 3D-rendering Objective-C framework that combines a high-performance rendering engine with a high-level, descriptive API. Scene Kit supports the import, manipulation, and rendering of 3D assets without requiring the exact steps to render a scene the way OpenGL does.

Scene Kit allows you to render 3D scene easily, without OpenGL ES APIs. However you should understand how Scene Kit works.

Basically, Scene Kit provides a view controller that maintains an animation loop. This loop follows a design pattern common in games and simulations, with two phases: update and render. In the implementation, Scene Kit has more phases like the following figure (from http://www.objc.io/issue-18/scenekit.html), but basically, two phases, update and render.

Scene Kit phases

So how to create Scene Kit project, the basics is

  • Prepare SCNView
  • Initialize 3D scene
  • Create touch event handler
  • Implement Update phase: Update game board using the touched object or the touched position, Update the animation of the objects, or some sort of stuff.
  • Implement Render phase: Basically, Scene Kit automatically renders registered 3D objects and models.

Thus, you should implement as the following.

  • Use SCNView instead of ViewController
  • Create a scene
  • Place Board and Tiles as Scene Kit 3D objects
  • Use hitTest for touching Tile and update Tiles in Update phase

Upvotes: 3

Related Questions