0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swift4でライフゲームを楽しむ

Last updated at Posted at 2019-01-25

#ちょっと見てくれ!!!
初めてSwiftでアプリを作りました。
まだ洗練されていないですが、皆さんのアドヴァイスが頂きたく投稿します。
Swift4
xcode10.1
iPad第6世代
iPhone6s
で動作確認済み

#今後の課題
Intervalの処理
より効率的に処理(今は3万cellでもう重い)
一時停止やcellの数を変化させるボタンをつける
長生きしているcellに色の変化をつけるのも面白そう

ViewController.swift
import UIKit
import SpriteKit
class ViewController: UIViewController {
    //方形作成関数
    func fillRect(recSize:CGSize,color:CGColor) -> UIImage {
        //↓引数:図形の大きさ、”不透明さ”、よくわからん
        UIGraphicsBeginImageContextWithOptions(recSize, false, 0.0)
        let context = UIGraphicsGetCurrentContext()
        let origin = CGPoint(x:0, y: 0)
        let rect = CGRect(origin: origin, size: recSize)
        
        //pathとは何か?
        let path = UIBezierPath(rect: rect)
        context?.setFillColor(color)
        
        path.fill()
        
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return image!
    }

    override func viewDidLoad() {
        //方形のサイズ
        let widthOfARect    =   UIScreen.main.bounds.width/CGFloat(width)
        let heightOfARect   =   UIScreen.main.bounds.height/CGFloat(height)
        let size:CGSize = CGSize(width: widthOfARect, height: heightOfARect)
        var rectangle:UIImage!
        
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.view.backgroundColor = UIColor.black
        //cellの配列を作成
        makeCellsStructure()
        //celsBackUpも同構造にする
        cellsBackUp=cells
     //方形のimageを作る
         rectangle = self.fillRect(recSize:size, color: UIColor.green.cgColor)
        
        //空の配列を作成
        var rectView = [UIImageView?]()
        for _ in 0...width*height-1{
            rectView.append(nil)
        }
        
        //初期設定用ランダム
        let startNum:Int = Int.random(in:0...height*width-1)
        //テスト用初期設定どんぐり
            cells[startNum].dOA=1
            cells[startNum+width+2].dOA=1
            cells[startNum+width*2-1].dOA=1
            cells[startNum+width*2].dOA=1
            cells[startNum+width*2+3].dOA=1
            cells[startNum+width*2+4].dOA=1
            cells[startNum+width*2+5].dOA=1
        //テスト用銀河
       for i in 0...5{
            cells[coorToCellNum(x: 0+width/2, y: i+height/2)].dOA=1
            cells[coorToCellNum(x: 1+width/2, y: i+height/2)].dOA=1
            
            cells[coorToCellNum(x: 7+width/2, y: i+3+height/2)].dOA=1
            cells[coorToCellNum(x: 8+width/2, y: i+3+height/2)].dOA=1
            
            cells[coorToCellNum(x: i+3+width/2, y: 0+height/2)].dOA=1
            cells[coorToCellNum(x: i+3+width/2, y: 1+height/2)].dOA=1
            
            cells[coorToCellNum(x: i+width/2, y: 7+height/2)].dOA=1
            cells[coorToCellNum(x: i+width/2, y: 8+height/2)].dOA=1
        }
        //テスト用パルサー
        for i in 0...2{
            //横並びの
            cells[coorToCellNum(x: 15+2+i, y: 20+6)].dOA=1
            cells[coorToCellNum(x: 15-2-i, y: 20+6)].dOA=1
            
            cells[coorToCellNum(x: 15+2+i, y: 20+1)].dOA=1
            cells[coorToCellNum(x: 15-2-i, y: 20+1)].dOA=1
            
            cells[coorToCellNum(x: 15+2+i, y: 20-1)].dOA=1
            cells[coorToCellNum(x: 15-2-i, y: 20-1)].dOA=1
            
            cells[coorToCellNum(x: 15+2+i, y: 20-6)].dOA=1
            cells[coorToCellNum(x: 15-2-i, y: 20-6)].dOA=1
            //縦並びの
            cells[coorToCellNum(x: 15-6, y: 20+2+i)].dOA=1
            cells[coorToCellNum(x: 15-6, y: 20-2-i)].dOA=1
            
            cells[coorToCellNum(x: 15-1, y: 20+2+i)].dOA=1
            cells[coorToCellNum(x: 15-1, y: 20-2-i)].dOA=1
            
            cells[coorToCellNum(x: 15+1, y: 20+2+i)].dOA=1
            cells[coorToCellNum(x: 15+1, y: 20-2-i)].dOA=1
            
            cells[coorToCellNum(x: 15+6, y: 20+2+i)].dOA=1
            cells[coorToCellNum(x: 15+6, y: 20-2-i)].dOA=1
        }
        
        //計算と描写をする。
        //この時間処理をもっと簡単にできないか?
        _=Timer.scheduledTimer(withTimeInterval: speed*2.0, repeats: true) { (timer) in
            rectView = self.drawCells(rectangle: rectangle, rectViewTan: rectView, widthOfARect: widthOfARect)
            DispatchQueue.main.asyncAfter(deadline: .now()+speed, execute: {
                rectView = self.drawCellsBackUp(rectangle: rectangle, rectViewTan: rectView, widthOfARect: widthOfARect)}
        )}
    }
    
