最初のiPhoneゲームをゼロから作成します。コーディングの経験は不要です。

前書き

このチュートリアルは、Swift 4.0 / XCode 9用に作成されたものであり、今後のバージョンでは、記事を更新するまで問題が発生する可能性があります。

あなたはモバイルゲームの構築に興味がありますか?それは、新しい車を離陸して購入することの魅力かもしれませんし、単にあなた自身のゲームを作成することへの情熱かもしれません。目的が何であれ、このチュートリアルでは、コンピューターをセットアップしてアプリを構築し、簡単なゲームをゼロから作成する手順を説明します!すべてのゲームコードはチュートリアルに含まれています。このプロジェクトを完了するためにプログラミングの予備知識は必要ありません。

このチュートリアルを完了すると、デバイスまたはデバイスシミュレータで独自の構成のIOSアプリを実行できるようになります。ゲームをゼロから設計する基本、アプリを閉じた後にデバイスにデータを保存する方法、画面にスプライトをレンダリングする方法を理解し、SpriteKitゲームエンジンの使用方法を理解し始めます。また、Snakeゲームの設計方法と、独自のデザインのゲームの構築を開始する方法についても説明します。

App Storeで入手できるこのゲームのわずかに変更されたバージョンへのダウンロードリンクを次に示します。https://itunes.apple.com/us/app/minimal-snake/id1355406338?mt = 8(広告なしで完全無料)。

注:記事全体を通して、コピー/ペーストという用語を使用しましたが、読者はこれが悪い習慣であると指摘し、完全に同意します。ただし、コードをすばやくコピーして機能する製品を作成したい場合は、手作業で各行を書き留めることでより多くのことが得られるでしょう。

完成品

これが、このチュートリアルの最後までに携帯電話に構築してインストールするものを示すビデオです!

入門

このチュートリアルを実行するには、Apple開発者アカウントを設定し、Xcode(IOSアプリのビルドに使用されるプログラム)をダウンロードする必要があります。残念ながら、XcodeはMacでのみ利用可能です。 Windows / Linuxマシンをお持ちの場合、ここにXcodeのセットアップに役立つWebサイトがあります。

次の手順では、無料の開発者アカウントに登録してXcodeをインストールします。すでにアカウントとXcodeを持っている場合は、次のセクションにスキップできます。開始するには、まずdeveloper.apple.comにアクセスしてメンバーセンターをクリックし、Apple IDでサインインします。 Apple Developer Agreementページにアクセスして、契約に同意します。これで無料の開発者アカウントができました!プロジェクトをアプリストアにアップロードするには、100ドルの年会費を支払う必要があります。

開発者アカウントを取得したら、Xcodeをインストールする必要があります。 XcodeはMac App Storeから入手できます。 Xcodeをインストールした後、プログラムを起動し、Xcode-> Preferences-> Accounts-> +をクリックして、Add Apple IDを選択します。開発者アカウントの登録に使用したApple IDでログインします。おめでとうございます。iPhoneシミュレーターでアプリをテストしたり、個人のデバイスで実行したりできます。

プロジェクトの開始

開発者アカウントに登録してXcodeをインストールしたら、最初のモバイルゲームの開発を開始できます!

Xcodeを起動し、「新しいXcodeプロジェクトを作成」をクリックします。

「ゲーム」テンプレートをクリックします。

ゲームの「Snake」(または好きな名前)を入力します。組織名を選択します。Webサイトを持っている場合は、それを逆方向に入力できます(com.gavinshrader)。または、単に名前として名前を識別子として使用できます。言語が「Swift」に設定され、ゲーム技術が「SpriteKit」であることを確認してください。 3つのチェックボックスがオンになっている場合はオフにします。

「Actions.sks」を右クリックしてゴミ箱に移動します。 GameScene.sksに移動し、「Hello World」テキストをクリックして削除します。 GameScene.swiftに移動して、ファイルが下の画像と一致するように、すべての事前作成されたコードを削除します。

[ファイル]-> [新規ファイル]に移動して[Swiftファイル]をクリックするか、プロジェクトフォルダー(「Snake」)を右クリックして新しいファイルを選択することにより、新しいSwiftファイルを作成します。下に強調表示されているSwift Fileアイコンを見つけます。存在しない場合は、フィルターバーに「Swift」と入力します。 「GameManager」という名前を入力し、プロジェクト(「Snake」)がターゲットの下で選択されていることを確認し、「作成」をクリックして新しいSwiftファイルを作成します。

ゲームのメニューを作成する

コーディングを開始する前に、最後のセクションで行った変更後にプロジェクトがコンパイルされることを確認してください。シミュレーターのリストからデバイスを選択し、「iPhone 6」があるボタンをクリックします。「Generic iOS device」というラベルが付けられます。物理デバイスでテストしてiPhoneを接続する場合は、Xcodeをしばらく待ってからデバイスをクリックします。完了したら、三角形の実行ボタンをクリックします。シミュレートされたデバイスを選択した場合、この画面が表示されます:

画面に「Hello World」と表示されている場合は、GameScene.sksに移動し、ラベルをクリックしてから削除を選択して、ラベルを削除したことを確認してください。

