このページの本文へ

アップルの無償プログラミング学習ツール「Swiftプログラミング」でいっしょに遊ぼう 第5回

アップルの無償プログラミング学習ツール「Swift Playgrounds」アクションゲーム作り(仕上編)

2020年10月11日 09時00分更新

文● 柴田文彦 編集●飯島恵里子/ASCII

  • この記事をはてなブックマークに追加
  • 本文印刷

ステップ3:ゲームオーバーの表示処理を加える

 このステップは、ゲームの動きとしては、大きな意味があるものではありません。前のステップで作ったゲームオーバー機能は、残り時間がゼロになると、パタッとゲームの動きが止まってしまって、それっきりというものでした。それも寂しいので、ゲームオーバーになったことを、ちょっと目立つように表示してみようというものです。おまけとして、プレイグラウンドの「停止」ボタンを押さなくても、プログラムの実行が自動的に止まるようにもしてみます。

・プログラムの先頭にPlaygroundSupportのインポートを加える
・「Game Over」と表示するTextを用意し、極小サイズで表示しておく
・タイマー処理の中で「gameover」状態なら「Game Over」表示を徐々に大きくする
・「Game Over」表示の拡大が終わったら、プログラムの実行を停止する
・ここまでのプログラムを動かして動作を確認する

 まず最初に、プログラムの先頭のFoundationのインポートに続いて、PlaygroundSupportのインポートを追加します。順番は逆でも構いません。これは、最後にプレイグラウンドで動作しているプログラムを停止させる際に必要となります。

 次にGame Overを表示するTextですが、プログラムの実行を開始した時点で用意しておくのが安心です。ただし、表示が見えてしまわないよう、サイズは極小にしておきます。ならばTextのfontSizeを0.0にすれば良いのではと思われるでしょう。

 しかしSwift Playgroundsの現在のバージョンでは、サイズを0.0に設定すると、なぜか17.0に設定したのと同じくらいの大きさで画面に表示されてしまいます。そこでここではサイズを0.1に設定しています。これで実質的に表示されないほどには十分小さくなります。このTextは、画面の中央に表示することとして、特に位置は設定しません。Textは、overDispという定数に代入しておきます。タイマーによるループの中で参照するので、タイマーの記述の直前に置きます。Game Overの表示を変化させる際のカウンター、overCountも用意しておきます。

 タイマーの中では、statusの値を調べて、それが「running」以外の場合の処理も加えます。それがつまり、ゲームオーバー処理になります。その中では、まずGame Over表示のフォントサイズを3.0だけ大きくします。これを繰り返すことで、表示がだんだん大きくなるようにするのです。それに続いて、overCountの値を1ずつ減らします。初期値は上で見たように20でした。つまり、この部分はタイマーによるループとして20回実行されることになります。

 そしてその値が0になったら、PlaygroundPage.current.finishExecution()によって、プログラムの実行を停止します。この機能が使えるようにするために、プログラムの先頭で、PlaygroundSupportをインポートしたのでした。

 これで、このゲームはひとまず完成です。実際に動かして遊んでみましょう。穴の中で動くのがボールでは臨場感が今ひとつと感じられるかもしれませんが、それなりに遊べるゲームになっているはずです。ゲームオーバーになったら、Game Overの表示と、その後のプログラムの終了も確認しておきましょう。

 タイマー処理の中にあれこれ付け加えたので、構造が分かりにくかったかもしれません。ここでプログラム全体を示しておきます。

import Foundation
import PlaygroundSupport

Canvas.shared.color = #colorLiteral(red: 0.466666668653488, 
;green: 0.764705896377563, blue: 
0.266666680574417, alpha: 1.0)

let locations = [
    [-8.0, 16.0], [8.0, 16.0],
    [-16.0, 0.0], [0.0, 0.0], [16.0, 0.0],
    [-8.0, -16.0], [8.0, -16.0]
]

var score = 0
var time = 30
var frames = 20
var status = "running"

let scoreDisp = Text(string: "残り\(time)秒 - 得点:\(score)", fontSize: 30.0, fontName: "Menlo-Bold", color: Color.white)
scoreDisp.center = Point(x: 0.0, y: 35.0)

var moles = [Text]()
var mVels = [Double]()

for i in 0 ..< locations.count {
    mVels.append(Double(0.0))
}

for loc in locations {
    let hole = Circle(radius: 4.0)
    hole.color = #colorLiteral(red: 0.960784316062927, 
green: 0.705882370471954, blue: 0.200000002980232, alpha: 1.0)
    hole.center = Point(x: loc[0], y: loc[1] + 4.0)
    hole.onTouchDown {
        score -= 10
        scoreDisp.string = "残り\(time)秒 - 得点:\(score)"
    }
}

for loc in locations {
    let mole = Text(string: "🏀")
    mole.fontSize = 50.0
    mole.center = Point(x: loc[0], y: loc[1])
    mole.onTouchDown {
        score += 10
        scoreDisp.string = "残り\(time)秒 - 得点:\(score)"
        animate {
            mole.center.y = loc[1]
        }
    }
    moles.append(mole)
}
    
for loc in locations {
    let cover = Rectangle(width: 8.0, height: 8.0)
    cover.color = #colorLiteral(red: 0.466666668653488, 
green: 0.764705896377563, blue: 0.266666680574417, alpha: 1.0)
    cover.center = Point(x: loc[0], y: loc[1])
}

let overDisp = Text(string: "Game Over")
overDisp.color = #colorLiteral(red: 0.572549045085907, 
green: 0.0, blue: 0.23137255012989, alpha: 1.0)
overDisp.fontSize = 0.1
overDisp.dropShadow = Shadow()
var overCount = 20

let timer = Timer.scheduledTimer(withTimeInterval: 0.05, 
repeats: true, block: {_ in
    if status == "running" {
        var i = 0
        for mole in moles {
            var y = mole.center.y - locations[i][1]
            if y <= 0.0 {
                mVels[i] = Double(arc4random_uniform(40)) / 100.0 + 0.1
            } else if y 
>
 6.5 {
                mVels[i] *= -1.0
            }
            mole.center.y += mVels[i]
            i += 1
        }
        
        frames -= 1
        if frames == 0 {
            time -= 1
            if time == 0 {
                status = "gameover"
                
                for i in 0 ..< moles.count {
                    moles[i].center.y = locations[i][1]
                }
            }
            scoreDisp.string = "残り\(time)秒 - 得点:\(score)"
            frames = 20
        }
    } else if status == "gameover" {
        overDisp.fontSize += 3.0         overCount -= 1
        if overCount == 0 {
            PlaygroundPage.current.finishExecution()         }
    }
})

カテゴリートップへ

この連載の記事

ASCII.jp RSS2.0 配信中