康威的《Betway体育平台》

生命游戏是一个零玩家游戏,它最初是为二维空间设计的. 在生命的游戏中,有一个网格包含许多细胞,这些细胞要么活着,要么死了. 这个游戏是独立进行的,它遵循四个重要规则. 如果一个活细胞的邻居少于两个,它就会死亡. 如果一个活细胞有两到三个邻居,它就会活下来. 如果一个死细胞恰好有三个邻居,它就会变成活细胞. 最后,如果一个活细胞有三个以上的邻居,它就会死亡. 这些是生命游戏的基本规则. 如果你想了解更多关于这款游戏的信息,请访问以下链接:

创建项目

对于这个项目,我们将开始创建一个ARKit Swift Xcode项目. 一旦你点击了增强现实应用程序,选择任何名称和位置, 请记住选择Scenekit作为您的图形框架.

创建AR应用模板

创建细胞

首先,让我们创建一个名为CellOfLife的新Swift文件.斯威夫特. 对于这个类,我们希望有一个方框来表示单元格, 盒子的颜色, 以及细胞是否活着.

进口SceneKit

类CellOfLife: SCNNode {
    //单元格使用的默认活色
    private let aliveColor = UIColor.白色.withAlphaComponent (0.75)
    //表示单元格的方框
    private var 盒子Node: SCNNode
    //一个可以被设置并且盒子将使用的颜色
    public var color: UIColor? {
        didSet {
            自我.盒子Node.几何?.firstMaterial?.扩散.内容=颜色 ?? aliveColor
        }
        
    }
    //如果单元格是死的,盒子将被隐藏
    public var isAlive: Bool {
        didSet {
            盒子Node.isHidden = !isAlive
        }
    }
    
    //使用SCNBox创建单元格
    init(isAlive alive: Bool, 节点Width: CGFloat, 节点Height: CGFloat) {
        让盒子 = SCNBox(width: 节点Width, height: 节点Height, length: 节点Width, chamferRadius: 0)
        //设置第一个材质为aliveColor
        盒子.firstMaterial?.扩散.内容= aliveColor
        盒子Node = SCNNode(几何形状:盒子)
        isAlive =活着
        超级.init ()
        addChildNode (盒子Node)
        盒子Node.isHidden = !isAlive
    }
    
    需要初始化?(coder: NSCoder) {
        fatalError("init(coder:)尚未实现")
    }
}

创建多维数据集

既然已经创建了单元格,现在就可以创建多维数据集了! 让我们创建一个新的Swift文件,命名为CubeOfLife.斯威夫特. 对于这个类,我们将需要一个三个基本的数组,我们刚刚创建的细胞和一个只是,如果他们是活着的或不是. 首先,让我们添加这个类需要的变量.

进口SceneKit

类CubeOfLife: SCNNode {
    [[[Bool]] = []
    var 细胞sOfLife: [[[CellOfLife]] = []
    var大小:Int
    var zSize: Int
    var宽度:CGFloat
    var高度:CGFloat
    var isBuilt = false

    初始化(n: Int, width: CGFloat, height: CGFloat, withAliveCells 细胞s: [float3])? = nil, nHeight: Int = 5) {
        自我.大小= n
        自我.zSize = nHeight
        自我.宽度=宽度
        自我.身高=身高
        超级.init ()
        setupLife (withAliveCells:细胞)
    }

// ...

    需要初始化?(coder: NSCoder) {
        fatalError("init(coder:)尚未实现")
    }
}

SetupLife函数

对于SetupLife函数,我们希望随机生成一个立方体或获取活细胞位置.

private func setupLife(withAliveCells 细胞Locations: [float3])? = nil) {
        对于x in (0 ..< size) {
            var 飞机: [[Bool]] = []
            对于y in (0 ..< size) {
                var 行: [Bool] = []
                对于z in (0 ..< zSize) {
                    如果让细胞s = 细胞Locations {
                        //将位置居中
                        让数 = 细胞s.过滤器{Int ($ 0.x) + Int(size / 2) == x &&
                                                    Int(0美元.y) + Int(size / 2) == y &&
                                                    Int(0美元.z + 1) == z }
                        行.追加(!数.isEmpty)
                        
                    其他}{
                        / /随机!
                        行.追加(Bool.随机())
                    }
                }
                飞机.追加(行)
            }
            生活.追加(飞机)
        }
    }

快速辅助函数

为了从数组中检索单元格,我们不希望索引越界! 因此,我们将创建一个helper函数来获取值并确保我们没有试图获取一个不在数组中的值.