ゲームの構築を開始する準備ができました!ゲームを開始するときは、事前に画面をレイアウトしておくと役立ちます。このゲームでは、ゲームのタイトル/ロゴを表示するシンプルなメニュー画面から始めます。プレイボタンは、ゲームエリアと、現在のスコアとベストスコアの2つのラベルを含むゲームプレイ画面を起動します。あなたが死ぬと、ゲーム終了画面が表示され、再びプレイするオプションが表示されます。

ゲームを実行するには、最初にメニューを作成してゲームを開始する必要があります。まず、ゲームタイトル、「ベストスコア」ラベル、および再生ボタンを追加してメニューを初期化するコードを作成します。 GameScene.swiftファイルを開き、下のすべてのコードをコピーして、ファイルが画像と一致するようにします(図A)。

// 1
var gameLogo:SKLabelNode!
var bestScore:SKLabelNode!
var playButton:SKShapeNode!
// 2
initializeMenu()
// 3
private func initializeMenu(){
    //ゲームタイトルを作成します
    gameLogo = SKLabelNode(fontNamed: "ArialRoundedMTBold")
    gameLogo.zPosition = 1
    gameLogo.position = CGPoint(x:0、y:(frame.size.height / 2)-200)
    gameLogo.fontSize = 60
    gameLogo.text = "SNAKE"
    gameLogo.fontColor = SKColor.red
    self.addChild(gameLogo)
    //最高スコアのラベルを作成
    bestScore = SKLabelNode(fontNamed: "ArialRoundedMTBold")
    bestScore.zPosition = 1
    bestScore.position = CGPoint(x:0、y:gameLogo.position.y-50)
    bestScore.fontSize = 40
    bestScore.text = "ベストスコア:0"
    bestScore.fontColor = SKColor.white
    self.addChild(bestScore)
    //再生ボタンを作成
    playButton = SKShapeNode()
    playButton.name = "play_button"
    playButton.zPosition = 1
    playButton.position = CGPoint(x:0、y:(frame.size.height / -2)+ 200)
    playButton.fillColor = SKColor.cyan
    let topCorner = CGPoint(x:-50、y:50)
    let bottomCorner = CGPoint(x:-50、y:-50)
    let middle = CGPoint(x:50、y:0)
    let path = CGMutablePath()
    path.addLine(to:topCorner)
    path.addLines(between:[topCorner、bottomCorner、middle])
    playButton.path =パス
    self.addChild(playButton)
}
図A

コードをコンパイルし、デバイスが上からの画像を表示していることを確認します。ここで何が行われているのかを説明します。これはコードの壁のように見えるかもしれませんが、分解すると簡単に理解できます。

  • 1:ロゴ/ボタンの変数を作成します。変数名の後の「!」は、変数を初期化する必要があることを意味します。空または「nil」にすることはできません。
  • 2:ゲームビューが読み込まれたら、「initializeMenu()」関数を呼び出します。 didMove(to:view:SKView)は、GameSceneがロードされると呼び出される関数です。
  • 3:これは、メニューオブジェクトを作成するために作成したintializeMenu()関数です。
  • 4/5/6:「self.addChild()」を呼び出してオブジェクトを作成し、GameSceneに追加します。
  • 7:このプロジェクトではSKShapeNodesを使用することを選択しましたが、これは単純であるためです。これは、画像エディターでグラフィックを作成する代わりになります。このコード行は、三角形の形のパスを作成します。アプリの構築と公開を計画している場合、SKSpriteNodesを使用して作成した画像を読み込む必要があります。ShapeNodeはフレームごとに動的に描画されるため、大量に使用するとパフォーマンスの問題が発生する可能性があります。
  • 8:作成した三角形のパスをplayButtonスプライトに設定し、GameSceneに追加します。

ゲームをする

シンプルなメニュー設定ができたので、再生ボタンを機能させましょう。まずGameManager.swiftファイルに移動し、以下の画像に一致するようにすべてのコードをこれに置き換えます(図B)。

SpriteKitをインポートする
クラスGameManager {
}
図B

下のコードをGameScene.swiftファイルにコピーして、下の画像と一致するようにします(図C)。

// 1
var game:GameManager!
// 2
ゲーム= GameManager()
// 3
func touchesBegan(_ touches:Set 、イベント:UIEvent?)をオーバーライドします{
    タッチインタッチの場合{
        let = touch.location(in:self)
        letededNode = self.nodes(at:location)
        touchedNode内のノード{
            node.name == "play_button" {
                ゲームを始める()
            }
        }
    }
}
// 4
private func startGame(){
    print( "ゲームを開始")
}
図C
  • 1:GameManagerオブジェクトを初期化します。これについては後で詳しく説明します。これはスコアデータを保持し、プレーヤーの動きを管理します。
  • 2:ゲーム変数を新しいGameManager()オブジェクトに設定します。
  • 3:この関数は、ユーザーが画面に触れるたびにゲームエンジンによって呼び出されます。前に作成した再生ボタンの名前は「play_button」であることを思い出してください。名前を使用して、ユーザーが「play_button」という名前のSpriteNodeに触れたかどうかを確認できます。これが発生したら、弾丸ポイント4からstartGame()関数を呼び出します。
  • 4:この関数はゲームを開始します。

