🎋

[保存版]3時間でミニゲームを書きましょう

2022/03/21に公開約19,600字

ようこそ!

このコースは、プログラミングをしたことがない学生やプログラミングに興味のある初心者を対象に、わかりやすいプログラミング入門ガイドを目指すものです。コースを学ぶことで、最終的には下のように自分でミニゲームを書くことができるようになります。

あなたは小学生、中学生、大学生、社会人、定年退職者にもかかわらず、基礎知識がなくてもいいです。Webを閲覧できるブラウザが搭載されたパソコンがあれば、勉強を始めることができます!

プログラミングの勉強は5時間以内で終了できます。 さあ、はじめましょう

準備

まず、パソコンがあることを確認します。 パソコンのOS(Operating System)は、ブラウザとメモ帳(テキストエディタ)があれば、Windows、MacOS、Linux、いずれもかまいません。

1.ZIPファイルをダウンロードし、解凍してください

ここをクリックして、zipファイルyour-first-experience-of-programming-main.zipをデスクトップにダウンロードし、zipファイルを右クリックして、「すべて展開」を選択してください。 (Macをお使いの場合、ZIPファイルの展開に問題がある場合は、こちらをクリックしたら解決できます)

デスクトップにyour-first-experience-of-programming-mainというフォルダが表示されて、ダブルクリックでこのフォルダを開くと、以下のような状態になっています。

2. ブラウザとメモ帳の両方で、my-game.htmlファイルを開く

「my-game.html」をダブルクリックすると、ブラウザで開くはずです。 ない場合は、右クリックしてメニューから「プログラムから開く」を選択し、お使いのブラウザを選択してください。

ブラウザで「my-game.html」を開くと、ページ上に薄いグレーのボックスが表示されます。これがゲーム表示エリアで、ここに私たちの書いたミニゲームが表示されます。

次に、再び「my-game.html」を右クリックし、「プログラムから開く」でメモ帳を選択すると、開いたメモ帳の中にこれが見えます。

<!DOCTYPE html>
<html lang="en">
<head>@[gist]()

   <meta charset="UTF-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <title>My Game</title>
</head>
<body>
   <canvas id="app"></canvas>
   <script src="./index.js"></script>
   <script src="./pattern.js"></script>
   <script>
       //この行より前の内容は変更しないでください
       let width = 30
       let height = 18
       let mygame = new Game(width, height, 2)
       // 次の行にコードを書く
   
       // この行以降は変更しないでください
   </script>
</body>
</html>

OK!準備が終わった!

ヒント:コードの後半でうまくいかない場合、ここに戻って上記のコードを再コピーすると、問題が解決できます。【やらなくてもいいが、コーディング体験を最適化できること】実際には、メモ帳でもコードを書くことはできますが、使い勝手が悪いかもしれません。 面倒なことでも我慢できる方は、http://www.sublimetext.com/3 からsublime textをダウンロードして、メモ帳の代わりに使うこともできます。 (他の使いやすいエディタがあれば、それもいい)

はじまり

次の目標は、冒頭に出てくるミニゲームを作ることです。ここをクリックすると、ゲームを体験することができます。

このゲームを体験すると、小さな人を操って歩いたりジャンプしたりすることで、薄いグレーのエリアは自由に動けるが、壁や床(濃いグレーの部分)を越えないことが発見します。

このゲームをブロックで作った集合と考えた場合、集合体に必ず含まれる最も基本的なブロックは何でしょうか?

  1. 操られる人

  2. 人には越えられない壁

  3. 自由に動ける空気

では、前のコードを見ながら、ここに注目しましょう。

let width = 30
let height = 18
let mygame = new Game(width, height, 2)
 // 次の行にコードを書いてください

注目すべきのは、この4行のうち、最初の3行がすべて同じ形式です。

すなわち

let AAA=BBB

この意味も非常に簡単で、文字通り「AAAイコールBBB」という意味です。

