ステージ0「Qratch に触ってみよう」

このステージではゲームの仕組みを実際に Qratch を触りながら学習していきます。

環境構築

環境構築とは、プログラマが作業をする環境を作ることです。今回はプレイグラウンドというブラウザだけで Qratch を試すことができる環境を使います。

下記のリンクを開いてみてください。

https://play.qratch.dev/open in new window

するとこのようなページがブラウザで開かれます。これが Qratch のプレイグラウンドです。このステージではこのプレイグラウンドでコードを実行しながらゲームの開発をしていきます。

プレイグラウンド

プレイグラウンドを触ってみよう

プレイグラウンドには最初から以下のようなコードが記述されています。詳しい説明の前にこのコードを実行してみましょう。

// app class.
class App extends QratchApp {
  frame() {
    const { cursor, drawer, renderer  } = this

    // clear screen with black.
    renderer.fill('black')

    // draw red circle at cursor position.
    drawer.fillArc(cursor, 32, 0, Math.PI * 2, 'red')
  }
}

// start app.
const options = createCanvasAppOptions(playground.canvas)
const app = new App(options)
app.start()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

右上に「RUN」というボタンがあるので、クリックしてみてください。するとこのような画面が開きます。マウスカーソルを動かすと、画面に表示されている赤い丸が付いてきます。これが先程のコードの実行結果です。

RUN

コードを改造してみよう

次に少しコードを改造してみましょう。コードの7行目にある renderer.fill('black')blackgreen にしてみてください。

// app class.
class App extends QratchApp {
  frame() {
    const { cursor, drawer, renderer  } = this

    // clear screen with black.
    renderer.fill('green')

    // draw red circle at cursor position.
    drawer.fillArc(cursor, 32, 0, Math.PI * 2, 'red')
  }
}

// start app.
const options = createCanvasAppOptions(playground.canvas)
const app = new App(options)
app.start()






 










1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

変更したら実行してみましょう。このように背景が黒色から緑色になったら成功です。

背景が緑色

先程の出てきた renderer.fill() は画面を好きな色で塗りぶす関数です。

例えば下記のように色の名前を指定してあげることで、自分の好きな色で画面を塗りつぶすことができます。

renderer.fill('white')  // 白色で塗りつぶす

背景が白色

renderer.fill('blue')  // 青色で塗りつぶす

背景が青色

renderer.fill('cyan')  // 白色で塗りつぶす

背景が水色

次にコードの10行目を以下のように変更してみましょう。

// app class.
class App extends QratchApp {
  frame() {
    const { cursor, drawer, renderer  } = this

    // clear screen with black.
    renderer.fill('black')

    // draw red circle at cursor position.
    drawer.fillArc(cursor, 16, 0, Math.PI, 'blue')
  }
}

// start app.
const options = createCanvasAppOptions(playground.canvas)
const app = new App(options)
app.start()









 







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

実行すると、青くて小さい半円が描画されます。

青くて小さい半円

先程の drawer.fillArc() は画面に半円を描画する関数で、下記のように引数を指定します。

drawer.fillArc(描画する位置, 半径, 開始角度, 終了角度, '塗りつぶす色')

先程の例だと描画する位置に、 cursor を指定しています。この cursor はコードの 4行目にある this からの オブジェクトの分割代入open in new window により、初期化されています。

// 4行目のオブジェクトの分割代入
const { cursor, drawer, renderer  } = this

cursor はマウスカーソルを管理する Cursor のインスタンスです。

次に半円の開始角度と終了角度に注目してみてください。開始角度には 0 が指定されていますが、終了角度には Math.PI が指定されています。Math.PI はブラウザで定義されている円周率の定数です。

//                         ┌─ 開始角度
drawer.fillArc(cursor, 16, 0, Math.PI, 'blue')
//                            └─────┴─ 終了角度
1
2
3

これは ラジアン角度open in new window と呼ばれる角度です。別名弧度法とも呼ばれます。

普段私達が使っている角度は度数法と呼ばれ、「円周を360等分した弧の中心に対する角度」と定義されています。

一方弧度法は「円の半径に等しい長さの弧の中心に対する角度」と定義されています。分かりやすくすると、度数法の 180度 は弧度法では π(PI:円周率) と表されます。

なので fillArc の開始角度に 0 終了角度に Math.PI を指定しているので、度数法に直すと開始角度が 0度 終了角度が 180度 となり、半円となります。

drawer.fillArc(cursor, 16, 0, Math.PI, 'blue')

青い半円

また最初のコードのように開始角度が 0 終了角度が Math.PI * 2 は開始角度が 0度 終了角度が 360度 となり、完全な円になります。

