こんにちは、武田です。
JavaScriptというプログラミング言語はわたしの大好きな言語です。
enchant.jsなどのゲーム制作に特化したライブラリ(楽にプログラミングするために使いやすくした関数群)やjQuery.jsなどのサイト作成に威力を発揮するライブラリなど色々とありますが、まずはJavaScript単体でどこまで出来るか勉強してみるのも面白いものです。
今回は、タッチするだけのシンプルゲームを作りました。
タイトル>ゲーム>結果表示>タイトル
などの基本的な状態遷移もしているので、参考にしてみてください。
ソースコードを載せておきます。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=320,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>JavaScript de Simple Game!</title>
<script src="Punyo.js"></script>
<script src="main.js"></script>
<style>
/* mystyle.css */
*{
margin: 0;
padding: 0;
}
body{
background-color: #fff;
}
h1{
font-family: 'メイリオ';
font-size: 12px;
}
#canvas{
border:solid 1px #000;
background-color: #ddd;
}
</style>
</head>
<body>
<canvas id="canvas" width="320" height="480"></canvas>
</body>
</html>
main.js(JavaScriptメイン)
// -------------------------------------------------------------------------
// main.js JavaScriptでシンプルゲーム
//
// created at 2014-10-09 on take3.asia
// -------------------------------------------------------------------------
/*
* グローバル変数
*/
var canvas = null; // キャンバス
var g = null; // コンテキスト
var $id = function(id){ return document.getElementById(id); };
var mode;
var objects = []; // 敵オブジェクト配列用
var mouse = {x: null, y: null}; // マウス座標取得用
var score;
var startTime; // 結果時間とタイトルまでの待ち時間に使用
var result; // 結果時間
var agent; // ユーザ端末
/*
* 定数
*/
// 画面サイズ
var SCREEN_WIDTH = 320;
var SCREEN_HEIGHT = 480;
// 描画間隔
var TICK = 1000/30;
var WAIT_TIME = 3;
// ゲームモード
var TITLE = 0;
var GAME = 1;
var WAIT = 2;
// 敵の数
var PUNYO = 7;
/*
* 初期設定
*/
function init(){
// ゲームの初期モード
score = 0;
// オブジェクトの初期状態
for(var i in objects){
objects[i].init();
}
// 開始時刻設定
startTime = new Date().getTime();
}
/*
* 結果表示
*/
function showResult(){
g.fillStyle = "#000";
g.font = "bold 36px メイリオ";
g.fillText(result + "sec.", 85, 245);
}
/*
* タイトル画面
*/
function title(){
g.fillStyle = "#555";
g.font = "bold 24px メイリオ";
g.fillText("A Simple Game!", 63, 180);
g.font = "bold 18px メイリオ";
g.fillText("Touch to Start.", 91, 330);
}
/*
* ゲーム画面
*/
function game(){
// オブジェクトの更新処理
for(var i in objects){
objects[i].update();
}
// 残りのぷにょ数表示
g.fillStyle = "#555";
g.font = "bold 72px メイリオ";
g.fillText(PUNYO-score, 50, 100);
// 終了判定
if(score >= PUNYO){
mode = WAIT;
result = (new Date().getTime()) - startTime;
result /= 1000; // 秒に直す
result = Math.floor(result * 100) / 100; // 小数点以下2桁表示
startTime = new Date().getTime(); // タイトル表示までの待ち時間開始
}
}
/*
* 終了待ち画面
*/
function wait(){
var nowTime = new Date().getTime();
var checkTime = (nowTime - startTime) / 1000;
// タイトル画面へ
if(checkTime > WAIT_TIME){
mode = TITLE;
}
// 結果表示
showResult();
}
/*
* メイン処理
*/
var mainLoop = function(){
// 画面クリア
g.clearRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
// 描画処理(モードにより切り替え)
if(mode == GAME){
game();
}
else if(mode == TITLE){
title();
}
else if(mode == WAIT){
wait();
}
// ループして描画更新
setTimeout(mainLoop, TICK);
};
/*
* イベント処理
*/
function onTouchStartListener(e){
// 画面切り替え
if(mode == TITLE){
init();
mode = GAME;
}
else if(mode == GAME){
var rect = e.target.getBoundingClientRect();
// マウス座標取得
if(agent == "PC"){
mouse.x = e.clientX - rect.left;
mouse.y = e.clientY - rect.top;
}
else if(agent == "TABLET"){
e.preventDefault();
var touch = e.touches[0];
mouse.x = touch.pageX - rect.left;
mouse.y = touch.pageY - rect.top;
}
// 当たり判定
for(var i in objects){
if(objects[i].hitCheck()){
objects[i].die();
score++;
}
}
}
}
/*
* 起動処理
*/
window.onload = function(){
// アドレスバー非表示
setTimeout(function(){
window.scrollTo(0,1);
}, 1);
// ユーザエージェント判定
var user = navigator.userAgent;
console.log("AGENT = " + user);
if((user.indexOf("iPhone") > 0) || (user.indexOf("iPad")) > 0 || (user.indexOf("iPod")) > 0 || (user.indexOf("Android")) > 0){
agent = "TABLET";
window.ontouchstart = onTouchStartListener;
}
else{
agent = "PC";
window.onmousedown = onTouchStartListener;
}
// キャンバス情報取得
canvas = $id("canvas");
g = canvas.getContext("2d");
// オブジェクト生成
for(var i=0; i<PUNYO; i++){
var punyo = new Punyo();
objects.push(punyo);
}
// メインループ実行
mode = TITLE;
mainLoop();
};
Punyo.js(敵の動きのみクラス化しました)
/**
* ぷにょという敵クラス
*/
function Punyo(){
var x; // x座標
var y; // y座標
var vx; // x方向の移動量
var vy; // y方向の移動量
var speed; // 移動速度
var r; // 半径
var angle; // 移動方向
var timer; // 一回の動作時間
// 移動方向を設定
this.setAngle = function(){
angle = Math.floor(Math.random() * 360);
vx = Math.cos(angle * Math.PI/180) * speed;
vy = Math.sin(angle * Math.PI/180) * speed;
};
// 初期化
this.init = function(){
// 移動速度
speed = 5;
// 円の半径を設定
r = 10 * (Math.floor(Math.random() *3) + 1);
// 初期位置
x = Math.floor(Math.random() * (SCREEN_WIDTH-r*2)) + r;
y = Math.floor(Math.random() * (SCREEN_HEIGHT-r*2)) + r;
// 動作時間
timer = Math.floor(Math.random() * 30);
this.setAngle();
};
// 移動処理
this.move = function(){
this.draw();
x+=vx;
y+=vy;
if((x-r) < 0 || (x+r) > SCREEN_WIDTH) vx = -vx;
if((y-r) < 0 || (y+r) > SCREEN_HEIGHT) vy = -vy;
if(timer > 30){
timer = 0;
this.update = this.wait;
}
timer++;
};
// ときどき待つ
this.wait = function(){
this.draw();
if(timer > 20){
timer = 0;
this.setAngle();
this.update = this.move;
}
timer++;
};
// 描画メソッド
this.draw = function(){
// 影をつける
g.shadowBlur = 5;
g.shadowColor = "rgba(50, 50, 50, 1)";
g.shadowOffsetX = 3;
g.shadowOffsetY = 3;
// 円を描画
g.fillStyle = "gray";
g.beginPath();
g.arc(x, y, r, 0, Math.PI*2, false);
g.fill();
// 影をリセット
g.shadowBlur = 0;
g.shadowOffsetX = 0;
g.shadowOffsetY = 0;
};
// 当たり判定
this.hitCheck = function(){
var distance = Math.sqrt((mouse.x-x)*(mouse.x-x) + (mouse.y-y)*(mouse.y-y));
if(distance < r){
return true;
}
return false;
};
// 破棄
this.die = function(){
x = y = -10000;
};
// 更新処理
this.update = function(){
};
// 生成時の処理
this.setAngle();
this.update = this.move;
}