例えば

let width = 30
変数width を 30 とする

let height = 18
height を 18 とする

let mygame = new Game(width, height, 2)
mygame を new Game(width, height, 2) とする

これはどういう意味? あなたが等号の右側にあるものに名前をつけて、その名前を等号の左側にあるものにし、その名前で等号の右側にあるものを召喚できるということです。

let width = 30
let height = 18
let mygame = new Game(width, height, 2)

new Game(width, height, 2)のコード列で、widthは30、heightは18を表します

すなわち
new Game(30, 18, 2)に相当します

例えば、let width = 30 の場合はwidthという名前を30につけた、あるいはwidthを30に紐づけたと理解できるでしょう。

つまり、右の数字は「入れ替える」ことができます。30に紐づける必要がなければ、「width = 42」というコードでwidthを42につけることができます。この時点で30は名前width を失い、42はwidthという新しい名前を獲得し、widthからすれば42という新しい「エンティティ」を持つことになります。コーディングでは、第二種の考え方が必要です。

次に4行目をご覧ください。

// 次の行にコードを書いてください

この行はコードではなく、あってもなくてもコードに影響を与えないメモです。 削除してもいいし、そのままでもいいです。忘れてはならないのは、このメモを新しい行で続けたい場合は、行頭を//で始めることです。

簡単でしょう?この小さなルールを理解した上で、本題に入りましょう。

前述したように、このミニゲームがブロックの集合から作られると考えるが、私たちのコードでそんなブロックの集合体とは?これです👇

let mygame = new Game(width, height, 2)

この意味は、幅30ブロック、高さ18ブロック(2の意味は後で説明します)のインタフェスを持つ新しいゲームを作り、それに「mygame」という名前を付けることです。つまり、mygameが提供するブロックのいくつかを使って、このインタフェス上にミニゲームを構築することができます。

先ほどの「人」「壁」「空気」という必ず持つべきブロックの種類を再確認しておきましょう。

そして、以下のコードで、それぞれ人間型のブロック、壁型のブロック、空気型のブロックを作ります。

let human = mygame.create.human() // 制御可能な人の人間型ブロックを作成
let wall = mygame.create.wall() // 人が通過できない壁型のブロックを作成
let air = mygame.create.air() // 人が自由に通過できる空気型ブロックを作成

よし、じゃ試してみよう! 【次の行にコードを書いてください】の下の行にこれをタイプします。

let human = mygame.create.human()

このように、インタフェスに小さな人を作り出したのです。 ctrl+sを押して保存し、ページをリフレッシュすると効果が表示されます。

ctrl+sの押し方:キーボードのctrlキーのいずれかを押したまま、sキーを1回押して、そして離します。

ゲーム画面の左下に小さな緑の四角が表示されますが、人の形をしていません。 これを人型にするために、次の行にこれを入力します。

human.style = pattern.human

保存してリフレッシュすると、次のようになります。

先ほどの2行のコードを振り返ってみましょう。

let human = mygame.create.human()
human.style = pattern.human

第1行コードの意味が分かるでしょう。「mygame.create.human()」に「human」という名前をつけています。

また、「mygame.create.human()」はどんな意味のでしょうか? 今でも文字通り、「mygameで人間を作る」という意味です。

最後の英語括弧「()」は「実行」を意味します。「人を作る」はアクションなので、このアクションを実行させるために最後に()を使用する必要があります。

第2行のコードも文字通りです。

human.style = pattern.human
人 の スタイル は(画面での)「human」というパターン

styleがhumanの属性であるから、属性を適切なもの(値)に変更することで、そのブロックに影響を与えます。 この例では、style属性は別のパターンに変更することができ、pattern.humanを選んで、デフォルトのhuman(緑の四角)の外観を、横柄なランニングマンの形に変えました。

これで小人は自由に動き回れます! キーボードのa、d、wで、小人の歩く方向やジャンプする方向を操作できます。

