2025-07-16
JavaScript
ICT基礎 第14回 補助資料
本ページのサンプルファイル集
下記のどちらかからからダウンロードできます
WebClassの授業資料
Windowsの人は必ずzipファイルを展開すること!! zipファイルの中では作業できません.
恐竜ゲーム風のミニゲーム
恐竜ゲーム (目標物の元の例)
Chromeにはインターネットにつながらない時などに遊べる簡単なゲームが用意されています.
実際に, Chromeで下記
xxxxxxxxxx
chrome://dino
をURL部分に入力し, Enter or Returnを押してみてください. すると, 下図のような画面が出てきます.
ここで, 実際にSpaceキーを押すと, 恐竜が横に進んでいき, サボテン等をジャンプして乗り越えるゲームが始まります.
今回は, これに似た簡単なゲームを作ることを目標にやっていきたいと思います.
htmlとjsの準備
まずは, htmlとjsの用意をします.
index.html
xxxxxxxxxx
<html lang="ja">
<head>
<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>Game</title>
</head>
<body>
<canvas width="600" height="400" id="gameCanvas" style="border: solid 1px;"></canvas>
<script src="./app.js"></script>
</body>
</html>
canvas
タグの中にゲーム画面を描いていきますcanvasの横幅600px, 高さ400pxで固定しています. 簡単のため, レスポンシブデザインとして画面サイズにフィットさせるようにはしていません. 描画領域がわかりやすいように,
style="border: solid 1px;"
で枠線を入れています後にJavaScript内で指定するために
id="gameCanvas"
でidを追加しています次節以降ではこの
index.html
を変更せずにやっていきます
app.js
xxxxxxxxxx
console.log("hello");
ゲームの初期処理とループ処理
本節以降ではindex.html
を変更しないため, app.js
の内容のみを記載します
app.js
xxxxxxxxxx
let canvas;
let ctx;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
setInterval("loop()", 1000);
}
function loop(){
console.log("Loop!!");
}
setup
という関数を用意し, 実行していることで, ここがゲームの起点 (初期処理) となります.xxxxxxxxxx
canvas = document.querySelector("#gameCanvas");
index.html
内のgameCanvas
というidを持ったcanvasタグを取得し,canvas
という名前の変数に入れています.xxxxxxxxxx
ctx = canvas.getContext("2d");
今回は2次元の横スクロールゲームなので, canvasの中の表示形式を
2d
にします (3D表示のゲーム等を作りたい場合はwebgl
にします)
xxxxxxxxxx
setInterval("loop()", 1000);
xxxxxxxxxx
function loop(){
console.log("Loop!!");
}
setInterval
という関数を使って,loop
という関数を1000ミリ秒ごとに実行する設定をします.実際には, 60FPS (60 frames per second. 1秒に60回描画) で実施することが多いため,
として,setInterval("loop()", 16);
程度にすることが多いです.
ゲームの状態と条件分岐 (if, else)
ゲームを実行している時と, ゲームオーバーの時とで場合分けをします.
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
setInterval("loop()", 1000);
}
function loop(){
if (isPlaying){
console.log("playing!!");
}
else {
console.log("game over!!");
}
}
xxxxxxxxxx
if (条件A){
条件Aを満たす場合の処理
}
else {
条件Aを満たさない場合の処理
}
という形で, 条件分岐を実施します.
ゲーム中 (
true
), ゲームオーバー後 (false
) を司る変数としてisPlaying
を用意しました
背景 (水面の線)
プレイヤーが動く高さのベースとなる水面の線を描きます
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
//console.log("playing!!");
drawBackground();
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
プレイヤー画像の表示
プレイヤーの画像 (赤い魚. fish.png
) を表示します.
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSize = 50;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
//console.log("playing!!");
drawBackground();
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
xxxxxxxxxx
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSize = 50;
プレイヤー (魚) の変数の宣言
xxxxxxxxxx
playerImg = new Image();
playerImg.src = "./fish.png";
fish.png
という画像を読み込む
xxxxxxxxxx
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
画像の表示
その他
xxxxxxxxxx
setInterval("loop()", 16);
60fps (16ミリ秒) に変更
xxxxxxxxxx
//console.log("playing!!");
コンソールがこのログで埋まってしまうのでコメントアウト
xxxxxxxxxx
//console.log("game over!!");
コンソールがこのログで埋まってしまうのでコメントアウト
キー入力
ジャンプの準備としてキー入力ができるようにします
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSize = 50;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
//console.log("playing!!");
drawBackground();
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function keydown(e){
console.log(e.code);
}
xxxxxxxxxx
document.onkeydown = keydown;
xxxxxxxxxx
function keydown(e){
console.log(e.code);
}
keydown
という関数を作成し, キーボードが入力された場合の処理を書けるようにするe.code
で実際に入力されたキーを取得できる
プレイヤーの更新・ジャンプ
スペースキーが入力されたらジャンプするようにします.
ジャンプは, 物理の基本的な式
から, 重力下での物体の加速度, 速度, 位置の関係は
のように決まります.
これを使って, 経過時間を1とした場合の物体の速度, 位置を更新してみましょう.
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSpeed = 0;
let playerAcc = 0;
let playerSize = 50;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
//console.log("playing!!");
drawBackground();
updatePlayer();
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
}
function keydown(e){
console.log(e.code);
if (e.code == "Space"){
playerSpeed = -20;
playerAcc = 1.5;
}
}
xxxxxxxxxx
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
毎フレームの背景を白にすることで, アニメ的な表現ができるようになる
xxxxxxxxxx
if (e.code == "Space"){
playerSpeed = -20;
playerAcc = 1.5;
}
spaceキーを押したときにプレイヤーの速度 (上向き) と加速度 (下向き) を与える
xxxxxxxxxx
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
:
:
}
xxxxxxxxxx
updatePlayer();
プレイヤーの速度, 位置を更新
xxxxxxxxxx
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
プレイヤーがジャンプで着地したら位置を固定して, 速度と加速度を止める
敵画像の表示
敵の画像 (サメ. shark.png
) を表示します.
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSpeed = 0;
let playerAcc = 0;
let playerSize = 50;
let enemyImg;
let enemyX = 600;
let enemyY = gndY;
let enemySize = 50;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
enemyImg = new Image();
enemyImg.src = "./shark.png";
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
//console.log("playing!!");
drawBackground();
updatePlayer();
ctx.drawImage(enemyImg, enemyX - enemySize / 2, enemyY - enemySize / 2, enemySize, enemySize);
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
}
function keydown(e){
console.log(e.code);
if (e.code == "Space"){
playerSpeed = -20;
playerAcc = 1.5;
}
}
xxxxxxxxxx
let enemyImg;
let enemyX = 600;
let enemyY = gndY;
let enemySize = 50;
サメ用の変数の準備
xxxxxxxxxx
enemyImg = new Image();
enemyImg.src = "./shark.png";
サメ画像の読み込み
xxxxxxxxxx
ctx.drawImage(enemyImg, enemyX - enemySize / 2, enemyY - enemySize / 2, enemySize, enemySize);
サメ画像の表示
敵をランダムな速さで動かす
サメを右から左に動かします
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSpeed = 0;
let playerAcc = 0;
let playerSize = 50;
let enemyImg;
let enemyX = 600;
let enemyY = gndY;
let enemySpeed = -10;
let enemySize = 50;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
enemyImg = new Image();
enemyImg.src = "./shark.png";
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
//console.log("playing!!");
drawBackground();
updatePlayer();
updateEnemy();
ctx.drawImage(enemyImg, enemyX - enemySize / 2, enemyY - enemySize / 2, enemySize, enemySize);
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
}
function updateEnemy(){
enemyX = enemyX + enemySpeed;
if (enemyX < 0){
enemyX = canvas.width + 100;
enemySpeed = - (Math.random() * 5 + 5);
}
}
function keydown(e){
console.log(e.code);
if (e.code == "Space"){
playerSpeed = -20;
playerAcc = 1.5;
}
}
xxxxxxxxxx
let enemySpeed = -10;
サメの初期速度を定義
xxxxxxxxxx
updateEnemy();
xxxxxxxxxx
function updateEnemy(){
enemyX = enemyX + enemySpeed;
if (enemyX < 0){
enemyX = canvas.width + 100;
enemySpeed = - (Math.random() * 5 + 5);
}
}
サメの速度によってサメの位置を更新
サメが左端に到達したら, 右端に移動するようにする
その際にサメの速さを-10から-5までの値になるように指定
Math.random()
は0から1までのランダムな値を出す関数Math.random() * 5
で0から5までのランダムな値を出すMath.random() * 5 + 5
で5から10までのランダムな値を出す- (Math.random() * 5 + 5)
で-10から-5までのランダムな値を出す
スコアの表示
経過時間をスコアとして表示するようにします
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSpeed = 0;
let playerAcc = 0;
let playerSize = 50;
let enemyImg;
let enemyX = 600;
let enemyY = gndY;
let enemySpeed = -10;
let enemySize = 50;
let initialTime;
let now;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
enemyImg = new Image();
enemyImg.src = "./shark.png";
initialTime = new Date();
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
now = new Date();
//console.log("playing!!");
drawBackground();
updatePlayer();
updateEnemy();
drawScore();
ctx.drawImage(enemyImg, enemyX - enemySize / 2, enemyY - enemySize / 2, enemySize, enemySize);
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function drawScore(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let score = now - initialTime;
let scoreText = "Score: " + score;
let scoreWidth = ctx.measureText(scoreText).width;
ctx.fillText(scoreText, canvas.width - scoreWidth - 10, 30);
}
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
}
function updateEnemy(){
enemyX = enemyX + enemySpeed;
if (enemyX < 0){
enemyX = canvas.width + 100;
enemySpeed = - (Math.random() * 5 + 5);
}
}
function keydown(e){
console.log(e.code);
if (e.code == "Space"){
playerSpeed = -20;
playerAcc = 1.5;
}
}
xxxxxxxxxx
let initialTime;
let now;
ゲームの開始時刻と現時刻を入れる変数を用意
xxxxxxxxxx
initialTime = new Date();
ゲームの開始時刻を取得
xxxxxxxxxx
now = new Date();
現時刻を取得
xxxxxxxxxx
drawScore();
xxxxxxxxxx
function drawScore(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let score = now - initialTime;
let scoreText = "Score: " + score;
let scoreWidth = ctx.measureText(scoreText).width;
ctx.fillText(scoreText, canvas.width - scoreWidth - 10, 30);
}
スコアを表示
xxxxxxxxxx
let score = now - initialTime;
ゲームの開始時刻から現時刻までの経過時間を計算し,
score
とする
xxxxxxxxxx
let scoreWidth = ctx.measureText(scoreText).width;
スコアの文字列の横幅を取得
xxxxxxxxxx
ctx.fillText(scoreText, canvas.width - scoreWidth - 10, 30);
スコアの文字列の横幅の分だけ位置を調整しつつ, 画面右上に表示
衝突判定
プレイヤーと敵がぶつかる (衝突する) ことを判定し, ぶつかってしまったらゲームオーバーとします.
衝突判定は
「プレイヤーと敵との距離」が「プレイヤーの半径と敵の半径の和」よりも小さくなってしまった場合
としてみます.
「プレイヤーと敵との距離」を求めるには, 直角三角形に対する三平方の定理を使って,
とします.
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSpeed = 0;
let playerAcc = 0;
let playerSize = 50;
let playerR = playerSize / 2;
let enemyImg;
let enemyX = 600;
let enemyY = gndY;
let enemySpeed = -10;
let enemySize = 50;
let enemyR = enemySize / 2;
let initialTime;
let now;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
enemyImg = new Image();
enemyImg.src = "./shark.png";
initialTime = new Date();
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
now = new Date();
//console.log("playing!!");
drawBackground();
updatePlayer();
updateEnemy();
checkCollision();
drawScore();
ctx.drawImage(enemyImg, enemyX - enemySize / 2, enemyY - enemySize / 2, enemySize, enemySize);
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function drawScore(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let score = now - initialTime;
let scoreText = "Score: " + score;
let scoreWidth = ctx.measureText(scoreText).width;
ctx.fillText(scoreText, canvas.width - scoreWidth - 10, 30);
}
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
}
function updateEnemy(){
enemyX = enemyX + enemySpeed;
if (enemyX < 0){
enemyX = canvas.width + 100;
enemySpeed = - (Math.random() * 5 + 5);
}
}
function checkCollision(){
let dx = playerX - enemyX;
let dy = playerY - enemyY;
let dr = Math.sqrt(dx**2 + dy**2);
if (dr < playerR + enemyR){
console.log("collision!!");
playerSpeed = 0;
enemySpeed = 0;
isPlaying = false;
}
}
function keydown(e){
console.log(e.code);
if (e.code == "Space"){
playerSpeed = -20;
playerAcc = 1.5;
}
}
xxxxxxxxxx
checkCollision();
xxxxxxxxxx
function checkCollision(){
let dx = playerX - enemyX;
let dy = playerY - enemyY;
let dr = Math.sqrt(dx**2 + dy**2);
if (dr < playerR + enemyR){
console.log("collision!!");
playerSpeed = 0;
enemySpeed = 0;
isPlaying = false;
}
}
リスタート
ゲームオーバーになったら, Rキーを押してリスタートするようにします
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSpeed = 0;
let playerAcc = 0;
let playerSize = 50;
let playerR = playerSize / 2;
let enemyImg;
let enemyX = 600;
let enemyY = gndY;
let enemySpeed = -10;
let enemySize = 50;
let enemyR = enemySize / 2;
let initialTime;
let now;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
enemyImg = new Image();
enemyImg.src = "./shark.png";
initialTime = new Date();
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
now = new Date();
//console.log("playing!!");
drawBackground();
updatePlayer();
updateEnemy();
checkCollision();
drawScore();
ctx.drawImage(enemyImg, enemyX - enemySize / 2, enemyY - enemySize / 2, enemySize, enemySize);
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function drawScore(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let score = now - initialTime;
let scoreText = "Score: " + score;
let scoreWidth = ctx.measureText(scoreText).width;
ctx.fillText(scoreText, canvas.width - scoreWidth - 10, 30);
}
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
}
function updateEnemy(){
enemyX = enemyX + enemySpeed;
if (enemyX < 0){
enemyX = canvas.width + 100;
enemySpeed = - (Math.random() * 5 + 5);
}
}
function checkCollision(){
let dx = playerX - enemyX;
let dy = playerY - enemyY;
let dr = Math.sqrt(dx**2 + dy**2);
if (dr < playerR + enemyR){
console.log("collision!!");
playerSpeed = 0;
enemySpeed = 0;
isPlaying = false;
drawGameOver();
}
}
function drawGameOver(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let restartText = "Game Over!! Press R to restart";
let restartWidth = ctx.measureText(restartText).width;
ctx.fillText(restartText, canvas.width / 2 - restartWidth / 2, canvas.height / 2);
}
function keydown(e){
console.log(e.code);
if (e.code == "Space"){
playerSpeed = -20;
playerAcc = 1.5;
}
else if (e.code == "KeyR" && isPlaying == false){
location.reload();
}
}
xxxxxxxxxx
drawGameOver();
xxxxxxxxxx
function drawGameOver(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let restartText = "Game Over!! Press R to restart";
let restartWidth = ctx.measureText(restartText).width;
ctx.fillText(restartText, canvas.width / 2 - restartWidth / 2, canvas.height / 2);
}
ゲームオーバーになったら「Rを押すと再スタートできるよ」というメッセージを表示します
xxxxxxxxxx
else if (e.code == "KeyR" && isPlaying == false){
location.reload();
}
「ゲームオーバー後 (
isPlaying=false
) 」 且つ 「キーボードでRを押した」 場合に, 今の画面を再読み込み (reload) します
プレイヤーの連続ジャンプ禁止
プレイヤーが水面に着いている場合にのみジャンプできるようにします.
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSpeed = 0;
let playerAcc = 0;
let playerSize = 50;
let playerR = playerSize / 2;
let enemyImg;
let enemyX = 600;
let enemyY = gndY;
let enemySpeed = -10;
let enemySize = 50;
let enemyR = enemySize / 2;
let initialTime;
let now;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
enemyImg = new Image();
enemyImg.src = "./shark.png";
initialTime = new Date();
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
now = new Date();
//console.log("playing!!");
drawBackground();
updatePlayer();
updateEnemy();
checkCollision();
drawScore();
ctx.drawImage(enemyImg, enemyX - enemySize / 2, enemyY - enemySize / 2, enemySize, enemySize);
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function drawScore(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let score = now - initialTime;
let scoreText = "Score: " + score;
let scoreWidth = ctx.measureText(scoreText).width;
ctx.fillText(scoreText, canvas.width - scoreWidth - 10, 30);
}
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
}
function updateEnemy(){
enemyX = enemyX + enemySpeed;
if (enemyX < 0){
enemyX = canvas.width + 100;
enemySpeed = - (Math.random() * 5 + 5);
}
}
function checkCollision(){
let dx = playerX - enemyX;
let dy = playerY - enemyY;
let dr = Math.sqrt(dx**2 + dy**2);
if (dr < playerR + enemyR){
console.log("collision!!");
playerSpeed = 0;
enemySpeed = 0;
isPlaying = false;
drawGameOver();
}
}
function drawGameOver(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let restartText = "Game Over!! Press R to restart";
let restartWidth = ctx.measureText(restartText).width;
ctx.fillText(restartText, canvas.width / 2 - restartWidth / 2, canvas.height / 2);
}
function keydown(e){
console.log(e.code);
if (e.code == "Space" && playerY == gndY){
playerSpeed = -20;
playerAcc = 1.5;
}
else if (e.code == "KeyR" && isPlaying == false){
location.reload();
}
}
xxxxxxxxxx
if (e.code == "Space" && playerY == gndY){
playerSpeed = -20;
playerAcc = 1.5;
}
ジャンプの箇所に
playerY == gndY
という条件を追加することで, プレイヤーが地面 (水面) に着いている場合のみジャンプできるようになります.
波と繰り返し (for)
水面の波を表現します
app.js
xxxxxxxxxx
let canvas;
let ctx;
let isPlaying = true;
let gndY = 300;
let playerImg;
let playerX = 100;
let playerY = gndY;
let playerSpeed = 0;
let playerAcc = 0;
let playerSize = 50;
let playerR = playerSize / 2;
let enemyImg;
let enemyX = 600;
let enemyY = gndY;
let enemySpeed = -10;
let enemySize = 50;
let enemyR = enemySize / 2;
let initialTime;
let now;
console.log("hello");
calcTest(10000);
setup();
function calcTest(x){
console.log(123 + x);
}
function setup(){
console.log("start!!");
canvas = document.querySelector("#gameCanvas");
ctx = canvas.getContext("2d");
playerImg = new Image();
playerImg.src = "./fish.png";
enemyImg = new Image();
enemyImg.src = "./shark.png";
initialTime = new Date();
document.onkeydown = keydown;
setInterval("loop()", 16);
}
function loop(){
if (isPlaying){
now = new Date();
//console.log("playing!!");
drawBackground();
updatePlayer();
updateEnemy();
checkCollision();
drawScore();
ctx.drawImage(enemyImg, enemyX - enemySize / 2, enemyY - enemySize / 2, enemySize, enemySize);
ctx.drawImage(playerImg, playerX - playerSize / 2, playerY - playerSize / 2, playerSize, playerSize);
}
else {
//console.log("game over!!");
}
}
function drawBackground(){
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = "blue";
ctx.beginPath();
ctx.moveTo(0, gndY);
let dt = now - initialTime;
for (let i = 0; i < canvas.width; i++){
ctx.lineTo(i, gndY + 5*Math.sin(i/10 + dt/100));
}
ctx.lineTo(canvas.width, gndY);
ctx.stroke();
}
function drawScore(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let score = now - initialTime;
let scoreText = "Score: " + score;
let scoreWidth = ctx.measureText(scoreText).width;
ctx.fillText(scoreText, canvas.width - scoreWidth - 10, 30);
}
function updatePlayer(){
playerSpeed = playerSpeed + playerAcc;
playerY = playerY + playerSpeed;
if (playerY > gndY){
playerY = gndY;
playerSpeed = 0;
playerAcc = 0;
}
}
function updateEnemy(){
enemyX = enemyX + enemySpeed;
if (enemyX < 0){
enemyX = canvas.width + 100;
enemySpeed = - (Math.random() * 5 + 5);
}
}
function checkCollision(){
let dx = playerX - enemyX;
let dy = playerY - enemyY;
let dr = Math.sqrt(dx**2 + dy**2);
if (dr < playerR + enemyR){
console.log("collision!!");
playerSpeed = 0;
enemySpeed = 0;
isPlaying = false;
drawGameOver();
}
}
function drawGameOver(){
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "20px sans-serif";
let restartText = "Game Over!! Press R to restart";
let restartWidth = ctx.measureText(restartText).width;
ctx.fillText(restartText, canvas.width / 2 - restartWidth / 2, canvas.height / 2);
}
function keydown(e){
console.log(e.code);
if (e.code == "Space" && playerY == gndY){
playerSpeed = -20;
playerAcc = 1.5;
}
else if (e.code == "KeyR" && isPlaying == false){
location.reload();
}
}
xxxxxxxxxx
let dt = now - initialTime;
for (let i = 0; i < canvas.width; i++){
ctx.lineTo(i, gndY + 5*Math.sin(i/10 + dt/100));
}
xxxxxxxxxx
for (let 繰返しの変数名 = 繰返しの初期値; 繰返しの変数名 < 繰返しの最終値; 繰返しの増減幅){
繰返しの実際の処理
}
という文法で, 繰返しの処理ができます.
xxxxxxxxxx
for (let i = 0; i < canvas.width; i++){
繰返しの実際の処理
}
繰返しの変数名は
i
i
は0以上600未満で, 1ずつ増える (i++
) ように繰返していく
xxxxxxxxxx
ctx.lineTo(i, gndY + 5*Math.sin(i/10 + dt/100));
Math.sin()
は三角関数の一種のsin関数のこと