barus's diary

とても真面目なblogですにゃ

swift4(Xcode9)iPhoneアプリ、パズドラ風ゲームを作成する

こちらのサイトで紹介されていたコードはswift4環境だと

エラーが沢山出たので、修正したのを紹介する。

 f:id:hatakeka:20180129183140g:plain

swiftという言語は頻繁にバージョンアップし内容も変わるので、超うんこ面倒くさい。

恐らく、数年もしないうちに、swift5、6・・・と出て、(2018年1月の現時点)互換性がすぐ無くなるだろうと思う。iphoneという機種が世代交代する度に機能に併せてバージョンアップしているから仕方ないかもしれない。(・・にしてもである(´Д` ))

 

 

 

プロジェクトはSingle View Appで作る。

f:id:hatakeka:20180129180324p:plain

modelフォルダを作成して、samp04.swift,GameSene4.swiftを新規作成、他のファイルはいじらない。

 Assets.xcassetsにアイコン配置。

f:id:hatakeka:20180129180227p:plain

 

アイコンは以下を利用した。

f:id:hatakeka:20180129174522p:plain

(他のタイルについては、面倒なので用意していない。)

 

 

 

Mainstory.board、画面の上の左側をクリックして

custam classにsamp04(この画面で実行するクラス)を指定する。

f:id:hatakeka:20180129190835p:plain

 

 

samp04.swift

//
//  samp04.swift
//  Pazz
//
//  Created by su5file on 2018/01/29.
//  Copyright © 2018年 su5file. All rights reserved.
//
    
import UIKit
import SpriteKit

let _w = UIScreen.main.bounds.size.width
let _h = UIScreen.main.bounds.size.height

class samp04: UIViewController {

    var skView : SKView?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let sv = SKView(frame: CGRect(x:0, y:0,width:_w,height:_h))
        let ss = GameScene4(size: sv.frame.size)
        
        sv.presentScene(ss)
        view.addSubview(sv)
    }

    
    open override var shouldAutorotate: Bool {
        return true
    }
    open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
        if UIDevice.current.userInterfaceIdiom == .phone {
            return UIInterfaceOrientationMask(rawValue: UIInterfaceOrientationMask.RawValue(Int(UIInterfaceOrientationMask.allButUpsideDown.rawValue)));UIInterfaceOrientationMask.allButUpsideDown
        } else {
            return UIInterfaceOrientationMask(rawValue: UIInterfaceOrientationMask.RawValue(Int(UIInterfaceOrientationMask.all.rawValue)));UIInterfaceOrientationMask.all
        }
    }
    

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Release any cached data, images, etc that aren't in use.
    }
    // 追加
    override var prefersStatusBarHidden: Bool {
        return true
    }
}

GameScene4.swift

//
//  GameSene4.swift
//  Pazz
//
//  Created by su5file on 2018/01/29.
//  Copyright © 2018年 su5file. All rights reserved.
//
import UIKit
import SpriteKit

let NumColumns = 6
let NumRows = 10
let TileSize:CGFloat = 50

let BoardLayerPosition = CGPoint(x:20, y:-80)
let TextFieldPosition = CGPoint(x:20, y:-20)

class GameScene4: SKScene {
    
    var board = SKSpriteNode()
    let boardLayer = SKNode()
    let shapeLayer = SKNode()
    
    let textLayer = SKNode()
    let strLayer = SKNode()
    
    let score = SKLabelNode()
    
    var tileArrayPos = Array(repeating: Array(repeating: CGPoint(), count: NumRows), count: NumColumns)
    var touchedNode = SKNode()
    var moveActionFlag = false
    var scorePoint = 0
    
    override init(size:CGSize){
        super.init(size: size)
        
        backgroundColor = UIColor.gray
        anchorPoint = CGPoint(x:0, y:1.0)
        
        addChild(boardLayer)
        addChild(textLayer)
        
        board = SKSpriteNode(color:UIColor.white,size:CGSize(width:CGFloat(NumColumns)*TileSize, height:CGFloat(NumRows)*TileSize))
        board.name = "board"
        board.anchorPoint = CGPoint(x:0, y:1.0)
        board.position = BoardLayerPosition
        
        let textfield = SKSpriteNode(color:UIColor.white,size:CGSize(width:CGFloat(NumColumns)*TileSize,height: 80))
        textfield.position = TextFieldPosition
        textfield.anchorPoint = CGPoint(x:0, y:1.0)
        
        score.fontColor = UIColor.black
        score.position = CGPoint(x:textfield.position.x*10, y:textfield.position.y-30)
        textfield.addChild(score)
        
        strLayer.position = TextFieldPosition
        strLayer.addChild(textfield)
        textLayer.addChild(strLayer)
        
        shapeLayer.position = BoardLayerPosition
        shapeLayer.addChild(board)
        boardLayer.addChild(shapeLayer)
        
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    override func didMove(to view: SKView) {
        /* Setup your scene here */
        initMakeTile()

    }
    
    //タイルの初期化
    func randomColor()->UIColor{
        //0:red,1:green,2:blue,3:yellow
        let rnd = arc4random()%4
        var color:UIColor!
        switch rnd{
        case 0:
            color = UIColor.red
        case 1:
            color = UIColor.green
        case 2:
            color = UIColor.blue
        case 3:
            color = UIColor.yellow
        default:
            break
        }
        return color
    }
    
    
    //タイルの初期化
    func initMakeTile(){
        
        for  i in 0..<NumColumns{
            for  j in 0..<NumRows{
                let sprite = makeTileOne()
                sprite.position = CGPoint(x:CGFloat(i)*TileSize,y:-CGFloat(j)*TileSize)
                tileArrayPos[i][j] = sprite.position
                
                board.addChild(sprite)
            }
        }
        
    }
    
    
    //タイルの初期化
    func makeTileOne()->SKSpriteNode{
        let sprite = SKSpriteNode()
        
        sprite.anchorPoint = CGPoint(x:0, y:1.0)
        sprite.alpha *= 0.8
        sprite.color = randomColor()
        sprite.size = CGSize(width:TileSize-1, height:TileSize-1)
        
        if sprite.color == .red {
            // 画像を変更
            sprite.texture = SKTexture(imageNamed: "icons8cat.png")
        }
        
        return sprite
    }
    
    //タイルの移動
    func moveTile(node:SKNode){
        let touchedAction = SKAction.move(to: node.position, duration: 0.08)
        let passeagedAction = SKAction.move(to: touchedNode.position, duration: 0.08)
        
        moveActionFlag = true
        touchedNode.run(touchedAction, completion: {self.moveActionFlag = false})
        node.run(passeagedAction, completion: {self.moveActionFlag = false})
    }
    
    func searchNode(pos:CGPoint)->SKSpriteNode{
        let node = self.atPoint(CGPoint(x:pos.x+TileSize,y:pos.y-4*TileSize))
        return node as! SKSpriteNode
    }
    
    
    func searchEmpty(node:SKSpriteNode)-> Int{
        
        var count = 0
        if(node.name != "board"){
              for k in Int(-1*node.position.y/TileSize)..<NumRows{
                let sprite = searchNode(pos: tileArrayPos[Int(node.position.x/TileSize)][k])
                if(sprite.name == "board"){
                    count+=1
                }
            }
        }
        
        return count
    }
    
    func insertArray( arrayA:inout [SKNode],arrayB:[SKNode]){
        for node in arrayB{
            arrayA.append(node)
        }
    }
    
    func deleteColumns( array:inout [SKNode]){
        
        var deleteColumnsArray = Array(arrayLiteral:SKNode())
        
          for i in 0..<NumColumns{
            var color = searchNode(pos: tileArrayPos[i][0]).color
            var count = 0
            
            for j in 0..<NumRows{
                let node = searchNode(pos: tileArrayPos[i][j])
                if(color == node.color){
                    deleteColumnsArray.append(node)
                    count+=1

                }else{
                    if(count >= 3){
                        insertArray(arrayA: &array, arrayB: deleteColumnsArray)
                    }
                    deleteColumnsArray.removeAll()
                    deleteColumnsArray.append(node)
                    color = node.color
                    count = 1
                }
                
            }
            
            if(count >= 3){
                insertArray(arrayA: &array, arrayB: deleteColumnsArray)
            }
            deleteColumnsArray.removeAll()
        }
    }
    
    func deleteRows( array:inout [SKNode]){
        
        var deleteRowsArray = Array(arrayLiteral:SKNode())
        
          for  j in 0..<NumRows{
            var color = searchNode(pos: tileArrayPos[0][j]).color
            var count = 0
            
            for i in 0..<NumColumns{
                let node = searchNode(pos: tileArrayPos[i][j])
                if(color == node.color){
                    deleteRowsArray.append(node)
                    count+=1
                    
                }else{
                    if(count >= 3){
                        insertArray(arrayA: &array, arrayB: deleteRowsArray)
                    }
                    deleteRowsArray.removeAll()
                    deleteRowsArray.append(node)
                    color = node.color
                    count = 1
                }
                
            }
            
            if(count >= 3){
                insertArray(arrayA: &array, arrayB: deleteRowsArray)
            }
            deleteRowsArray.removeAll()
        }
    }
    //タイルの消去と落ちる処理
    func dropTile(completion:@escaping ()->()){
        
          for i in 0..<NumColumns{
              for j in 0..<NumRows{
                let k = searchEmpty(node: searchNode(pos: tileArrayPos[i][j]))
                if(k != 0){
                    let sprite = searchNode(pos: tileArrayPos[i][j])
                    let action = SKAction.move(to: tileArrayPos[i][j+k], duration: 0.1)
                    sprite.run(action)
                }
            }
        }
        
        run(SKAction.wait(forDuration: 0.2), completion: completion)
    }
    
    func makeTileInEmpty(completion:@escaping ()->()){
        
        var emptyList = Array(repeating:0, count:NumColumns)
         for i in 0..<NumColumns{
            var count = 0
                for j in 0..<NumRows{
                    if(searchNode(pos: tileArrayPos[i][j]).name == "board"){
                    count+=1
                    scorePoint += 100
                }
            }
            emptyList[i] = count
        }
        var i = 0
          for k in emptyList{
            for j in  0..<k{
                let sprite = makeTileOne()
                board.addChild(sprite)
                let action = SKAction.move(to: tileArrayPos[i][j], duration: 0.1)
                sprite.run(action)
            }
            i+=1
        }
        run(SKAction.wait(forDuration: 0.2), completion: completion)
    }
    
    //タイルの消去と落ちる処理
    func deleteAndDropTile(){

        var deleteTileArray = Array(arrayLiteral:SKNode())
        deleteTileArray.removeAll()
        deleteColumns(array: &deleteTileArray)
        deleteRows(array: &deleteTileArray)
        
        if(deleteTileArray.isEmpty){
            moveActionFlag = false
            return
        }
        
        board.removeChildren(in: deleteTileArray)
        
        dropTile(){
            self.makeTileInEmpty(){
                self.deleteAndDropTile()
            }
        }
  
    }
    
    //タッチ操作
    override func touchesBegan(_ touches: Set, with event: UIEvent?) {
        /* Called when a touch begins */
        
        for touch in touches {
            let location = touch.location(in: self)
            touchedNode = self.atPoint(location)
            for node in self.board.children{
                if(touchedNode == node as NSObject && !moveActionFlag){
                    touchedNode.alpha *= 0.5
                }
            }

        }
    }
    
    override func touchesMoved(_ touches: Set, with event: UIEvent?) {
        for touch in touches {
            let location = touch.location(in: self)
            let passagedNode = self.atPoint(location)
            for node in self.board.children{
                if(passagedNode == node as NSObject && touchedNode != passagedNode && !moveActionFlag){
                    moveTile(node: passagedNode)
                }
            }
        }
    }
    
    override func touchesEnded(_ touches: Set, with event: UIEvent?) {
        for touch in touches {
            let location = touch.location(in: self)
            let endedNode = self.atPoint(location)
            for node in self.board.children{
                if(endedNode == node as NSObject && !moveActionFlag){
                    moveActionFlag = true
                    deleteAndDropTile()
                    touchedNode.alpha *= 2
                }
            }
        }
    }
   
    override func update(_ currentTime: CFTimeInterval) {
        /* Called before each frame is rendered */
        score.text = "Score : \(scorePoint)"
    }
}

 

 

 

ちなみに、わしのパズドラのランクは379(´Д` )だす。

 

 以上。

 

 

参考URL

https://qiita.com/konpeto000/items/0003db7e561fb03a4c8a