#ちょっと見てくれ!!!
初めて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
}
}