壁を追加する

ここで、ミニゲームに壁を追加します。
前回は、小人の作り方を学びました。それと同じように、壁面を作りましょう。

mygame.create.wall()

上の図のように、ゲーム画面の左下にグレーの四角が表示されます。 左下にあった小さな小人が消えました。これは、小人とグレーの四角が同じ場所にあり、グレーの四角が小人を覆っているからです。
とりあえず小人を無視して、残りの壁を作ることを考えよう。
ご覧のように、これがゲームに必要な壁です(濃いグレーの部分)。

壁はそれぞれ異なる位置を持つので、作成時に壁の位置を指定する必要があります。
mygame.create.wall(0, 1)
// 0は壁の座標x、1はその座標y

この壁が位置を変えて、小人の頭の上の1フレームになったことにお気づきでしょう。
よし、ここまでもうお分かりでしょう。前の図面と同じ、適切な場所に壁を作ればいいのです。
例えば、

mygame.create.wall(0, 0)
mygame.create.wall(0, 1)
mygame.create.wall(0, 2)
mygame.create.wall(0, 3)
mygame.create.wall(0, 4)
mygame.create.wall(0, 5)
mygame.create.wall(0, 6)
mygame.create.wall(0, 7)
mygame.create.wall(0, 8)
mygame.create.wall(0, 9)

そして

一挙に10枚の壁が完成!しかし、これは遅すぎではないか?
先ほどの図面の壁を数えると、外周だけで18 * 2 + 30 * 2 = 96枚の壁があり、これを書いたら手が攣りそうです。
この問題を解決するために、「ループ」というツールを導入します。
上の10枚の壁を作るコードを見ると、そのパターンが発見できます。すべての壁のx座標は同じ(すべて0)で、yは0から増加し、yが9になるまで毎回1を加えます。
このプロセスを短いコードで説明できます。

for (let i = 0; i <= 9; i++) {
    mygame.create.wall(0, i)
}

その意味は:iを作成して0とし、括弧{ }の内容を実行するたびに、iに1を加算します。そして、毎回括弧の内容を実行する時、i小なりイコール9かどうかをチェックし、この条件が満たされないと、実行しません。
全サイクルの内訳は以下の通りです。

let i = 0
問題:i <= 9 か?
答え:はい
実行:mygame.create.wall(0, 0)
i = 1
問題:i <= 9 か?
答え:はい
実行:mygame.create.wall(0, 1)
i = 2
問題:i <= 9 か?
答え:はい
実行:mygame.create.wall(0, 2)
i = 3
......
......
i = 9
問題:i <= 9 か?
答え:はい
実行:mygame.create.wall(0, 9)
i = 10
問題:i <= 9 か?
答え:いいえ

サイクルが終了します
この道具を覚えれば、壁を作るのが楽になります。 上の図面に従って、外周に壁を作ろう。

for (let i = 0; i <= 17; i++) {
   mygame.create.wall(0, i)
   mygame.create.wall(29, i)
}
for (let i = 0; i <= 29; i++) {
   mygame.create.wall(i, 0)
   mygame.create.wall(i, 17)
}

あとは図面通りに壁を作るだけです。もし何かに行き詰まったら、メモ帳でexample.htmlを開いてヒントを探してみてください。

human.x = 1
human.y = 1

そうしたら、彼が見えます!

星と炎

その直後に、星と炎を加えます。 まずは図面を見ましょう。

星や炎の位置はわかりやすいので、あとは適所でブロックを作り、パターンを設定すればいいのです。しかし、いざコーディングに取りかかろうとすると、「どのようなブロックを作ればいいのか、星型や炎型のブロックはあるのでしょうか?」という問題が出てきます。
例えば、こんな風に書けるかな?

mygame.create.star(28, 4)
mygame.create.fire(14, 3)

