最近サーバーサイドのプログラミングを勉強してみようと思い、PHPの勉強を始めました。サーバーを借りてこのサイトを立ち上げたりしてなんとなくWebの仕組みがわかってきたので、前々から興味があったWebアプリの制作をしてみることにしました。
Webアプリについていろいろ調べていると”初心者でも2時間でブラウザゲームを作ってWebにアップしよう! ”という記事を見つけたのでとりあえずやってみることにしました。
自分はJavascriptに触れたことがない全くの初心者ですが、5時間ほどでWebゲームを作ることができました。
目次
今回作ったゲーム
ゲームの内容はクリック(またはタップ)すると上からブロックが落ちてくるので、タイミングよくクリックしてブロックを積み上げていき、15個積み上げることができたらクリアというクソゲーです(笑)
Webゲームなのでパソコンでもスマホでもでき、上のリンクをクリックするとすぐに始まります。興味がある方はやってみてください。
必要な知識
- 基礎的なプログラミングの知識(if文, for文, 関数, 変数など)
- HTML、CSS(基本コピペでできますが、レイアウトなどにこだわりたいなら)
必要なもの
- enchant.js(今回はenchant.jsを圧縮したものであるenchant.min.jsを使用)
enchant.jsは、JavaScriptのゲームエンジンライブラリです。
こちら(https://github.com/wise9/enchant.js/)からダウンロードできます。
構成
– enchant.min.js
– Script.js
– index.html
– game.html
– image(画像保存用フォルダー)
上記のゲームは4つのスクリプトと画像データでできています。
.jsはJavaScriptファイルです。ゲームの制御、画面表示の切り替えなどを行います。
.htmlはWEBページの大枠のレイアウトを決めるものです。(htmlファイルの中に、CSSと呼ばれるさらに細かなレイアウトを調整できる言語を記載できます。)
enchant.js(enchant.min.js)
enchant.js(enchant.min.js)にはゲームを簡単に作るための様々な関数が記載されており、その関数を後述するScript.jsから呼び出して使用することで、複雑な処理も最低限の記述量で記述できます。
例えば、
namae.onenterframe = function () {
処理内容
}
と書くだけで、毎フレームごとの処理を書くことができたり、
namae.ontouchend = function () {
処理内容
}
と書くだけで、タップ(クリック)したときの動作を記述できたりするので非常に簡単です。
Script.js
ゲームの制御やクリア後の画面表示の切り替えなどを制御するスクリプトです。
今回作成したScript.jsは以下のようになっています。
enchant();
window.onload = function () {
var game = new Game(400, 500); //画面サイズを400*500にする。(このサイズだとスマホでも快適なのでおススメ)
game.fps = 60;
//結果ツイート時にURLを貼るため、このゲームのURLをここに記入
var url = "https://jorugame.jorublog.site/tower/index.html";
url = encodeURI(url); //きちんとURLがツイート画面に反映されるようにエンコードする
/////////////////////////////////////////////////
//ゲーム開始前に必要な画像・音を読み込む部分
for (var i = 1; i <= 6; i++) {
eval('var B_Image' + i + '="image/image' + i + '.png"'); //eval関数を使ってimage/image〇のURLを取得して、変数B_Imageに格納
eval('game.preload([B_Image' + i + ']);'); //ついでにプリロードまで済ませてしまう
}
for (var i = 1; i <= 7; i++) {
eval('var C_Image' + i + '="image/block' + i + '.png"'); //eval関数を使ってimage/image〇のURLを取得して、変数B_Imageに格納
eval('game.preload([C_Image' + i + ']);'); //ついでにプリロードまで済ませてしまう
}
//読み込み終わり
/////////////////////////////////////////////////
game.onload = function () { //ロードが終わった後にこの関数が呼び出されるので、この関数内にゲームのプログラムを書こう
/////////////////////////////////////////////////
//グローバル変数
var Time = 0;
var Second
var Minits = 0;
var Score = 0; //スコア
var BlockAry = []; //モンスタースプライトを管理する配列
var now_block;
var shoot_frag = 1;
var S_FirstPos_now;
var Stage = 1;
//グローバル変数終わり
/////////////////////////////////////////////////
var S_MAIN = new Scene(); //シーン作成
game.pushScene(S_MAIN); //S_MAINシーンオブジェクトを画面に設置
S_MAIN.backgroundColor = "grey"; //S_MAINシーンの背景は黒くした
//テキスト
var S_Text = new Label(); //テキストはLabelクラス
S_Text.font = "20px Meiryo"; //フォントはメイリオ 20px 変えたかったらググってくれ
S_Text.color = 'rgba(255,255,255,1)'; //色 RGB+透明度 今回は白
S_Text.width = 400; //横幅指定 今回画面サイズ400pxなので、width:400pxだと折り返して二行目表示してくれる
S_Text.moveTo(0, 30); //移動位置指定
S_MAIN.addChild(S_Text); //S_MAINシーンにこの画像を埋め込む
S_Text.text = "現在:" + Score; //テキストに文字表示 Scoreは変数なので、ここの数字が増える
var S_table = new Sprite(25, 25); //スライムを配置
S_table.image = game.assets[C_Image4]; //スライム画像
S_table.moveTo(190, 475)
S_MAIN.addChild(S_table);
function init() { //初期化用関数
Time = 0;
Second = 0;
Minits = 0;
Score = 0;
Stage = 1
shoot_frag = 1;
now_block = S_table;
//画面のスライムをすべて削除する
for (var i = 0; i < BlockAry.length; i++) {
if (BlockAry[i] != null) BlockAry[i].parentNode.removeChild(BlockAry[i]); //画面にスライムが残っていれば削除する
}
BlockAry = [];
}
init();
///////////////////////////////////////////////////
//メインループ ここに主要な処理をまとめて書こう
S_MAIN.onenterframe = function () {
if (Second < 10 && Minits < 10) {
S_Text.text = "現在 Score: " + Score + " " + "Time: " + "0" + Minits + ":" + "0" + Second + ":" +Time;
}else if (Second < 10) {
S_Text.text = "現在 Score: " + Score + " " + "Time: " + Minits + ":" + "0" + Second + ":" +Time;
}else if (Minits < 10) {
S_Text.text = "現在 Score: " + Score + " " + "Time: " + "0" + Minits + ":" + Second + ":" +Time;
}else{
S_Text.text = "現在 Score: " + Score + " " + "Time: " + Minits + ":" + Second + ":" +Time;
}
Time++; //毎フレームカウントを1増やす
if (Time == 59) {
Time = 0;
Second++;
}
if (Second == 59) {
Second = 0;
Minits++;
}
if (Score <= 2) {
Stage = 1;
} else if (Score <= 4) {
Stage = 2;
} else if (Score <= 6) {
Stage = 3;
} else if (Score <= 8) {
Stage = 4;
} else if (Score <= 10) {
Stage = 5;
} else if (Score <= 12) {
Stage = 6;
} else if (Score <= 14) {
Stage = 7;
} else {
Stage = 8;
}
if (shoot_frag == 1 && S_FirstPos_now == null) {
var S_FirstPos = new Sprite(25, 25); //スライムを配置
S_FirstPos.image = game.assets[C_Image1]; //スライム画像
S_FirstPos.x = S_Hero.x,
S_FirstPos.y = S_Hero.y+30
S_MAIN.addChild(S_FirstPos); //S_MAINシーンに追加
S_FirstPos_now = S_FirstPos;
S_FirstPos_now.onenterframe = function () { //モンスターの動作
this.x = S_Hero.x; //下に降りる
};
};
};
///////////////////////////////////////////////////
//クリックで球を発射
S_MAIN.ontouchend = function () {
if (shoot_frag == 1) {
shoot_frag = 0;
S_FirstPos_now.parentNode.removeChild(S_FirstPos_now);
S_FirstPos_now = null;
var S_Shoot = new Sprite(25, 25); //爆弾画像のspriteを宣言(数字は縦横サイズ)
S_Shoot.image = game.assets[C_Image1]; //爆弾画像
S_Shoot.moveTo(S_Hero.x, S_Hero.y+30); //自機の位置に持ってくる
S_MAIN.addChild(S_Shoot);
S_Shoot.onenterframe = function () { //毎フレーム毎に実行
if ( (this.y + 25) >= now_block.y ) {
if ( (this.x + 25 > now_block.x) && (this.x < now_block.x + 25) ) {
Score++;
shoot_frag = 1;
now_block = this;
this.parentNode.removeChild(this);
var S_block = new Sprite(25, 25); //スライムを配置
S_block.image = game.assets[C_Image4]; //スライム画像
S_block.x = this.x,
S_block.y = this.y;
S_MAIN.addChild(S_block);
S_block.number = BlockAry.length; //自分がBlockAryのどこにいるか覚えておく(削除するときに使う)
BlockAry.push(S_block); //BlockAry(モンスター管理用配列)に格納
if (Score>=15) {
//this.parentNode.removeChild(this);
game.popScene(); //S_MAINシーンを外して
game.pushScene(S_END);
}
return;
}else{
this.y += 5;
}
}else{
this.y += 5;
}
if (this.y > 500) {
this.parentNode.removeChild(this);
game.popScene(); //S_MAINシーンを外して
game.pushScene(S_END);
}
};
};
};
//自機
var S_Hero = new Sprite(32, 32); //自機のサイズのspriteを宣言(数字は縦横サイズ)
S_Hero.image = game.assets[B_Image3]; //自機画像
S_Hero.moveTo(180, 60); //自機の位置
S_MAIN.addChild(S_Hero); //S_MAINシーンに貼る
S_Hero.time = 0; //Sin波で自機を左右に移動させるので、カウントが必要
S_Hero.onenterframe = function () {
this.time++; //カウントを1増やす
this.x = Math.sin(this.time / 60*(1+0.5*(Stage))) * 180 + 180; //Sin波で自機を左右に移動させる
};
////////////////////////////////////////////////////////////////
//結果画面
S_END = new Scene();
S_END.backgroundColor = "blue";
S_END.onenterframe = function () {
if (Score < 15) {
S_GameOverText.text = Score + "個積み上げた!";
}else{
S_GameOverText.text = Minits + ":" + Second + ":" + Time + " でクリアした!";
}
};
//GAMEOVER
var S_GameOverText = new Label(); //テキストはLabelクラス
S_GameOverText.font = "20px Meiryo"; //フォントはメイリオ 20px 変えたかったらググってくれ
S_GameOverText.color = 'rgba(255,255,255,1)'; //色 RGB+透明度 今回は白
S_GameOverText.width = 400; //横幅指定 今回画面サイズ400pxなので、width:400pxだと折り返して二行目表示してくれる
S_GameOverText.moveTo(0, 30); //移動位置指定
S_END.addChild(S_GameOverText); //S_ENDシーンにこの画像を埋め込む
//リトライボタン
var S_Retry = new Sprite(120, 60); //画像サイズをここに書く。使う予定の画像サイズはプロパティで見ておくこと
S_Retry.moveTo(50, 300); //コインボタンの位置
S_Retry.image = game.assets[B_Image2]; //読み込む画像の相対パスを指定。 事前にgame.preloadしてないと呼び出せない
S_END.addChild(S_Retry); //S_MAINにこのコイン画像を貼り付ける
S_Retry.ontouchend = function () { //S_Retryボタンをタッチした(タッチして離した)時にこの中の内容を実行する
init(); //スコアなど、変化する変数を初期化する関数
game.popScene(); //S_ENDシーンを外す
game.pushScene(S_MAIN); //S_MAINシーンを入れる
};
//ツイートボタン
var S_Tweet = new Sprite(120, 60); //画像サイズをここに書く。使う予定の画像サイズはプロパティで見ておくこと
S_Tweet.moveTo(230, 300); //コインボタンの位置
S_Tweet.image = game.assets[B_Image1]; //読み込む画像の相対パスを指定。 事前にgame.preloadしてないと呼び出せない
S_END.addChild(S_Tweet); //S_MAINにこのコイン画像を貼り付ける
S_Tweet.ontouchend = function () { //S_Tweetボタンをタッチした(タッチして離した)時にこの中の内容を実行する
//ツイートAPIに送信
if (Score < 15) {
window.open("http://twitter.com/intent/tweet?text=頑張って" + Score + "個積み上げた!&hashtags=ahoge&url=" + url);
}else{
window.open("http://twitter.com/intent/tweet?text=頑張って" + Minits + ":" + Second + ":" + Time + "でクリアした&hashtags=jorugame&url=" + url);
}
//ハッシュタグにahogeタグ付くようにした。
};
};
game.start();
};
index.html
index.htmlではWEBページのメイン画面を作成しています。
今回作成したindex.htmlは以下のようになっています。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=500px,user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>jorugame_tower</title>
<style type="text/css">
a:link {
text-decoration: none;
color: #E6E6FA;
}
body {
background-color: #222222;
color: #FFFFFF;
}
</style>
</head>
<body>
<center>
<div style="border:1px solid gray; width:420px;">
<iframe src="game.html" width=" 400" height="500" scrolling="no" marginwidth="0" marginheight="0" frameborder="0" style="border:none;"></iframe>
<h2>遊び方</h2>
<p>タップ(クリック)してブロックを落とし、積み上げていくゲーム<br>
15個積み上げることができたらクリアです。
</p>
<h2>お借りした素材</h2>
<a href="http://hothukurou.firebird.jp/blog/post-713">作っちゃうおじさん制作記録</a>
<br>
<a href="https://note.mu/ryuunosuke0721/n/n45188a6b4597">テトリス風ブロック素材</a>
<br>
</div>
</center>
</body>
</html>
game.html
game.htmlではゲーム画面の表示や作成したScript.jsの読み込みを行っています。game.html自体はindex.htmlからiframeというhtmlタグを使って呼び出しています。
今回作成したgame.htmlは以下のようになっています。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<head>
<meta charset="utf-8">
<meta name="robots" content="noindex" />
<script src="enchant.min.js"></script>
<script src="Script.js"></script>
<script>
if (window == top.window) location.href = "index.html";//万が一直接ここを読み込まれたとき、index.htmlに戻す
</script>
</head>
<body>
</body>
</html>
最後に
今回Webゲームを作ってみて、なんかしらのプログラミング経験がある方なら、少し知識を学ぶだけですぐに書けるので意外と簡単だなと感じました。
また、インタプリタ言語なので面倒くさいコンパイラーのインストールなどをする必要がないので手軽に始めることができるというのもいいですね。
今まで興味があったけど”難しそう”という勝手な思い込みで距離をおいていたゲーム開発を意外と簡単に作ることができたので、次はオンライン対戦ができるボードゲームのwebゲームに挑戦してみたいと思います。
HTML,CSS,JavaScriptの知識がなくても数時間でゲームを作ることができるので、興味がある方はぜひ挑戦してみてください(^^)
分からないことや疑問点があれば、以下からコメントを頂ければ回答いたします。お気軽にご質問ください!