アプリを実行し、三角形の再生ボタンをクリックして、コードが適切に機能することを確認します。タッチが適切に測定されている場合、下の画像に示すように、コンソールに「ゲーム開始」が表示されます(図D)。

図D

コンソールが表示されていない場合は、トップバーに移動して[ヘルプ]をクリックし、検索バーに「コンソール」と入力して、[デバッグ領域]> [コンソールのアクティブ化]をクリックします。これで、機能するメニューシステムと再生ボタンができました。ここから、本当に楽しみ始めることができます。

ゲームビューの読み込み

関数をトリガーできる再生ボタンができました。どうすればよいですか?ゲームビューを表示するには、まずメニューボタンを非表示にする必要があります。このコード行を追加して、シンプルなアニメーションでメニューボタンを非表示にします。コードは下の画像と一致するはずです(図E)。

//ゲームを始める
private func startGame(){
    print( "ゲームを開始")
    // 1
    gameLogo.run(SKAction.move(by:CGVector(dx:-50、dy:600)、duration:0.5)){
    self.gameLogo.isHidden = true
    }
    // 2
    playButton.run(SKAction.scale(to:0、duration:0.3)){
        self.playButton.isHidden = true
    }
    // 3
    let bottomCorner = CGPoint(x:0、y:(frame.size.height / -2)+ 20)
    bestScore.run(SKAction.move(to:bottomCorner、duration:0.4))
}
図E図F
  • 1:gameLogoを画面から移動して、表示から非表示にします。アクションが完了すると、SKActionが実行された後の括弧。たとえば、期間10のSKActionを実行すると、ブラケット内のコードは10秒後に実行されます。以下に例を示します。
