5. JS魔法修行: (3) "察する" という技術
5-1. モンスターを動かす
ちょっと 飽きてきた かも知れません。一つ前が長かったですからね。
そろそろアニメーション をやりましょうか?
そろそろ
前回の setInterval がありました。ミリ秒単位でぐるぐる回すやつ ですね。実は世の中のゲームというものは、多かれ少なかれ、あんな感じで無限にぐるぐる回る処理ループを使っています。これは イベントループ と 呼ばれるのでした ね。
それでゲーム中にイベントが発生すると (プレイヤーがジャンプのボタンを押す、とかね) そのイベントをキャッチし、対応する動きを画面に描き出します。これをやってみましょう。でもまず "動かすもの" が必要です。四角形のブロック にしましょうか。
まずは現在の HTML を見てみます。
第五章なので web_05.html とします。
第五章なので web_05.html とします。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Test</title>
<link href="style_05.css" rel="stylesheet">
<script type="text/javascript" src="app_05.js"></script>
</head>
<body>
<div>
<button onclick="startCheck()">Start</button>
</div>
<div>
<button onclick="stopCheck()">Stop</button>
</div>
<div class="extra-large">
<div class="color-1">Hello</div>
<div class="color-2">World</div>
<div class="color-3">You</div>
<div class="color-4">Are</div>
<div class="color-5">Beautiful</div>
</div>
</body>
</html>
Start ボタンの上に、四角形のブロックを置いてみます。
この四角形のブロックを monster と呼ぶことにしましょう。
この四角形のブロックを monster と呼ぶことにしましょう。
<div id="monster"></div> というのを追加しました。
でも id="monster" って何でしょうね。気になりますね・・・
でも気にせずに進めることにしましょうか・・・
でも気にせずに進めることにしましょうか・・・
CSS をいじります。style_05.css です。
現在はこうなっています。
.extra-large { font-size: 100px; }
.color-1 { color: crimson; }
.color-2 { color: gold; }
.color-3 { color: limegreen; }
.color-4 { color: dodgerblue; }
.color-5 { color: rebeccapurple; }
さて、HTML に追加した <div id="monster"></div> がありました。CSS を利用してこれにスタイルを適用することにします。以前は class というものにスタイルを適用する方法を学びました。でも今回のこれは id です。これはどうやって指定するんでしょうか。まだ学んでいません。
class を指定するときは先頭に . をくっ付ければいいんでしたよね。id の場合は # をくっ付けると指定できます。次のような感じです。
#monster {
width: 20px;
height: 20px;
background-color: limegreen;
margin-bottom: 10px;
}
.extra-large { font-size: 100px; }
.color-1 { color: crimson; }
.color-2 { color: gold; }
.color-3 { color: limegreen; }
.color-4 { color: dodgerblue; }
.color-5 { color: rebeccapurple; }
ファイルを保存し、ブラウザを更新してみましょう。
こうなっているはずです。
一つずつ見ていきます。
width と height というのは CSS でよく使われる属性です。両方とも 20px を指定しています。これで monster という <div> が四角形になります。
次に background-color として limegreen が指定されています。これは何でしょうか。想像力が大切です。background-color なので 「背景色」 です。
そして最後に margin-bottom に 10px が指定されています。これも想像してみましょう。monster の真下 (つまり Start ボタンの手前まで) の部分に 「マージン (隙間)」 として 10px を割り当てているのです。
次は app_05.js です。
結構な量を追加しています。
めげるかも知れません。
めげないで下さい。
めげてはいけない理由 は少しあとで説明します。
めげるかも知れません。
めげないで下さい。
めげてはいけない理由 は少しあとで説明します。
まずはコードを見てみましょう。
そして何も考えずに書き写します。
そして何も考えずに書き写します。
let pokemon = null;
let monster = null;
let x = 0; // 画面の x 軸で monster が進んだ距離
// ブラウザが読み込まれたら initMonster を実行するようブラウザに伝える
window.onload = initMonster;
// ブラウザが読み込まれたら実行される関数
function initMonster() {
// ここで monster という ID の要素を取得する
monster = document.querySelector('#monster');
}
function startCheck() {
if (!pokemon) {
pokemon = setInterval(check, 50); // 0.05 秒に一回
}
}
function stopCheck() {
if (pokemon) {
clearInterval(pokemon);
pokemon = null;
}
}
function check() {
if (navigator.onLine) {
console.log('yes!');
} else {
console.log('no...');
}
// もし monster という要素が取得できていたら中に入る
if (monster) {
// 位置 x が 100 に達していたら 0 に戻す
if (x >= 100) {
x = 0;
}
// 位置 x を 10 ずつ増やす
x = x + 10;
// monster 要素の margin-left を指定する
monster.style.marginLeft = x + 'px';
}
}
ちゃんと書き写しましたか?
ファイルを保存し、ブラウザを更新してみましょう。
Start ボタンを押すと、四角形が 左右に動き始め ます。
Start ボタンを押すと、四角形が 左右に動き始め ます。
ソースコードはページの一番下でダウンロードできます
5-2. "察する" という技術
さて 次のセクションでプログラムの解説を行います。
しかしその前に知っておいて欲しいことがあります。
しかしその前に知っておいて欲しいことがあります。
それは "プログラミングに要求される能力" のことです。
開 発者たちが上司に新しいプロジェクトを言い渡されると、必要となる技術について調べ始めます。それらの技術をアプリケーションに組み立て、要求されるサービスとして運用を開始するまで、そのときから 開発者たちの悪夢のような日々 が始まります。
私たちがやっているチュートリアルのようなものが提供元に置いてあればいいですが、普通は簡単な説明書きがあるだけで、あとは自力で調べなければなりません。どうやって調べるかというと、まずは手元の仕様書があるでしょう。しかし仕様書も大半はお粗末であることが多いものです。そうすると開発者は頭を激しくかきむしり始めます。そして開発者は提供されるそのプログラムの "ソースコード" を読むことを余儀なくされます。
開発者たちの誰もが知っていることがあります。それは 「プログラミング言語を学ぶには他人のコードを読むのが一番だ」 ということです。
実は プログラミングで最も重要なこと がここに隠されています。
チュートリアルの冒頭にも書きましたが、一般のプログラミングで重視されるのは "論理的な思考" です。教育の目標はここに焦点が置かれます。しかし長いことプログラムを書いていると、本質はそこじゃないのではないか、と疑問を感じます。
プログラムを実際に書き始めると、通常は 別の誰かが作ったライブラリやその関数 を使うことになります。そうすると関数やモジュールの名称、またはそこに展開される処理の前後の流れなどから 何となくプログラムの動作を察する ということが重要になってきます。
"論 理的な思考" というのは確かに大切なのです。でも前に "料理" の喩えでも書いたように 「包丁はどのように作られているのか」 などと考えていたら、いつまでたっても料理はできません。必要なのは道具についての知識ではなく、どんな材料が揃っており、どうやって調理すればいいかを "察する力" です。
そして "他人のソースコードを読む" というのは、その力を養うための最高の方法です。
5-3. モンスターを動かす: 解説編
そういうわけで 他人のソースコードを読む ということを嬉々としてやっていきましょう!
今回みたいに長いコードというのは読むだけで苦労するかも知れませんが、でもそれだけ "得るもの" は多いはずですので。
今回みたいに長いコードというのは読むだけで苦労するかも知れませんが、でもそれだけ "得るもの" は多いはずですので。
(a) DOM とその読み込みのこと
まずは window.onload = initMonster という表現の周辺からみていきましょう。
let monster = null;
// ブラウザが読み込まれたら initMonster を実行するようブラウザに伝える
window.onload = initMonster;
// ブラウザが読み込まれたら実行される関数
function initMonster() {
// ここで monster という ID の要素を取得する
monster = document.querySelector('#monster');
}
まず window.onload の onload について考えます。
onclick も onload も関数名がどちらも on で始まります。on の意味は 「4-2. ボタンでプログラム実行」 でも触れました。「on〜」 という関数名は他のプログラミング言語でも 「何かが起きたとき」 を表現するのによく使われます。
onload という表現を素直に察すれば、おそらくこれは "何かを読み込む" という意味でしょう。そして window と言っているのですから、おそらく "window が読み込まれたとき" という意味になのでしょう。
何度も言いますが、名前から察する、ということがプログラミングの真骨頂です。
じゃあ次は window に行きましょう。こっちは難しいのでちょっと説明が必要です、、、、
<div> や <button> などのことを 「タグ」 といいました。< で始まり > で終わります。「タグ」 はページの要素たちを 「人間にとって理解しやすくするための表現形式」 です。
タグで表現されるこれらの要素たちは、ブラウザによって HTML ページが読み込まれるときに DOM と呼ばれる "ツリー構造のデータ" に変換されます。DOM はページの "要素" たちを 「ブラウザにとって理解しやすくするための表現形式」 です。
DOM を 「ツリー構造になっているデータ」 としてイメージしてみて下さい。
一番上には window オブジェクトが鎮座しています。
二番目には document オブジェクトがぶら下がっています。
三番目には body オブジェクトがぶら下がっています。
その下に、それ以外のありとあらゆる要素たちが枝分かれしてぶら下がっていきます。
二番目には document オブジェクトがぶら下がっています。
三番目には body オブジェクトがぶら下がっています。
その下に、それ以外のありとあらゆる要素たちが枝分かれしてぶら下がっていきます。
ツリー構造になっているので、たとえば <div> の中をものすごい数の "入れ子構造" にしてどんなに大量の <div> を入れても、ブラウザはちゃんとこれを理解することができます。
window のほうの説明は以上です。
DOM というのは Document Object Model の略です。
ちなみに上の話では 「ツリー構造」 だとか 「オブジェクト」 だとか、なんか難しそうな用語が出てきますが、これらは 「何となくこんなものかな」 ぐらい曖昧に把握できていれば十分です。現場で働く開発者たちでさえ、DOM に対する理解はそんなものです。
またちょっと onload について補足します。
「クリックする」 とか 「読み込まれる」 などといった "出来事" のことを 「イベント」 と呼びます。そのまんまですね。それで、onload や onclick などは 「イベント・リスナー」 と呼ばれます。ようするに 「イベントにずっと耳を傾けて聞き続けるやつら」 です。
だから今回の window.onload というのをプログラマーの言葉に翻訳すると 「window の読み込みイベントをずっと聞いておく」 という表現になります。
ちょっと脱線しますが window.onload を今回使った意図を説明します。今回は onload で window の読み込みイベントをずっと "聞いておく" ようにしました。でもこれは document の読み込みイベントでもよかったのです。つまり どの要素に仕掛けてもよかった のです。やりたいことが実現できれば、それでいいのです。
でもなんで window の "読み込み" がそんなに重要なのでしょう?
それを次に説明していきます。
それを次に説明していきます。
(b) 読み込むタイミングのこと
では window の "読み込み" の重要性について。
物事というものには "タイミング" があります。
ちょっと initMonster 関数を見て下さい。
// ブラウザが読み込まれたら実行される関数
function initMonster() {
// ここで monster という ID の要素を取得する
monster = document.querySelector('#monster');
}
ここで querySelector という不可解な関数が実行され、結果を monster に入れています。
querySelector は 「その DOM の中の要素をすべて検索してくれる関数」 です。したがって document.querySelector とやると、querySelector は documentの配下を検索して くれます。
でも何を検索したかったのでしょう?
#monster です。
querySelector の仕様をみると色々と複雑な検索を行うためのセレクターの記述方式が書いてあり、それはそれですごくエキサイティングなのですが、いずれにせよ、この #monster というセレクター記述で検索する対象は
です。これを検索する。
そして見つかったら、その要素を monster という変数に入れたい。
そして見つかったら、その要素を monster という変数に入れたい。
しかし物事には "タイミング" があります。
もし test.js の中身が以下だけだとします:
let monster = document.querySelector('#monster');
実はこれだけだと monster 変数の中には何も入りません!
ブラウザが すべての DOM を読み込み終え、準備ができて からじゃないと、#monster を検索しても #monster はまだ存在していない のです。そのために window.onload = initMonster が必要になります。
だからこうしています。
window が読み込まれるのを待ち、読み込まれたら initMonster を実行する。そして取得された #monster 要素を monster 変数の中に入れる。
let monster = null;
// ブラウザが読み込まれたら initMonster を実行するようブラウザに伝える
window.onload = initMonster;
// ブラウザが読み込まれたら実行される関数
function initMonster() {
// ここで monster という ID の要素を取得する
monster = document.querySelector('#monster');
}
(c) モンスターの移動の仕組み
次は 「モンスターの移動」 について説明していきます。
let pokemon = null;
let monster = null;
let x = 0; // 画面の x 軸で monster が進んだ距離
....
....
....
function check() {
if (navigator.onLine) {
console.log('yes!');
} else {
console.log('no...');
}
// もし monster という要素が取得できていたら中に入る
if (monster) {
// 位置 x が 100 に達していたら 0 に戻す
if (x >= 100) {
x = 0;
}
// 位置 x を 10 ずつ増やす
x = x + 10;
// monster 要素の margin-left を指定する
monster.style.marginLeft = x + 'px';
}
}
最終的にやりたいこと、つまり 「モンスターを動かす」 を表現している箇所を見ていきます。
コードの一番下にあります。
コードの一番下にあります。
// monster 要素の margin-left を指定する
monster.style.marginLeft = x + 'px';
これです。
この部分は何をやっているのでしょう?
この部分は何をやっているのでしょう?
monster.style.marginLeft
長ったらしいこの表現、、、、
見てすぐに拒絶せずに、
開発者たちの "努力の結晶" である 「名前」 から彼らの意図を汲んであげましょう。
見てすぐに拒絶せずに、
開発者たちの "努力の結晶" である 「名前」 から彼らの意図を汲んであげましょう。
monster の中には、どうやら style というのがあるようだ。
さらにそこには marginLeft というものがあるようだ。
さらにそこには marginLeft というものがあるようだ。
ここまでは "察する" ことができますか?
取得された monster は DOM という "オブジェクト" です (これは次の章で説明します) 。そして DOM オブジェクトには色々な機能が用意されています。そのうちの一つが style です。
スタイルを操作するのは本来は "CSS の役目" ですが、これは JS からも操作できます。それを可能にするのが style です。ここでは 「JS から monster のスタイルを操作しようとしている」 のです。
では marginLeft の意味を推測してみましょう。
えっと... 「monster の左側の隙間」?
正解!
そこに x + 'px' を指定しています。
じゃあ x って何でしょう?
一つ上の行にこんなのがあります。
// 位置 x を 10 ずつ増やす
x = x + 10;
こんなふうにコメントがあると助かりますね。
つまり 0.5 秒 に一回 check が実行されるたびに marginLeft が 10px ずつ増えるのでしょう。
でもこれだと monster は永久に右へと移動し続けて、しまいには ブラウザの外側に出てしまい ます。
つまり 0.5 秒 に一回 check が実行されるたびに marginLeft が 10px ずつ増えるのでしょう。
でもこれだと monster は永久に右へと移動し続けて、しまいには ブラウザの外側に出てしまい ます。
そのために一つ上の行があります。これがそれを防いでくれます。
// 位置 x が 100 に達していたら 0 に戻す
if (x >= 100) {
x = 0;
}
これで monster がブラウザの外側に出てしまうことはありません。
ふー。お疲れさまです。
何となく見えてきましたか?
何となく見えてきましたか?
他人のソースコードを読むとき、開発者たちはこんなふうに ある程度、割り切る のです。
そうしないければ一生かかっても仕事が終わらないからです。
あまり考えすぎず 察する ことの重要性が分かりましたか?
そうしないければ一生かかっても仕事が終わらないからです。
あまり考えすぎず 察する ことの重要性が分かりましたか?
これで アニメーションのやり方 を覚えました。
ゲームなどのアニメーションは、だいたいこんな仕組みで動いているのです。
ゲームなどのアニメーションは、だいたいこんな仕組みで動いているのです。