このように書いて、コードを保存してページを更新したときに、ゲーム画面に星や炎が期待通りに表示されません。ブロックの中に星型ブロックや炎型ブロックがないためです。
今何をしたらいいかわからないかもしれないが、落ち着いて考えましょう。
ゲームに加えたい星の特性は何ですか?
あるいは小人を星に近づけて操ったとき、どの位置で小人が星を食べるのしょうか?
一般的に、小人が通り過ぎるときに、その星と重なって食べられます。
つまり、壁と違って、星は小人に通過されます。
ここまで、「小人に通過されるって、さっきの空気型のブロックと同じじゃないか!」と思われたかもしれませんね。
もう一度、前回の分析を振り返ってみましょう。最も基本的なブロックは何ですか?

  1. 操作できる人
  2. 人が踏み越えられない壁
  3. 人が自由に動ける空気
    現在のシーンでは、追加したい星の性質は空気ブロックが持っているものと同じです。
    つまり、星を作るために「星」型のブロックを用意する必要はなく、既存のブロックを使って、星の性質に合うようなものを作ることができるのです。このように、空気型のブロックを使って星を作られます。
let star1 = mygame.create.air(28, 4)
star1.style = pattern.star


そして、図面を参照して他の星を作り、同じ方式で炎を作ること考えましょう。問題が発生した場合は、example.htmlをメモ帳で開いてヒントを確認します。

弾丸を飛ばしましょう

弾丸を追加する前に、弾丸の飛行経路を解析してみましょう。
ゲームを体験したと、赤い銃口で発生した弾丸がゲーム画面の左端まで飛んでいき、消えていくのがわかります。

こうして、結論が出ます。弾丸を銃口で生成させ、ゲーム画面の外に出るまでずっと左に移動させます。1秒に1発の弾丸が生成します(希望する弾丸密度に応じて、ここでは1秒間隔にしていますが、この間隔は自由に変更可能です)。
まず、弾が1つしか生成しない場合を考えてみましょう。
銃口で弾丸が生成されるコードは、あなたはこう書くかもしれません。

let bullet = mygame.create.air(28, 1)
bullet.style = pattern.bullet

次に、左に動かし続けることです。 その前に、ブロックが提供する自動移動機能について知っておく必要があります。自動移動とは、設定した距離を一定の方向に、設定した速度でブロックを通過させることです。説明書でのautomove.leftの説明を読むと、自動移動機能の詳細を知ることができます。

bullet.automove.left(29, 4)

上のコードで、29は弾丸が左に移動する距離、4は弾丸が移動する速度です。 保存してページを更新すると、弾丸が飛ぶ効果を確認することができます。
1発の弾丸の飛ぶ問題さえ解決すれば、あとは銃口の位置で1秒ごとに弾丸を生成し、弾丸を「automove」させれば、すべてが完成します。
1秒ごとに弾丸を生成させることを実現したいと、簡単に考えて、もしタイマーがあれば、1秒ごとにトリガーして、以下のコードが実行します。

let bullet = mygame.create.air(28, 1)
bullet.style = pattern.bullet
bullet.automove.left(29, 4)

それがいいじゃない? 要は、そんなタイマーがあるのか。

まあ、ありますよ! 次のように書く。

setInterval(function() {
    // コードを書く
}, 1000)

上記のコードを理解するためには、関数を知る要がある。

私たちの多くは、中学数学の「y = kx + b」から関数の初歩的な知識を持っています。ここで、k と b は定数、x は独立変数、y は従属変数です。 x の入力が異なれば、異なる y または同じ y がられる(この例では、異なる y しか得られない)。
xを入力するとyが出力されるという点では同じ構造であるが、プログラミング言語には、xもyも持ち得ない関数も存在する点が異なる。上記のタイマーのコードのように、単に手続き(コード)を実行して渡すための入れ物として使われる。

function() {
    // 実行されるコード
}

内部で実行したいコードを書き、この関数と実行間隔をタイマーに伝える(渡す)と、タイマーは指定した間隔でこの関数内のコードを実行します。