exampleNode.run(SKAction.move(by:CGVector(dx:0、dy:0)、duration:10){
    print( "10秒後に到達しました")
}
  • 2:playButtonを0にスケーリングします。このアクションにより、ボタンが縮小され、表示されなくなります。
  • 3:bestScoreラベルを画面の下部に移動します。

再生ボタンをクリックすると、メニューはこのgif(図F)のようになります!

次に、このゲームの実際の「蛇」部分の設計を開始します。まず、これらのコード行を追加して、コードが下の画像と一致するようにします(図G)。

// 1
var currentScore:SKLabelNode!
var playerPositions:[(Int、Int)] = []
var gameBG:SKShapeNode!
var gameArray:[(ノード:SKShapeNode、x:Int、y:Int)] = []
// 2
initializeGameView()
// 3
private func initializeGameView(){
    // 4
    currentScore = SKLabelNode(fontNamed: "ArialRoundedMTBold")
    currentScore.zPosition = 1
    currentScore.position = CGPoint(x:0、y:(frame.size.height / -2)+ 60)
    currentScore.fontSize = 40
    currentScore.isHidden = true
    currentScore.text = "スコア:0"
    currentScore.fontColor = SKColor.white
    self.addChild(currentScore)
    // 5
    let width = frame.size.width-200
    let height = frame.size.height-300
    let rect = CGRect(x:-width / 2、y:-height / 2、幅:幅、高さ:高さ)
    gameBG = SKShapeNode(rect:rect、cornerRadius:0.02)
    gameBG.fillColor = SKColor.darkGray
    gameBG.zPosition = 2
    gameBG.isHidden = true
    self.addChild(gameBG)
    // 6
    createGameBoard(幅:幅、高さ:高さ)
}
図G図G
  • 1:新しい変数!現在のスコア、「ヘビ」またはプレイヤーが現在持っているすべての位置の配列、ゲームビューの背景、およびゲームビュー内の各セルの位置を追跡する配列を示すラベルを作成しています。
  • 2:initializeGameView()関数を呼び出します。
  • 3:ゲームビューを初期化します。
  • 4:現在のスコアラベルを画面に追加します。これはメニューを終了するまで非表示です。
  • 5:ゲームのプレイ可能な領域を表すShapeNodeを作成します。これは、ヘビが動き回る場所です。
  • 6:ゲームボードを作成します。この関数は、大量の正方形セルを初期化し、ゲームボードに追加します。

次に、画面上の蛇と点をレンダリングするために使用するセルの配列を作成します。図Hに一致するように、以下のコードからcreateGameBoard関数を作成します。

//ゲームボードを作成し、セルの配列を初期化します
private func createGameBoard(width:Int、height:Int){
    let cellWidth:CGFloat = 27.5
    numRows = 40とする
    numCols = 20とする
    var x = CGFloat(width / -2)+(cellWidth / 2)
    var y = CGFloat(height / 2)-(cellWidth / 2)
    //行と列をループし、セルを作成します
    for i in 0 ... numRows-1 {
        0 ... numColsのj-1 {
            let cellNode = SKShapeNode(rectOf:CGSize(width:cellWidth、height:cellWidth))
            cellNode.strokeColor = SKColor.black
            cellNode.zPosition = 2
            cellNode.position = CGPoint(x:x、y:y)
            //セルの配列に追加-次にゲームボードに追加
            gameArray.append((node:cellNode、x:i、y:j))
            gameBG.addChild(cellNode)
            // xを繰り返す
            x + = cellWidth
        }
        // xをリセットし、yを繰り返します
        x = CGFloat(width / -2)+(cellWidth / 2)
        y-= cellWidth
    }
}
図H

コードは上記のコードと一致する必要があります。ゲームを実行しても、何も変わっていないように見えます。上記のシミュレータのスクリーンショットのようにゲームボードを表示する場合は、図Iに一致するように、次のコードをゲーム開始関数に追加します。

bestScore.run(SKAction.move(to:bottomCorner、duration:0.4)){
    self.gameBG.setScale(0)
self.currentScore.setScale(0)
self.gameBG.isHidden = false
self.currentScore.isHidden = false
self.gameBG.run(SKAction.scale(to:1、duration:0.4))
self.currentScore.run(SKAction.scale(to:1、duration:0.4))
}

先に進む前のcreateGameBoardメソッドの簡単な説明。このメソッドは40行20列をループし、各行/列の位置に対して新しい正方形のボックスまたは「cellNode」を作成し、これをシーンに追加します。また、このcellNodeを配列「gameArray」に追加して、行と列を適切なセルに簡単にピンポイントできるようにします。

図I —新しいゲームボードを表示します!

ゲームインスタンスの作成

現在、有効な再生ボタン、小さなボックスでいっぱいのボックス、いくつかのラベルがあります。これを実際にプレイするのが楽しいゲームにするにはどうすればよいですか?最初に、画面上の「蛇」の位置を追跡するオブジェクトが必要になります。 GameManager.swiftクラスを開き、次のメソッドを作成します。また、この変更(// 1)をGameScene.swiftのdidMove(to view:SKView)関数に追加して、コードが図Jに一致するようにします。

// 1-GameScene.swift
game = GameManager(scene:self)
// 2-GameManager.swift
クラスGameManager {
    
    var scene:GameScene!
    init(scene:GameScene){
        self.scene = scene
    }
}
図J

これらの変更を行うことで、GameManagerが初期化されたらGameSceneクラスへの参照をGameManagerに含める必要があると言っています。これで、GameManagerクラスはscene.method_nameを呼び出すことでGameSceneと通信できます。たとえば、scene.startGame()はGameManagerクラスのコントロール内からゲーム開始機能を実行します。

これで、プレーヤーをGameViewにロードする準備ができました。まず、bestScore.run(){の括弧内のstartGame()メソッドでGameScene.swiftファイルに次のコードスニペットを追加します。このメソッドは、bestScoreラベルがSKActionを終了するとinitGame関数を呼び出します。

//新しいコード
self.game.initGame()
図K

ここで、GameManager.swiftに移動し、init(scene:GameScene)メソッドの下に次のメソッドを追加して、コードが図Lに一致するようにします。

// 1
func initGame(){
    //プレーヤーの位置を開始
    scene.playerPositions.append((10、10))
    scene.playerPositions.append((10、11))
    scene.playerPositions.append((10、12))
    renderChange()
}
// 2
func renderChange(){
    scene.gameArrayの(node、x、y){
        if contains(a:scene.playerPositions、v:(x、y)){
            node.fillColor = SKColor.cyan
        } else {
            node.fillColor = SKColor.clear
        }
    }
}
// 3
func contains(a:[(Int、Int)]、v:(Int、Int))-> Bool {
    let(c1、c2)= v
    for(v1、v2)in {if v1 == c1 && v2 == c2 {return true}}
    偽を返す
}
図L
  • 1:initGame()関数。これにより、GameSceneのplayerPositions配列に3つの座標が追加され、
  • 2:renderChange()関数。 「ヘビ」またはプレイヤーを移動するたびにこのメソッドを呼び出します。これにより、すべての空白の正方形が透明になり、プレーヤーが配置されているすべての正方形がシアンになります。
  • 3:これは、タプル((Int、CGFloat、Int、String)などの形式で型の組み合わせを含むことができる迅速なデータ構造)がタプルの配列に存在するかどうかをチェックする単純な関数です。この関数は、playerPositions配列にGameSceneのセル配列から入力された座標が含まれているかどうかを確認します。各更新中にすべてのセルをチェックするため、これは必ずしも最も効率的な方法ではありません。自分で挑戦したい場合は、playerPositions配列の正方形のみを変更するようにコードを更新してください!

プレーヤーを移動する

図M

これでプレーヤーが画面にレンダリングされ、任意の数のポジションをレンダリングできるようになりました。 playerPositions配列に座標を追加すると、より多くの正方形がシアン色になります。ゲーム中、プレイヤーが画面をスワイプして方向を変えるまで、「ヘビ」を常に一方向に動かしたいと考えています。グリッドシステムの座標を示すグリッドは次のとおりです。これにより、座標が舞台裏でどのように機能するかを簡単に理解できます(図M)。

ひどく小さなラベルでわかるように、左上隅は0,0で、右下隅は39,19です。これは、プレイヤーを左、右、上、下の方向に移動したい場合、次の基本的な代数を適用することで実行することを意味します(図N)。

図N

ご覧のとおり、左右の方向は一般的な座標平面の方向と一致しています。左は負、右は正です。ただし、座標平面で上に移動するにはyを減らし、下に移動するにはyを増やします。これは、createGameBoard関数のforループが先頭から開始されて機能しなくなったためです。

ボードの方向を理解したので、プレーヤーを動かすメソッドを実装できます。 GameScene.swiftファイルを開くと、update(_ currentTime:TimeInterval)という便利なメソッドがあります。レンダリングループでは、更新関数は1秒に1回呼び出されます。つまり、アプリが60 fpsで実行されている場合、関数は1秒間に60回呼び出され、ゲームが40 fpsで実行されている場合、1秒間に40回しか呼び出されません。更新関数内に、このコード行を追加して、コードが図Oに一致するようにします。

// 1
game.update(time:currentTime)
図O

このコードを追加すると、赤いエラーがポップアップします。これを修正するには、GameManager.swiftファイルに移動し、これらのコード行を追加して、ファイルが図Pに一致するようにします。

// 1
var nextTime:Double?
var timeExtension:Double = 1
// 2
func update(time:Double){
    nextTime == nil {
        nextTime = time + timeExtension
    } else {
        time> = nextTime!の場合{
            nextTime = time + timeExtension
            印刷(時間)
        }
    }
}
図P

アプリを実行すると、コンソールは毎秒新しい時間を出力します。以下に、このコードが実行していることの概要を示します。

  • 1:2つの新しい変数を初期化します。 nextTimeは、ステートメントをコンソールに出力するnextTime間隔です。timeExtensionは、各印刷の間に待機する時間(1秒)です。
  • 2:この更新関数は毎秒60回呼び出されます。プレーヤーの位置を毎秒1回だけ更新して、ゲームが途方もなく高速にならないようにします。これを実現するために、nextTimeが設定されているかどうかを確認します。 // 1からわかるように、nextTimeはオプションの値に初期化されています。 「? 「Doubleが、nextTimeをdoubleにしたいこと、nilに設定できることを迅速なコンパイラに伝えます。更新関数が呼び出されると、まずnextTimeが設定されているかどうかを確認し、設定されていない場合は、現在の時間+ timeExtension(1秒)に設定します。現在の時刻が「nextTime」を超えると、nextTimeが1秒増加します。この関数は現在、不規則な更新関数(約30〜60回/秒)を受け取り、1秒に1回だけ出力を生成します。

ゲームの速度を0より大きい値に単純に下げる場合、ゲームを遅くしたい場合はtimeExtensionの値を増やす場合、1秒に1回実行する関数があります。 (注:timeExtensionの場合、「1」== 1秒)。

プレーヤーを画面上で移動し、次のコードを追加して、ファイルが図Qに一致するようにします。また、GameManager.swiftで作成した更新関数から「print(time)」行を削除します。コンソールであり、コードの妥当性をテストするためにのみ本当に役立ちました。

// 1
var playerDirection:Int = 1
// 2
updatePlayerPosition()
// 3
private func updatePlayerPosition(){
    // 4
    var xChange = -1
    var yChange = 0
    // 5
    playerDirectionの切り替え{
        ケース1:
            //左
            xChange = -1
            yChange = 0
            ブレーク
        ケース2:
            //アップ
            xChange = 0
            yChange = -1
            ブレーク
        ケース3:
            //右
            xChange = 1
            yChange = 0
            ブレーク
        ケース4:
            //ダウン
            xChange = 0
            yChange = 1
            ブレーク
        デフォルト:
            ブレーク
    }
    // 6
    scene.playerPositions.count> 0 {
        var start = scene.playerPositions.count-1
        開始時> 0 {
            scene.playerPositions [start] = scene.playerPositions [start-1]
            開始-= 1
        }
        scene.playerPositions [0] =(scene.playerPositions [0] .0 + yChange、scene.playerPositions [0] .1 + xChange)
    }
    //7
    renderChange()
}
図Q

このコードを追加すると、ゲームは図Qのgifのようになります(playerDirectionを4に設定して同じ移動方向を取得します)。これを書いたとき、2つのことがすぐに目立った。まず、ヘビはゆっくりと痛みを伴いながら移動します。おそらく、ゲームの速度を1秒から1/2または1/4秒に上げる必要があります。第二に、ヘビが壁にぶつかったときに何をしますか?ヘビのいくつかのバージョンでは、プレイヤーは画面の周りをワープします。他のバージョンでは、壁との衝突により死に至ります。私はスクリーンワープの外観が好きなので、このゲームではこの方法を使用すると思います。今、あなたが書いたこのコードの説明:

  • 1:プレーヤーの現在の方向を決定するために使用される変数を作成します。コードでは、変数は1に設定されています。図Qのgifでは、方向を4に設定しています。この変数を変更して、すべての異なる方向を確認します。
  • 2:print(time)を削除し、updatePlayerPosition()の呼び出しに置き換えました。この反復では、毎秒更新を呼び出しています。
  • 3:このメソッドは、プレーヤーまたは「蛇」を画面上で移動します。
  • 4:変数を設定して、蛇の正面のx / yに加える変更を決定します。
  • 5:これはswitchステートメントです。playerPositionの入力を受け取り、プレーヤーが上下左右に移動しているかどうかに応じてx / y変数を変更します。
  • 6:このコードブロックは、配列内の位置を前方に移動します。テールの前面を適切な方向に移動してから、すべてのテールブロックを次の位置に前方に移動します。
  • 7:位置の配列に加えた変更をレンダリングします。

画面の周りで蛇をゆがめる

私たちは今、動くプレーヤー、素晴らしい仕事をしています!まず、ゲームの速度を上げたいと思っています。1秒間待つのは遅すぎて面白くありません。これはゲームデザインで学ぶレッスンであり、ゲームの雰囲気を完璧にするために微調整と小さな変更を数多く行う必要があります。私がプロジェクトに取り組むときは、ほとんどの時間を微調整して感覚を調整することがよくあります。楽しいゲームを構築するには、メカニックを完璧にする必要があります。完璧なメカニックが得られたら、パーティクルやサウンドなどの派手な追加機能を追加できます。

timeExtension変数を0.15に変更し、プロジェクトをコンパイルします。

// 1-GameManager.swift
var timeExtension:Double = 0.15

これで、画面の周りで蛇を歪め始めることができます。プロジェクトが図Rに一致するように次のコードを追加します。このコードは、先ほど書いたGameManager.swiftのupdatePlayerPosition()関数に追加されます。

// 1
scene.playerPositions.count> 0 {
    let x = scene.playerPositions [0] .1
    let y = scene.playerPositions [0] .0
    y> 40の場合{
        scene.playerPositions [0] .0 = 0
    } else if y <0 {
        scene.playerPositions [0] .0 = 40
    } else if x> 20 {
       scene.playerPositions [0] .1 = 0
    } else if x <0 {
        scene.playerPositions [0] .1 = 20
    }
}
図R

アプリをコンパイルすると、画面は図Rのgifと一致するはずです。gifでplayerDirection 4を使用しました。これで、ヘビは画面の各辺でゆがむことができます。

  • 1:このコードは非常に単純で、ヘビの頭の位置が上、下、左側、または右側を通過したかどうかを確認し、プレーヤーを画面の反対側に移動します。

スワイプジェスチャを使用してヘビの動きを制御する

私たちのゲームは近づいてきており、ヘビの方向を制御する方法が必要です。これを実装するには、スワイプジェスチャを使用して、左、右、上下に移動します。このコードをGameScene.swiftファイルに追加して、図Sに一致するようにします。

// 1
let swipeRight:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target:self、action:#selector(swipeR))
swipeRight.direction = .right
view.addGestureRecognizer(swipeRight)
let swipeLeft:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target:self、action:#selector(swipeL))
swipeLeft.direction = .left
view.addGestureRecognizer(swipeLeft)
let swipeUp:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target:self、action:#selector(swipeU))
swipeUp.direction = .up
view.addGestureRecognizer(swipeUp)
let swipeDown:UISwipeGestureRecognizer = UISwipeGestureRecognizer(target:self、action:#selector(swipeD))
swipeDown.direction = .down
view.addGestureRecognizer(swipeDown)
// 2
@objc func swipeR(){
    print( "r")
}
@objc func swipeL(){
    print( "l")
}
@objc func swipeU(){
    print( "u")
}
@objc func swipeD(){
    print( "d")
}
図S
  • 1:スワイプジェスチャーをdidMove(to view:SKView)関数に追加します。
  • 2:ユーザーがスワイプジェスチャーを入力したときに呼び出される関数を作成します。関数がObjective-C関数を作成する前の「@objc」。これは、元のUISwipeGestureRecognizerの#selectorを介して呼び出されるために必要です。

アプリをコンパイルし、4方向すべてにスワイプしてコードをテストすると、コンソールは各スワイプジェスチャーに対応する文字を印刷する必要があります。ジェスチャ認識機能が設定されたので、プレーヤーの動きの方向を変更し、スワイプ関数内の印刷ステートメントをこのコードに置き換え、GameManager.swiftにコードを追加して、プロジェクトが図Tに一致するようにする必要があります。

// 1-GameScene.swift
game.swipe(ID:3)
game.swipe(ID:1)
game.swipe(ID:2)
game.swipe(ID:4)
// 2-GameManager.swift
func swipe(ID:Int){
    if!(ID == 2 && playerDirection == 4)&&!(ID == 4 && playerDirection == 2){
        if!(ID == 1 && playerDirection == 3)&&!(ID == 3 && playerDirection == 1){
            playerDirection = ID
        }
    }
}
図T図T
  • 1:スワイプジェスチャーが検出されると、gameManagerクラスに通知されます。
  • 2:スワイプが現在の方向と競合していない場合、プレーヤーの方向をスワイプ入力に設定します。下に移動する場合、すぐに上に移動することはできません。左に移動している場合、突然右に移動することはできません。このような不適切な動きを入力するヘビのいくつかのバージョンでは死に至りますが、このバージョンでは無関係な入力を無視します。

ゲームにポイントを追加してスコアを調整する

ゲームボード、セルの配列、プレーヤーの位置の配列、画面内を移動して端を曲がるプレーヤー、およびコントロール用のスワイプレコグナイザーを開く作業メニューシステムが用意されました。次に、ゲームが楽しくなるようにスコア付けメカニズムを追加する必要があります。このメカニズムにより、スコアが増加し、「蛇」またはプレイヤーの軌跡が長くなるランダムポイントが生成されます。

最初のステップ:ランダムポイントを生成し、画面上にレンダリングします。次のコードをGameScene.swiftファイルに追加して、図Uに一致するようにします。

// 1
var scorePos:CGPoint?
図U

GameManager.swiftファイルに移動し、図Vに一致するように次のコードを追加します。

// 2
generateNewPoint()
// 3
private func generateNewPoint(){
    let randomX = CGFloat(arc4random_uniform(19))
    let randomY = CGFloat(arc4random_uniform(39))
    scene.scorePos = CGPoint(x:randomX、y:randomY)
}
// 4
if scene.scorePos!= nil {
    if Int((scene.scorePos?.x)!)== y && Int((scene.scorePos?.y)!)== x {
        node.fillColor = SKColor.red
    }
}
図V

コードを実行すると、シミュレーターにランダムに配置された赤い正方形が表示されます。

  • 1:ランダムなスコア位置の変数を初期化します。 「? “は、変数を後で設定するまで、これがnil(空またはまだ設定されていない)であることを示します。
  • 2:新しいランダムポイントを生成するinitGame()関数内の関数を呼び出します。
  • 3:この関数は、ボードの境界内でランダムな位置(20/40)を生成します。配列は0からカウントを開始するため、0〜19および0〜39でカウントします。これは20x40アレイです。
  • 4:レンダリングループ内で、現在のノードの位置がランダムに配置されたスコアの位置と一致するかどうかを確認し、一致する場合は色を赤に設定します。お好みに合わせて色を変更できます。スコアの位置を保存する変数はCGPointです。つまり、point.xおよびpoint.yを確認し、現在のノードのxおよびyの位置と比較する必要があります。ノードの配列でx / yの位置が反転していることに注意してください。これが、x == yとy == xを比較している理由です。

ここで、現在のゲームのスコアを保持する変数を割り当て、プレーヤーがポイントにヒットしたらそれを繰り返す必要があります。プレイヤーがポイントにヒットしたら、新しいランダムポイントを生成し、プレイヤーの「尾」の長さを増やす必要があります。

次のコードをGameManager.swiftファイルに追加して、図Wに一致するようにします。

// 1
var currentScore:Int = 0
// 2
checkForScore()
// 3
private func checkForScore(){
    if scene.scorePos!= nil {
        let x = scene.playerPositions [0] .0
        let y = scene.playerPositions [0] .1
        if Int((scene.scorePos?.x)!)== y && Int((scene.scorePos?.y)!)== x {
            currentScore + = 1
            scene.currentScore.text = "スコア:\(currentScore)"
            generateNewPoint()
         }
     }
}
// 4
while contains(a:scene.playerPositions、v:(Int(randomX)、Int(randomY))){
    randomX = CGFloat(arc4random_uniform(19))
    randomY = CGFloat(arc4random_uniform(39))
}
図W
  • 1:このゲームの現在のスコアを追跡する変数を初期化します。
  • 2:更新関数内でcheckForScore()関数を呼び出します。これは、プレーヤーが移動するたびに呼び出されます。
  • 3:この関数は、scorePosが設定されているかどうかを確認し、設定されている場合は蛇の頭を確認します。ヘビがポイントに触れている場合、スコアが繰り返され、スコアを示すテキストラベルが更新され、新しいポイントが生成されます。
  • 4:このコードをgenerateNewPoint()メソッドに追加して、ヘビの体内でポイントが生成されないようにしました。ヘビの長さが大きくなると、この問題に遭遇する可能性が高くなるため、このコードブロックでその問題を修正する必要があります。

コードを実行すると、スコアを打つとボード上に新しいスコアが生成され、スコアラベルが反復されることがわかります。ゲームプレイの仕組みがより近くなるように、ヘビの長さを増やす必要があります。これは非常に簡単であることがわかりました。このコードスニペットをcheckForScore()関数に追加するだけで、コードが図Xと一致します。

scene.playerPositions.append(scene.playerPositions.last!)
scene.playerPositions.append(scene.playerPositions.last!)
scene.playerPositions.append(scene.playerPositions.last!)
図X

ゲームを終了する

ここで、ゲームを終了してメニューシステムに戻るメソッドを実装する必要があります。ヘビのゲームでは、プレイヤーが自分の尻尾にぶつかるとゲームは終了します。 GameManager.swiftファイルに次のコード行を実装することにより、この効果を実現できます。コードが図Yと一致することを確認します。

// 1
checkForDeath()
// 2
private func checkForDeath(){
    scene.playerPositions.count> 0 {
        var arrayOfPositions = scene.playerPositions
        let headOfSnake = arrayOfPositions [0]
        arrayOfPositions.remove(at:0)
        if contains(a:arrayOfPositions、v:headOfSnake){
            playerDirection = 0
        }
    }
}
// 3
playerDirection!= 0 {
    playerDirection = ID
}
// 4
ケース0:
    //デッド
    xChange = 0
    yChange = 0
    ブレーク
図Y図Y
  • 1:checkForDeath()関数を呼び出します。
  • 2:プレーヤーの頭が尾の位置のいずれかと衝突していないか確認します。プレーヤーが死亡した場合、playerDirectionを0に設定します。
図Z
  • 3:プレーヤーが死亡した場合(playerDirection = 0)、入力として新しいスワイプジェスチャーを許可しません。
  • 4:playerDirectionが0に設定されている場合、updatePlayerPosition()のswitchステートメントに新しいケースを追加し、頭の位置を変更しないでください。これにより、テールの位置がゆっくりと見えなくなります。

これらのコードの変更を実装すると、アプリは画面記録のように機能するはずです(図Z)。

スネークがそれ自体と衝突すると、ゲームは終了します。ゲームを再起動し、スコアをハイスコアとして保存するためのメソッドを構築する必要があります。

ゲームを再起動し、ハイスコアデータを保存する

動作するヘビゲームを作成しました(ほとんどの場合)。最後の手順はもうすぐです!ゲームを再開してメニューに戻るための方法が必要です。また、このラウンドのスコアが最高のハイスコアよりも優れていた場合は、ハイスコアデータをデバイスに保存する必要があります。

まず、ヘビが閉じるアニメーションを終了すると、メニューに戻るメソッドを実装しましょう。次のコードをGameManager.swiftファイルに追加して、コードが図AAと一致するようにします。

// 1
finishAnimation()
// 2
private func finishAnimation(){
    playerDirection == 0 && scene.playerPositions.count> 0 {
        var hasFinished = true
        let headOfSnake = scene.playerPositions [0]
        scene.playerPositions {
            if headOfSnake!= position {
                hasFinished = false
            }
         }
     hasFinished {
        print( "ゲーム終了")
        playerDirection = 4
        //アニメーションが完了しました
        scene.scorePos = nil
        scene.playerPositions.removeAll()
        renderChange()
        //メニューに戻る
        scene.currentScore.run(SKAction.scale(to:0、duration:0.4){
        self.scene.currentScore.isHidden = true
}
        scene.gameBG.run(SKAction.scale(to:0、duration:0.4)){
            self.scene.gameBG.isHidden = true
            self.scene.gameLogo.isHidden = false
            self.scene.gameLogo.run(SKAction.move(to:CGPoint(x:0、y:(self.scene.frame.size.height / 2)-200)、duration:0.5)){
                 self.scene.playButton.isHidden = false
                 self.scene.playButton.run(SKAction.scale(to:1、duration:0.3))
                 self.scene.bestScore.run(SKAction.move(to:CGPoint(x:0、y:self.scene.gameLogo.position.y-50)、duration:0.3))
               }
          }
          }
     }
}
図AA

この方法の説明は次のとおりです。

  • 1:finishAnimation()関数を呼び出します。
  • 2:この関数は、ヘビが自分自身に近づくと、ヘビの最終アニメーションの完了を確認します。 playerPositions配列のすべての位置が互いに一致すると、ヘビは1つの正方形に縮小しました。これが発生した後、playerDirectionを4に設定し(以前は0に設定されて死亡を示していました)、メニューオブジェクトを表示します。また、currentScoreラベルとgameBGオブジェクト(正方形のグリッド)を非表示にします。

アプリを閉じたときに高得点データが失われないように、高得点をデバイスに保存するメソッドを追加しましょう。新しいメソッド(finishAnimation())で、このコード行を追加して、ファイルが図BBに一致するようにしました。

updateScore()
図BB

AppDelegate.swiftファイルを開き、プロジェクトが図CCに一致するように、次のコード行を追加します。このコードスニペットは、UserDefaultsを使用してデータをデバイスのメモリに保存します。保存された大量のデータを使用してプロジェクトを構築することを計画している場合、問題が発生する可能性がありますが、設定の切り替えや変数などの単純なものではうまく機能します。

let defaults = UserDefaults.standard
let defaultValue = ["bestScore":0]
defaults.register(デフォルト:defaultValue)
図CC

GameManager.swiftファイルに戻り、次のメソッドを作成して、コードが図DDに一致するようにします。このコードブロックは、スコアが最高のスコアを超えているかどうかを確認し、それに応じて更新します。

// 1
private func updateScore(){
     if currentScore> UserDefaults.standard.integer(forKey: "bestScore"){
          UserDefaults.standard.set(currentScore、forKey: "bestScore")
     }
     currentScore = 0
     scene.currentScore.text = "スコア:0"
     scene.bestScore.text = "ベストスコア:\(UserDefaults.standard.integer(forKey:" bestScore "))"
}
図DD

GameScene.swiftを開き、initializeMenu()関数を編集して、ファイルが図EEに一致するようにします。これにより、ゲームのロード時に、保存された最高スコアが0ではなく表示されます。

bestScore.text = "ベストスコア:\(UserDefaults.standard.integer(forKey:" bestScore "))"
図EE

この新しいコードを追加すると、アプリが終了するとハイスコアがデバイスのメモリに保存されます。

おわりに

画面の下部にある開発者情報を削除するには、GameViewController.swiftファイルを開き、view.showFPSとview.showsNodeCountをfalseに設定します。

これで、iPhoneゲーム全体をゼロから作成できました。これが最終製品のデモビデオです。

プロジェクトを楽しんだ場合は、IOSアプリをチェックしてみてください!問題や質問がある場合は、shrader.gavin @ gmail.comまでお気軽にメールしてください。

本日作成したものは、SpriteKitゲームエンジンが提供しなければならない複雑さの表面をかろうじて傷つけただけです。将来的には、物理​​学、レベル設計、アニメーションを扱うチュートリアルを行う予定です。