Snake Time 01
Snake Time 01
index.html
にcanvas
エレメント設定しよう
index.html
の<body></body>
の中に<canvas>
エレメントを作成しよう。
<canvas id="field"></canvas>
script.js
でcanvas
エレメントを取得し Context も取得しよう
script.js
にcanvas
エレメントを取得し、canvas
エレメントから Context を取得しよう
// index.htmlのcanvasエレメントを取得する
const canvas = document.getElementById("field")
// canvasのContextを取得する
let ctx = canvas.getContext("2d")
// canvasが巨大なオブジェクトだと確認をしよう
// (たくさんの機能が詰まっている)
console.log(ctx)
canvas.width
とcanvas.height
について
canvas
エレメントにはwidth
とheight
属性がある
それを現在のブラウザの大きさに設定しよう
// script.js
// canvasのwidthにwindow.innerWidthはブラウザ画面の幅を設定する
canvas.width = window.innerWidth
// canvasのheightにwindow.innerWidthはブラウザ画面の幅を設定する
// つまり正方形を作る
canvas.height = window.innerWidth
// 現在のブラウザの画面の高さ・幅をログしてみよう
console.log(window.innerWidth)
console.log(window.innerHeight)
初期化関数(init()
)を作成しよう
関数は作ったら呼び出さないとコードは走らない。init()
を宣言したすぐ下にinit()
を呼び出そう。
ブラウザで走らせ、コンソールに"In init()!!"
が表示されたら成功。
// 画面を開いたら呼び出す関数
// index.htmlの<body onload="init()">で呼ばれる
// eslint-disable-next-line no-unused-vars
function init() {
console.log("In init()!!")
}
// 呼び出す
init()
drawGrid()
を宣言しよう No. 1
画面に Grid を描画するdrawGrid()
関数を宣言しよう。
まずは画面に四角を描画してみよう。
関数は宣言するだけではダメ。
init()
関数の中にdrawGrid()
を呼び出そう。
// 画面のグリッドを描画する
function drawGrid() {
// 色を設定する
ctx.fillStyle = "#00bfff"
// fillRect(開始x座標, 開始y座標, 幅, 高さ)
// 開始座標は四角の左上の角
ctx.fillRect(0, 0, canvas.width, canvas.height)
}
drawGrid()
を宣言しよう No. 2
今度は四角ではなく、グリッドの線を書こう。 そのために canvas 上にグリッドを一つ一つを四角で描画しよう。
for
ループが二重にすることよって縦と横を2次元でループをすることができる。
i
が x を表していて、j
が y を表している。
strokeRect()
を使用することによって塗りつぶされた四角ではなく、四角の外枠を描画することができる。
// 画面のグリッドを描画する
function drawGrid() {
// グリッドの線の太さ
ctx.lineWidth = 0.1
// グリッドの色
ctx.fillStyle = "rgba(200,200,200,0.8)"
// グリッド、縦横の数は示す数字
// 以下の場合は10x10のグリッド
const GRID_SIZE = 10
// 1セルのサイズ(ピクセル)
const SIZE = canvas.width / GRID_SIZE
// グリッドを描画する
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
ctx.strokeRect(i * SIZE, j * SIZE, SIZE, SIZE)
}
}
}
グローバル変数は上に
グローバル変数はプログラム内の様々な場所で使用する変数のこと。
様々な場所で使用するから特定の関数に入れずにファイルの一番上に移動をしよう。
GRID_SIZE
とSIZE
は他の場所で使用するので移動をしよう。
*SIZE
はcanvas
変数を使用しているため、canvas
変数が宣言されている後にSIZE
変数を宣言しよう。
// script.js
// ファイルの上部
// グリッド、縦横の数は示す数字
// 以下の場合は10x10のグリッド
const GRID_SIZE = 10
// 1セルのサイズ(ピクセル)
const SIZE = canvas.width / GRID_SIZE
// ...(省略)
// 画面のグリッドを描画する
function drawGrid() {
// グリッドの線の太さ
ctx.lineWidth = 0.1
// グリッドの色
ctx.fillStyle = "rgba(200,200,200,0.8)"
// グリッドを描画する
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
ctx.strokeRect(i * SIZE, j * SIZE, SIZE, SIZE)
}
}
}
円を描画しよう No. 1
drawCircle()
関数を宣言し、円を描画しよう。
円を描くのは少し特殊で、ctx.arc()
という関数を使う。
ctx.arc()
を使用する前にctx.beginPath()
を呼び出し、「これから描くよ」ということを宣言する。
その後、ctx.arc()
で弧を描きそれを円にする。
ctx.fill()
で円を塗りつぶす。
関数は宣言するだけではダメ。
drawCircle()
をinit()
の中に呼び出そう。
画面の真ん中にピンクの円が表示されれば成功。
// 円を描画する
function drawCircle() {
// 色を設定
ctx.fillStyle = "lightcoral"
// 描くことをはじめる
ctx.beginPath()
// 円を描く
ctx.arc(
canvas.width / 2, // 画面の真ん中
canvas.height / 2, // 画面の真ん中
50, // 半径
0, // 弧は0度から
360, // 弧は360度まで
false // 逆向きに描きますか?
)
// 円の中を塗りつぶす
ctx.fill()
}
円を描画しよう No. 2
drawCircle()
をパラメータを受け取れるように変更をしよう。
point
という名前のパラメータ
- グリッド上の座標を格納しているオブジェクト
- ex.
{x: 2, y: 1}
は左から3番目、上から2番めの座標を指す
color
という名前のパラメータ
- 描画される色を表す
- ex.
blue
とか#024234
円をセルの真ん中に描画するためのコードを理解しよう。
point.x * SIZE + SIZE / 2
ポイントは+ SIZE / 2
をすることによってセルの左上に描かれてしまう円を真ん中にずらすこと。
宣言した新しいdrawCircle()
を引数を与えてinit()
の中に呼び出そう。
*x=3,y=4 の座標に青い丸が表示されれば成功。
// 円を描画する
function drawCircle(point, color) {
// 色を設定
ctx.fillStyle = color
// 描くことをはじめる
ctx.beginPath()
// 円を描く
ctx.arc(
point.x * SIZE + SIZE / 2, // CELLの真ん中に描画するため
point.y * SIZE + SIZE / 2, // CELLの真ん中に描画するため
SIZE / 2,
0,
360,
false
)
// 円の中を塗りつぶす
ctx.fill()
}
function init() {
// (省略)
drawCircle({ x: 3, y: 4 }, "royalblue")
}
snakes
配列を表示しよう
snakes
配列の中のポイント({x: 2, y: 3}
)を表示するdrawSnake()
関数を宣言しよう。
配列を表示するためにはfor
ループを使用しよう。
snakes
配列変数を作成し、配列の中にいくつかのポイントを入れよう。
drawSnake()
関数を宣言したあとはinit()
関数の中で呼び出そう。
*画面に縦に3つの青い丸が表示されれば成功。
// snakeの体を入れておく配列
let snakes = [
{ x: 5, y: 5 },
{ x: 5, y: 6 },
{ x: 5, y: 7 },
]
// snakesを描画する関数
function drawSnake() {
for (let i = 0; i < snakes.length; i++) {
drawCircle(snakes[i], "royalblue")
}
}
function init() {
// (省略)
drawSnake()
}
foods
配列を表示しよう
foods
配列の中のポイント({x: 2, y: 3}
)を表示するdrawFood()
関数を宣言しよう。
配列を表示するためにはfor
ループを使用しよう。
*画面に 3 つの緑色の円が表示されれば成功。
// 画面にあるfoodを入れておく配列
let foods = [
{ x: 1, y: 3 },
{ x: 8, y: 4 },
{ x: 4, y: 7 },
]
// foodsを描画する関数
function drawFood() {
for (let i = 0; i < foods.length; i++) {
drawCircle(foods[i], "yellowgreen")
}
}
for
ループを逆から呼び出し描画しよう
アニメーションをする際に、foods
配列、snakes
配列ともに中身が入れ替わること多い。
snake
がfood
を食べた時にsnakes
配列やsnakes
配列からポイントを削除したりする。
その時に配列の頭から描画するとバグが起こるので、for
ループを逆から描画しよう。
関数を宣言した後、同じように動くかを確認しよう。
*現在は変更前と全く変わらない挙動をする。 *ゲーム中のアニメーションが激しくなり、snakes や foods の配列が増えたり、足されたりする時にエラーが起こらなくなるためにやっていること。
// foodsを描画する関数
function drawFood() {
// iはfoodsの長さからはじまる
// iが0になるまでループ
// ループごとにiを1減らす
for (let i = foods.length - 1; i >= 0; i--) {
drawCircle(foods[i], "yellowgreen")
}
}
// snakesを描画する関数
function drawSnake() {
// iはsnakesの長さからはじまる
// iが0になるまでループ
// ループごとにiを1減らす
for (let i = snakes.length - 1; i >= 0; i--) {
drawCircle(snakes[i], "royalblue")
}
}
ランダムな座標にfood
を描画しよう
food
がランダムな場所に描画されるようにポイントを作成しよう。
Math.random()
関数とGRID_SIZE
グローバル変数を使ってランダムの範囲を設定しよう。
*緑の food がランダムな場所に表示されれば成功。
// 画面にあるfoodを入れておく配列
let foods = [
{
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE),
},
]
main()
、update()
、draw()
関数を宣言し、コードを整理しよう
今後、コードが増えていくに連れ、わかりにくくなってくる。 それを防ぐためには役割ごとの関数を宣言することが必要になる。
main()
- フレームごとに呼び出されるメインの関数。
- この中に全てのコードを集める、呼び出す。
- このなかで
update()
とdraw()
を呼び出す。
update()
- 1フレームごとの変化をここで計算をする。
- 例えば、
snake
が動く座標を計算し、snakes
変数をアップデートする。
draw()
- アップデートされたデータを使って描画する。
// 画面にあるfoodを入れておく配列
let snakes = [
{ x: 5, y: 5 },
{ x: 5, y: 6 },
{ x: 5, y: 7 },
]
// 画面にあるfoodを入れておく配列
let foods = [
{
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE),
},
{
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE),
},
]
function main() {
// update関数で変数をアップデート
update()
// draw関数で描画する
draw()
}
function update() {}
function draw() {
// グリッドを描画する
drawGrid()
// foodsを描画する
drawFood()
// snakesを描画する
drawSnake()
}