次のように書きましょう。

let createbullet = function() {
    let bullet = mygame.create.air(28, 1)
    bullet.style = pattern.bullet
    bullet.automove.left(29, 4)
}
setInterval(createbullet, 1000)

それともこう書いてもいい

setInterval(function() {
   let bullet = mygame.create.air(28, 1)
   bullet.style = pattern.bullet
   bullet.automove.left(29, 4)
}, 1000)

ここで、英語カンマの後の1000は1000msを意味します(1秒=1000ms)。全コードの意味は、タイマーに1000msに1回関数内のコードを実行させる——弾丸を作成し、速度4で左に29マス移動させる - ということです。
最後に、もう1つの銃口を追加してみましょう。

let gun = mygame.create.wall(28, 1)
gun.style = pattern.gun

こうして、弾丸発射の機能が完成しました!
小さな練習:付録のパターンイラストと説明書を参考に、あの飛んでいる緑の亀甲を作ってください。 行き詰まったときは、メモ帳でexample.htmlを開いてヒントを参照してください。

多様な危険

先ほどから弾丸や炎などの危険なアイテムを追加しましたが、自分でプレイしてみると、これらは小人にとって致命的ではないことがわかります。
私たちの予想では、小人が弾、炎や亀甲に触れるとゲームが終了します。 そのロジックをわかりやすく言うと、下の図のように

まず、1つ目のフレームから見ていきます。「小人は炎に触れるか」って、コードでどう書きますか?
答えは、条件文を使うことです。

if (xxx) {
    //  ここに条件を満たしたときに実行されるコードを書く
} else {
    // ここに条件を満たさないときに実行されるコードを書く
}

ifの後の括弧内の「xxx」は判定条件であり、もしその判定条件の結果がtrue、つまり「はい、その通りです」であれば、最初の括弧内のコードが実行されます。
判定条件の結果がfalse、つまり「いいえ」場合は、elseにはいて、2番目の括弧内のコードが実行されます。
前の文で見たように、判定条件は「人が炎に触れたかどうか」であり、つまりこの部分の疑似コードは次のように書くべきです。

if (人が炎に触れた) {
    // ここで人が炎に触れた後に実行すべきコードを書く
}

上の擬似コードでは、小人が炎に触れないとゲームが続くので「else」を使っておらず、「ゲームが続く」ということは、現在の場合では「何も起こらない」と考えます。
では、「小人が炎に触れた」はどのように表現するのでしょうか。 私たちのブロックは、istouchedというアクションを提供しており、このブロックが他のブロックと接触したかどうかを判断することに使用します。接触した場合はそのアクションが「true」で、その逆は「 false」です。
だから、この部分は次のように書きます。

if (human.istouched(fire1)) {
    //  人が火に触れたときに実行されるコードをここに書く
}

ブロックには、istouchedの他に、isoverlappingというアクションがあります。それはブロックが重なっているかどうかを判断するために使用されることです。この二つのアクションの具体的な違いについては、説明書で探してみましょう。
続けて、「人が炎に触れたか? 」
そうであれば、ゲームは終了です。 また、ゲームではゲームを終了させるためのアクションも用意されています。

mygame.over.lose() // gameoverで負けを宣告
mygame.over.win() // gameoverで勝利を宣告

そして、この過程をコードで表すと、次のようになります。

if (human.istouched(fire1)) {
   mygame.over.lose()
}