    func drawCells(rectangle:UIImage,rectViewTan:[UIImageView?],widthOfARect:CGFloat) ->[UIImageView?] {
        var rectView = rectViewTan
        
        for i in 0...width*height-1{
            //前回の描写の削除
            if rectView[i] != nil {
                rectView[i]!.removeFromSuperview()
            }
            //描写開始
            if cells[i].dOA==1 {
                //そこにimageを追加。この時点でrectView[i]はnilじゃない
                rectView[i] = UIImageView(image: rectangle)
                //rectViewのアンカーポイントを左上にする
                rectView[i]!.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
                //ancherポイントのsuperview座標x,y
                let xOfRect = CGFloat(cellNumToCoorX(celNum: i))*widthOfARect
                let yOfRect = CGFloat(cellNumToCoorY(celNum: i))*widthOfARect
                rectView[i]!.center =  CGPoint(x: xOfRect, y: yOfRect)
                self.view.addSubview(rectView[i]!)
                
            }
            //cellsから次のcellsBackUpを決めておく
            gameOfLife1(i: i)
        }
        return rectView
    }
    func drawCellsBackUp(rectangle:UIImage,rectViewTan:[UIImageView?],widthOfARect:CGFloat)->[UIImageView?] {
        var rectView = rectViewTan
        //cellsbackupより描写
        for m in 0...width*height-1{
            //さっきの描写の削除
            if rectView[m] != nil {
                rectView[m]!.removeFromSuperview()
            }
            if cellsBackUp[m].dOA==1{
                rectView[m] = UIImageView(image:rectangle)
                rectView[m]!.layer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
                //ancherポイントのsuperview座標x,y
                let xOfRect = CGFloat(cellNumToCoorX(celNum: m))*widthOfARect
                let yOfRect = CGFloat(cellNumToCoorY(celNum: m))*widthOfARect
                rectView[m]!.center =  CGPoint(x: xOfRect, y: yOfRect)
                self.view.addSubview(rectView[m]!)
            }//cellsVackUpから次のcellsを用意
            gameOfLife2(i: m)
        }
        return rectView
    }
       }

Cell.swift
import Foundation

//下処理
//描写と描写のインターバル(秒)
let speed:Double   = 0.05

//並べるcellの数...height:width = 4:3にしよう(iPadのアスペクト比は4:3)
let height  = 200
let width   = 150
struct Cell{
    var x:Int
    var y:Int
    //DeadOrAlive 0:死亡,1:生存
    var dOA:Int
}
//cellsとcellsBackUpを交互に計算し、描写する
//他のもっとシンプルな方法はないか?
var cells = [Cell]()
var cellsBackUp = [Cell]()

/*
 配列の並べ方、地球の歩き方
 example
 0  1   2   3
 4  5   6   7
 8  9   10  11
 */