private func get(_ x: Int, _ y: Int, _ z: Int) -> Bool? {
        if x > 0, y > 0, z > 0, x < size, y < size, z < zSize {
            让价值=生命[x][y][z]
            
            返回值
        }
        返回nil
        
    }
    
    private func get(_ x: Int, _ y: Int, _ z: Int, from: [[[Bool]]]) -> Bool? {
        if x > 0, y > 0, z > 0, x < from.数, y < from.数, z < from.计数{
            让value = from[x][y][z]
            
            返回值
        }
        返回nil
        
    }

建筑的盒子

现在我们将创建构建函数. 对于这个函数,它应该只运行一次,这样它就会触发isBuilt标志.

函数构建(){
        对于x in (0 ..< size) {
            [[CellOfLife]] = []
            对于y in (0 ..< size) {
                var 行: [CellOfLife] = []
                对于z in (0 ..< zSize) {
                    //如果单元格是活的
                    let isAlive = 生活[x][y][z]
                    //获取宽度和高度
                    让节点Width = width / CGFloat(size)
                    让节点Height = height / CGFloat(size)
                    //创建基本单元格
                    让细胞 = CellOfLife(isAlive: isAlive, 节点Width: 节点Width, 节点Height: 节点Height)
                    //设置单元格的位置
                    细胞.位置 = SCNVector3((CGFloat(x) * 节点Width) - width / 2, (CGFloat(y) * 节点Height) - width / 2, CGFloat (z) * 节点Width)
                    //计算到中心的距离
                    让节点1Pos = SCNVector3ToGLKVector3(单元格.位置)
                    让节点2Pos = SCNVector3ToGLKVector3(SCNVector3(CGFloat(位置 . 位置 . 位置).x) + 节点Width / 2, CGFloat(位置.y) + 节点Height / 2, CGFloat(位置 . y).z) + 节点Width / 2))
                    让距离= GLKVector3Distance(节点1Pos, 节点2Pos)
                    //设置框的颜色
                    让颜色= UIColor(红色:CGFloat(255 - (x * 10)) / 255.0、绿色:CGFloat(255 - (y * 10)) / 255.0,蓝色:CGFloat(255 - (z * 10)) / 255.0, alpha: CGFloat(1 - distance))
                    细胞.颜色=颜色
                    //将细胞添加到生命的立方体中
                    addChildNode(细胞)
                    行.追加(细胞)
                }
                
                飞机.追加(行)
            }
            细胞sOfLife.追加(飞机)
        }
        //立方体已经建好了
        isBuilt = true
        //启动定时器
        计时器.scheduled计时器 (timeInterval: 0.5、target: 自我, selector: #selector(tick), userInfo: nil, repeats: true
    }

更新数据集

接下来,我们将创建一个简单的函数来遍历立方体并更新所有单元格.

函数update () {
        对于x in (0 ..< size) {
            对于y in (0 ..< size) {
                对于z in (0 ..< zSize) {
                    让细胞 = 细胞sOfLife[x][y][z]
                    let isAlive = 生活[x][y][z]
                    细胞.isAlive = isAlive
                }
            }
        }
    }

计时