では、この「小人」と「炎」が接触しているかどうかを確認する行為は、いつ行われるのだろうか。
コンピュータから見ると、プレイヤーの操作する小人がいつ炎に接触するか分からず、接触したかどうか常に検出することで、実際に炎に接触した時に正しいフィードバックができるのです。
以前使っていたタイマーを使って、上記の検出コードをタイマーに入ればどう?と思われたかもしれません。
では、「常に」というのは、どのように定義すればいいのでしょうか。 1秒に1回?あるいは0.5秒ごと? 0.01秒ごと?
実際、コンピュータはプレイヤーのキー操作の頻度を知らないので、どのような間隔を選んでも適切ではありません。そして、「常に」というのは、時間の次元ではなく、小人の動きの次元で考えます。
つまり、頻度ではなく、小人が動くたびに炎との接触を検知することが重要なのです。 これにより、プレイヤーの動きが「速い」「遅い」に関わらず、効果的に検出することができます。
私たちのブロックは、この「常に」を実現するための方法を提供します。

human.move.after = function() {
    // ここに小人が動いたときに実行されるコードを書く
    // 小人が動くたびに、コードが一度実行される
}

したがって、「小人が動く→炎と接触したか判断→YESならゲーム終了/NOならゲーム続行」という処理を実現できます。

human.move.after = function() {
   if (human.istouched(fire1)) {
       mygame.over.lose()
   }
}

亀甲(ここではデフォルトで亀甲となるブロックはturtleと呼ばれる)と炎も同じように実装されており、引き続きその検出をhuman.move.after関数に追記すればよいのです。

human.move.after = function() {
    if (human.istouched(fire1)) {
        mygame.over.lose()
    }
    if (human.istouched(fire2)) {
        mygame.over.lose()
    }
    if (human.isoverlapping(turtle)) {
        mygame.over.lose()
    }
}

だから、今はもう弾丸しか残っていない。
小人と弾が重なるかどうかを検出する方法は、前と少し異なります。
これは、ゲームの実行に伴って新しい弾が生成されるため、一度書いたコードを保存して実行すると自動的に修正できません。即ち、human.move.after関数に、弾丸が追加する際に小人と弾丸が重なるかどうかを検出するコードを追加できません。
つまり、小人が動いたときに小人と弾が重なるかどうかは検出できないが、弾が動いたときに小人と弾が重なるかどうかは検出できます! では、作成後の各弾にmove.after関数を設定します。

setInterval(function () {
    let bullet = mygame.create.air(28, 1)
    bullet.style = pattern.bullet
    bullet.automove.left(29, 4)
    // この部分が重要👇👇
    bullet.move.after = function () {
        if (human.isoverlapping(bullet)) {
            mygame.over.lose()
        }
    }
    // この部分が重要👆👆
}, 1000)

こうすると、これらの危険物の致死性はすべて設定されました。
この項の最後に、星の扱いについて簡単に説明します。
星に対する設定は、小人が星を食べる、つまり小人と重なると星が消えます。小人が右下(star1)と左上(star2)の星を食べるとジャンプの高さが上がり、右上(star3)の星を食べるとゲームの勝ちになるということです。
亀甲の重なり検出と同様に,星の重なり検出も human.move.after 関数に付加しています。

if (human.isoverlapping(star1)) {
    human.strength = 5
    star1.remove()
}

human.strengthは、小人のジャンプの最大高さを制御します。デフォルト値は3。小人が二段目の台までジャンプできるように、より高い高さを選んでいます。removeアクションはブロックを自分から取り除くために使用され、star1.remove()が実行されると星は消えます。
最後の二つの星の扱いはお任せします。 行き詰まったときは、example.htmlをメモ帳で開く、ヒントが表示されますよ。

石に乗って帰ろう

これまでの内容を振り返ってみると、ブロックの作成方法、ループを使ったブロックの効率化、関数についての知識、条件文を使って特定のブロックにトリガーされるものの定義、そして関連するプログラミングの考え方を少し学ぶことができました。
この部分では、新しいことを学ぶのではなく、問題の分析に注目します。
さて、いよいよ人を乗せる飛ぶ石が登場します。 この飛ぶ石の作り方はもうわかったと思います。

let stone = mygame.create.wall(9, 13)
stone.automove.right(16, 1, true)