drawer.fillArc(cursor, 16, 0, Math.PI * 2, 'blue')

青い円

フレームについて

普段私達がプレイしているゲームは、連続で画面が更新され動いているように見えます。いわばパラパラ漫画のようなものです。

例えば下のようにキャラクターが動いている様子を1枚1枚連続して表示させることで、ゲーム画面で「キャラクターがジャンプする」というアニメーションを描画しているのです。

キャラクターがジャンプするときのフレームのイメージ

また、この画像1枚1枚のことを「フレーム」と呼び、特定のフレームを指すときは「〇〇フレーム目」と呼びます。

フレームのイメージ

Qratch では下のように frame 関数の中にフレームの描画処理を記述することができます。

class App extends QratchApp {
  frame() {
    const { renderer } = this

    renderer.fill('black')

    // フレームの描画処理
    // ...
  }
}

なぜフレームごとに renderer.fill を呼び出すかというと、フレームを描画するときに、前のフレームで描画されたものを消すためです。試しに、 renderer.fill をコメントアウトしてデモのコードを動かしてみましょう。

class App extends QratchApp {
  frame() {
    const { cursor, drawer, renderer  } = this

    // renderer.fill('green')

    // draw red circle at cursor position.
    drawer.fillArc(cursor, 32, 0, Math.PI * 2, 'red')
  }
}




 





1
2
3
4
5
6
7
8
9
10

実行すると下のように1フレーム1フレームが重なって見えます。ゲームなどは通常、画面を塗りつぶしてからキャラクターなどの描画を行うため、フレームの描画の前には一度 renderer.fill を呼び出すようにしましょう。

1フレーム1フレームが重なっている

またゲームなどをやっていると FPS(Frame Per Seconds:フレームレート)open in new window という言葉を耳にします。これは1秒間に処理できるフレームの数のことです。この FPS が高ければ高いほど、ゲームは滑らかに動くようになります。

Qratch は通常 60 FPS で動作するため、滑らかに動きます。試しに、FPSを 30 FPS に制限してみましょう。

デモコードの App クラスに下のような init 関数を追加しましょう。ticker.setTargetFPS() 関数は引数で渡された値を FPS の目標値に設定します。

// app class.
class App extends QratchApp {
  init() {
    this.ticker.setTargetFPS(30)
  }

  frame() {
    const { cursor, drawer, renderer  } = this

    // clear screen with black.
    renderer.fill('black')

    // draw red circle at cursor position.
    drawer.fillArc(cursor, 32, 0, Math.PI * 2, 'red')
  }
}


 
 
 











1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

実行してみましょう。マウスカーソルを動かすと円が動きますが、少し粗い動きになっています。

30FPS

QratchApp クラス

デモコードには QratchApp クラスを継承した App クラスが定義されています。

class App extends QratchApp {
  frame() {
    // ...
  }
}

QratchApp クラスは Qratch でアプリ開発するための抽象クラスです。QratchApp を継承したクラスは必ず frame() 関数を実装する必要があります。

そして QratchApp を継承しただけではアプリは動きません。でもコードの15行目から17行目には下のようなコードがあります。

/* 15|*/ const options = createCanvasAppOptions(playground.canvas)
/* 16|*/ const app = new App(options)
/* 17|*/ app.start()

まず15行目です。

const options = createCanvasAppOptions(playground.canvas)

この行では createCanvasAppOptions() 関数を呼び出しています。この関数は HTMLCanvasElement を引数に取り、Qratch のアプリを動かすのに必要なオプションを生成しています。生成されるオプションの中には下記のようなものが含まれます。

  • cursor: HTMLElementCursor
    • カーソルを管理するクラスです。
  • keyboard: HTMLElementKeyboard
    • キー入力を管理するクラスです。
  • mouse: HTMLElementMouse
    • マウス入力(クリックなど)を管理するクラスです。
  • renderer: CanvasRenderer
    • CanvasRenderingContext2D への描画APIを提供するクラスです。
  • ticker: RequestAnimationFrameTicker
    • requestAnimationFrame() 関数を利用したフレームを管理するクラスです。
    • QratchApp クラスの frame() メソッドはこの ticker により呼び出されます。

また createCanvasAppOptions() への引数に playground.canvas を渡しています。playground.canvas はプレイグラウンドで実行される際に生成される HTMLCanvasElement インスタンスが代入されています。

次に16行目です。

const app = new App(options)

この行では先程生成した optionsApp クラスのコンストラクタに渡して、インスタンスを生成しています。

最後に17行目です。

app.start()

この行はアプリケーションの init メソッドを呼び出し、ticker によるフレームの呼び出しを開始します。

最後に

以上でステージ0は終了です 🎉。お疲れ様でした!