最后,我们需要创建它的最后一个函数,即每次触发计时器时都会发生的tick方法. 对于这个方法,我们会遍历ever 细胞并得到该细胞的所有邻居. 然后我们会得到邻居的总数. 对于三维的“生命游戏”,我们的规则如下:

  • 如果一个活细胞有0 - 3个邻居,它就会死亡
  • 如果一个活细胞有4 - 6个邻居,它就会活下来
  • 如果死亡细胞有4个邻居,它就会变成活的
  • If an Alive Cell has > 6 neighbors it will die
   @objc
    func蜱虫(){
        var 美国: [[[Bool]] = []
        对于x in (0 ..< size) {
            var 飞机: [[Bool]] = []
            对于y in (0 ..< size) {
                var 行: [Bool] = []
                对于z in (0 ..< zSize) {
                    让邻居:[保龄球?] = [
                        / /底部
                        get (x - 1, y-1 z 1),
                        得到(x, y-1 z 1),
                        get (x, y, z 1),
                        得到(x, y + 1, z 1),
                        得到(x + 1, y + 1, z 1),
                        得到(x - 1, y + 1, z 1),
                        得到(x + 1, y-1 z 1),
                        get (x, y, z 1),
                        得到(x + 1, y, z 1),
                        / /边
                        (x - 1, y-1, z),
                        (x, y-1, z),
                        得到(x, y + 1, z),
                        得到(x + 1, y + 1, z),
                        得到(x - 1, y + 1, z),
                        得到(x + 1, y-1, z),
                        (x, y, z),
                        得到(x + 1, y, z),
                        / /顶部
                        得到(x - 1, y-1, z + 1),
                        得到(x, y-1, z + 1),
                        得到(x, y, z + 1),
                        get (x, y + 1, z + 1),
                        get (x + 1, y + 1, z + 1),
                        get (x - 1, y + 1, z + 1),
                        get (x + 1 y-1 z + 1),
                        get (x, y, z + 1),
                        get (x + 1, y, z + 1),
                        ]
                    
                    让neighborsSum =邻居.compactMap {$0}.地图{$ 0 ? 1 : 0 }.减少(0,+)
                    开关neighborsSum {
                    例0 ... 3:
                        行.追加(假)
                    例4 ... 6:
                        if isAlive = get(x, y, z) {
                            如果isAlive {
                                行.追加(真正的)
                            其他}{
                                行.追加(neighborsSum = = 4)
                            }
                        其他}{
                            行.追加(假)
                        }
                    默认值:
                        行.追加(假)
                    }
                }
                飞机.追加(行)
            }
            美国.追加(飞机)
        }
        生活=美国
        update ()
    }

创造生命!

首先我们会进入ViewController.并改变viewDidLoad来加载一个空白场景. 我们还想将世界跟踪的飞机Detection设置为水平.

进口UIKit
进口SceneKit
进口ARKit

//基于off: http://en.Wikipedia.org/wiki/Conway%27s_Game_of_Life

类ViewController: UIViewController, ARSCNViewDelegate {

    @IBOutlet var sceneView: ARSCNView!
    private var isspawn = false
    私有var 多维数据集: CubeOfLife?
    
    重载func viewDidLoad () {
        超级.viewDidLoad ()
        
        //设置视图的委托
        sceneView.委托=自我
        
        //显示统计信息,如fps和计时信息
        sceneView.showsStatistics = true
        
        //将场景设置到视图中
        sceneView.现场= SCNScene ()
    }
    
    重载func viewWillAppear(_ animated: Bool) {
        超级.那些(动画)
        
        //创建会话配置
        让配置= ARWorldTrackingConfiguration()
        
        配置.飞机Detection = .水平

        //运行视图的会话
        sceneView.会话.运行(配置)
    }
    
// ...
}

渲染器ARSCNViewDelegate

接下来,我们需要使用ARSCNViewDelegate中的渲染器方法. 对于这个,我们想看看传递的锚是否是一个ARPlaneAnchor,然后我们将构建创建CubeOfLife.

func renderer(_ renderer: SCNSceneRenderer, didAdd 节点: SCNNode, for anchor: ARAnchor) {
        if !isSpawned {
            如果让飞机Anchor = anchor as? ARPlaneAnchor {
                / /创建平面
                让飞机Width = CGFloat(飞机Anchor ..程度上.x)
                让飞机Height = CGFloat(飞机Anchor ..程度上.z)
                
                让平面= SCNPlane(width: 飞机Width, height: 飞机Height)
                
                让飞机Node = SCNNode()
                飞机Node.= SCNVector3 (飞机Anchor位置.center.x, 0, 飞机Anchor.center.z)
                飞机Node.变换= SCNMatrix4MakeRotation(——漂浮.pi / 2, 1, 0, 0)
                飞机.firstMaterial?.扩散.内容=用户界面颜色.黑色的.withAlphaComponent (0.75)
                飞机Node.平面几何=
                
                //创造生命立方体
                立方体= CubeOfLife(n: 10, width: 飞机Width / 2, height: 飞机Width / 2, nHeight: 10)
                多维数据集?.位置= 飞机Node.位置
                
                飞机Node.addChildNode(多维数据集!)
                
                节点.addChildNode (飞机Node)
                isSpawned.切换()
            }
        }
    }

TouchesEnded

最后,我们将让用户触摸屏幕来生成立方体. 一旦我们在增强现实中生成了平面,我们将重写touchesEnded函数来构建立方体.

override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        超级.touchesEnded(触摸事件):
        如果数据集?.isBuilt ?? 假{
            多维数据集?.蜱虫()
        其他}{
            多维数据集?.build ()
        }
    }

最终结果

一旦你完成了,你应该有以下结果:

如果您有任何问题或想看源代码,它张贴 GitHub

如果你想了解更多关于CRi如何帮助你 IOS开发 请联系 我们的团队 或致电 gig@korean-business-cards.com.

Swift开发人员,热情地把咖啡变成代码. 目前在Objective-C和Swift中使用MetalKit, ARKit和SceneKit. 请随我来 GitLab 或者加入我的开源敏捷开发团队 oneleif.

联系