しかし、コードを保存してページを更新して乗ってみると、すでに岩に飛び乗っていても、岩が動くと人形が落ちてしまうという、なんだか予想と違う展開に?人形が石の上に飛び乗ったら、石と一緒に終点まで運ぶはずです。
現実には確かにそうです。現実の人間が動いている物体の上に立つと、摩擦の存在により、物体の速度と一致するまで加速されるからです。
しかし、このゲームでは、コンピュータがこの物理学的な現象を意識していないため、ブロックとブロックの間に自然に摩擦が発生することはないのです。
他のゲームをしてキャラクターを操作するときに感じる摩擦は、ゲーム開発者が現実の物理法則に基づいてコードを書いてシミュレートしてきたことです。
このゲームでは、摩擦の模擬はしていませんので、石の上に立つと、人形が自動的に期待通りに動いてはくれません。
つまり、人形が動いているブロックの上に立って、ブロックと一緒に動くというのは、当たり前のことではなく、プログラマーが自ら作らなければならないことです。
次は、この「機能」を設計し実現することです。
前述のように、このような現象は摩擦を模擬することでコンピュータに実現する必要があるが、実際に摩擦を模擬してその問題を解くには複雑すぎるでしょう。
人が石の上に立ったときに、その石と一緒に動けばいいのですから、 他の石の上に立ったときに同じ効果が得られるかどうかを考えることなく、この一つの必要性に集中します。
この需要に基づいて、プレイヤーが人形を操作して石に飛び乗り、石から降りてくるまでの全体の流れを整理すると、次のようになります。
altテキスト

上の図を参照し、この部分で小人が移動する流れを感じてみてください。また、ゲーム例を体験してもいいです。
次に、小人がジャンプから石で移動するまでの具体的な筋道を詳細に分析しよう。
まず、私たちの需要をおさらいします。人が石の上に立ったときに、その石と一緒に動けます。
つまり、人が石の上に立つ(触れた)と、石が動いたら小人がすくその後に追いかけます。また小人はいつも石の1マス上にいます。 小人自身が石との接触を絶つと、石と一緒に動かなくなります。
文字通り、先ほど学んだ move.after と istouched アクションを使ってそれを実現するとすぐに思い浮かべるかもしれません。

stone.move.after = function() {
    if (human.istouched(stone)) {
        human.x = stone.x
        human.y = stone.y + 1
    }
}

簡単にできそうな気がするか? このコードを保存してページを更新すると、実行結果を確認しましょう。 小人が石に飛び乗ったとき、小人は石を追わず、石が動いた後に落ちていることになります。
なぜ、このようになったのでしょうか? コードの実行理路は問題ないそうですが……
構いませんよ。コードを書いていると、見た目は正しいのに実行して間違ったということがよくあります。
では、石が数回動いて、小人が石に触れるとどうなるか。ちょっと考えてみよう。
上記によって、stone.move.after関数はまず小人が石に接触したかどうかを検出します。現在の場合は接触しました。そのため、小人の位置は更新され、石に接触したまま真上に移動することになります。
ここまで問題がないですが、時間が経って、石は次の移動をします。この時、小人と石が接触しなくなるという出来事が起こります。
そのため、石が次の動きをしたときに、stone.move.after関数内の検出コードhuman.istouched(stone) が false を返し、小人の位置を変更する条件文内のコードが実行されなくなる、つまり小人が石と接触しなくなり、小人が石から落ちることになります。
このとき、石と人が接触しているかどうかを、石が動いた後のこの瞬間に実際に接触しているかどうかで判断してしまうという。
そして、先ほどの考え方の盲点が発覚します。
先ほどの分析によって、石が次の動きをしたとき小人と石はもう接触していない。しかし、論理的には、この場合小人と石が接触しているとプログラムに思わせることで、この理路をもとに「石が少し動くと小人がついてくる」ようにしたいのです。
実況と予想は違うので、「論理的な状態」を作り、論理の流れを修正・確認することで、「石が少し動くと小人がついてくる」という理想的な状態にする必要があります。
上記の分析をより分かりやすいフローチャートにまとめました(図中の「状態」はすべて「論理状態」を意味します)。