//cellの配列を作成
func makeCellsStructure() {
    for i in 0...height-1 {
        for m in 0...width-1{
            cells.append(Cell(x: m, y: i, dOA: 0))
        }
    }
}
//座標系と配列の接続
//ドラクエ式ループX
func modifyX(v:Int ) ->Int {
    var modifiedX = v
    if v<0 {
        modifiedX = v+width
    }else if v>width-1{
        modifiedX = v-width
    }
    return modifiedX
}
//ドラクエ式ループY
func modifyY(v:Int ) ->Int {
    var modifiedY = v
    if v<0 {
        modifiedY = v+height
    }else if v>height-1{
        modifiedY = v-height
    }
    return modifiedY
}
//座標からcells[]のindex番号へ
func coorToCellNum(x:Int,y:Int)-> Int {
    var celNum = 0
    let xx = modifyX(v: x)
    let yy = modifyY(v: y)
    celNum = width*yy+xx
    return celNum
}
//index番号からx座標を出す
func cellNumToCoorX(celNum:Int)->Int {
    return celNum%width
}
//index番号からy座標を出す
func cellNumToCoorY(celNum:Int)->Int {
    var retY = 0
    for i in 0...height{
        if celNum+1 <= i*width {
            retY = i-1
            break
        }}
    return retY
}

//cellsでの周囲のセルの生死を確認
func countAliveNeighbors(celNum:Int)->Int {
    
    let coorX = cellNumToCoorX(celNum: celNum)
    let coorY = cellNumToCoorY(celNum: celNum)
    
    
    let uL = coorToCellNum(x: coorX-1, y: coorY+1)
    let uC = coorToCellNum(x: coorX, y: coorY+1)
    let uR = coorToCellNum(x: coorX+1, y: coorY+1)
    
    let mL = coorToCellNum(x: coorX-1, y: coorY)
    let mR = coorToCellNum(x: coorX+1, y: coorY)
    
    let lL = coorToCellNum(x: coorX-1, y: coorY-1)
    let lC = coorToCellNum(x: coorX, y: coorY-1)
    let lR = coorToCellNum(x: coorX+1, y: coorY-1)
    
    let counter =   cells[uL].dOA+cells[uC].dOA+cells[uR].dOA +
        cells[mL].dOA+cells[mR].dOA +
        cells[lL].dOA+cells[lC].dOA+cells[lR].dOA
    return counter
}
//cellsBackUpでの周囲のセルの生死を確認
func countAliveNeighborsAmongCellBackUp(celNum:Int)->Int {
    
    let coorX = cellNumToCoorX(celNum: celNum)
    let coorY = cellNumToCoorY(celNum: celNum)
    
    
    let uL = coorToCellNum(x: coorX-1, y: coorY+1)
    let uC = coorToCellNum(x: coorX, y: coorY+1)
    let uR = coorToCellNum(x: coorX+1, y: coorY+1)
    
    let mL = coorToCellNum(x: coorX-1, y: coorY)
    let mR = coorToCellNum(x: coorX+1, y: coorY)
    
    let lL = coorToCellNum(x: coorX-1, y: coorY-1)
    let lC = coorToCellNum(x: coorX, y: coorY-1)
    let lR = coorToCellNum(x: coorX+1, y: coorY-1)
    
    let counter =   cellsBackUp[uL].dOA+cellsBackUp[uC].dOA+cellsBackUp[uR].dOA +
        cellsBackUp[mL].dOA+cellsBackUp[mR].dOA +
        cellsBackUp[lL].dOA+cellsBackUp[lC].dOA+cellsBackUp[lR].dOA
    return counter
}

func gameOfLife1(i:Int) {
    //cellsでの周囲を考えてcellsBackUpに反映する関数
        let lives = countAliveNeighbors(celNum: i)
        switch lives {
            //中央のcellに接するcellのうち3個が生存なら中央のcellは誕生(あるいは生存)
        case 3:
            cellsBackUp[i].dOA=1
            //中央のcellに接するcellのうち2個が生存なら中央のcellは現状維持(死亡状態なら死亡、生存状態なら生存)
        case 2:
            cellsBackUp[i].dOA=cells[i].dOA
            //過密、過疎で死亡
        case 0,1,4,5,6,7,8:
            cellsBackUp[i].dOA=0
            if cells[i].dOA==1{
                    }
            //とりあえず0代入
        default:
            cellsBackUp[i].dOA=0
                }
    }

func gameOfLife2(i:Int) {
    //cellsBackUpでの周囲を考えてcellに反映する関数
        let lives = countAliveNeighborsAmongCellBackUp(celNum: i)
        switch lives {
        case 3:
            cells[i].dOA=1
        case 2:
            cells[i].dOA=cellsBackUp[i].dOA
            
        case 0,1,4,5,6,7,8:
            cells[i].dOA=0
            if cellsBackUp[i].dOA==1{
            }
        default:
            cells[i].dOA=0
    }
}

0
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?