上記のフローチャートを擬似コードで表現してみましょう。

// stone.move.after 関数は上の図の
// 石が移動する -> 論理的な状態が【触れている】であれば,小人が石を追いかけて移動す
//ることを実現
stone.move.after = function() {
    if (小人が石に触れている) {
        human.x = stone.x
        human.y = stone.y + 1
    }
}
// human.move.after 関数は上の図の「小人が石に触れているかどうか」と問うことで、「小人
//が石と接触しているか」という論理状態を変更
human.move.after = function() {
    if (human.istouched(stone)) {
        「石に触れている」と小人の論理状態をマーク
    } else {
        「石に触れていない」と小人の論理状態をマーク
    }
}

この論理状態をhuman.move.after関数で修正(定義)して、stone.move.afterで使用することがわかります。
では、この論理状態をどのように表現すればいいのでしょうか。実は、本文の冒頭で学んだように。

let state = false

小人の論理状態が石に触れているとき、 stateをtrueとし、その逆に、 stateをfalseとします。小人の論理状態を知る必要がある場合、 stateにアクセスし、それがtrueであれば、小人の位置を同期して変更します。

let state = false
stone.move.after = function() {
    if (state) {
        human.x = stone.x
        human.y = stone.y + 1
    }
}
human.move.after = function() {
    // 前の部分で書かれた他のコードも忘れないで
    if (human.istouched(stone)) {
        state = true
    } else {
        state = false
    }
}

これでゲーム全体が完成しました! あなたの成果をお楽しみください :D

それだけ?

このミニゲームを一緒に書い上げたてどんな感じですか?
「なんだこれ!簡単すぎる!」、あるいは、「なるほど、これは面白い 」と思う?
とにかく、あなたはとても素晴らしい!!!
上記のコースで、あなたは常にmygameとそれが提供するさまざまなブロックを中心にプログラミングしています。
● mygameが提供するアクションをもとに、ブロックを作成する
● ブロックが提供するアクションで、ブロックたちが接触しているか、重なり合っているかを判断する
● ブロックのプロパティを変更することで、ブロックの外観や状態を変える
● プログラミング言語が提供する論理構造でロジックを表現する
これで、ゲーム全体のコーディングが完了しました。
もし興味があれば、説明書を参考にしながら、自分の創造力を発揮してもっと多いのミニゲームを設計・開発することもできます。

おめでとう!

あなたはこのコースをすべて修了しました! プログラミングそのものを体験するだけでなく、実際に、問題を分析し、解決策を設計し、問題を解決し、またマニュアルを参照しチームで決めた文書や仕様に沿ってコーディングするなど、プロのプログラマーの仕事を一通り体験しました。
このコースを通して、プログラミングの楽しさを少しは味わっていただけたのではないかと思います。プログラミングは積み木のようなもので、決して神秘的なものではありません。
しかし、それはまだ始まりに過ぎません。
実際の現場では、プロのプログラマーは、より複雑な環境、より複雑な要件、コード品質への高い要求に対処しなければならないことが多く、彼らが考えている以上に複雑な問題を解決しなければならないのです。
ここから少しずつコンピュータについて学んでいくと、それは思ったより難しくなく、怖くもないことがわかると思います。
がんばれー !

添付のファイル

1.パターンイラスト
2.独自のパターンを作る:your-first-experience-of-programming-mainファイルで
3.説明書:your-first-experience-of-programming-mainファイルで
4.単語リスト:your-first-experience-of-programming-mainファイルで
6.ミニゲームの例


この記事はMengruさんの原作から転載されたものであり、Mengruさんが転載の過程で助けてくれたことにとても感謝しています。原作のURL:https://fp.mengru.space

Discussion

ログインするとコメントできます