HTML5
js
canvas
ゲーム制作

ゲームプログラミング入門~その4~【シューティングゲーム】完成


「シューティングゲーム」がやっと完成しました!!

2度目のプログラミングチャレンジで、1か月ほどかけて、やっと「シューティングゲーム」が完成しました。

test.jpg

今回の学習では、エンジニアの友人にいろいろと教わりながら勉強&作成できたので、途中で挫折しかけたのですが、何とか完成までたどり着くことができました。

URL

今回作成したシューティングゲームのコードを下に書いておきたいと思います。

<!DOCTYPE html>

<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="robots" content="noindex,nofollow">
<title>WEB SCREEN GAME</title>
<style>
#screen {
border: solid 1px #000000;
background-color: #000000;
}

</style>
</head>
<body>
<canvas id="screen" width="860" height="485"></canvas>
<h2>操作方法</h2>
<p>自機の操作 → 矢印キー</p>
<p>ミサイルの発射 → Sキー</p>
<p>※ゲームの時間制限はありません。自機と敵が衝突したらゲームオーバーになります。</p>
<p>再度プレイする場合は、ブラウザの表示を更新してください。
<script>
//canvas要素のDOM(Document Object Model)を取得
var canvas = document.getElementById('screen');
var context;

var bgImg1; // 背景画像1
var bgImg2; // 背景画像2
var bgImgs; // 背景画像配列

var score = 0; // スコア
var scoreImg; // スコア画像
var selfBody; // 自機データ
var selfPosX = 20; // 自機の画面横方向の位置
var selfPosY = 190; // 自機の画面縦方向の位置
var selfCenterPos = []; // 自機中心座標配列
var selfSpeed = 10; // 自機のスピード
var enemyBody; //敵データ

var gameOverImg; // ゲームオーバー画像

var playFlag = true; // ゲームプレイフラグ

var frameWidth = 860; // CANVAS横サイズ
var frameHeight = 485; // CANVAS縦サイズ

// キーボードの状態
// [ 上 , 下 , 右 , 左 ]
var keyStatus = [false, false, false, false]

// ミサイル
var missiles = []; // ミサイルデータ配列
var missileSpeed = 30; // ミサイルスピード
var maxMissileCount = 5; // ミサイル最大同時発射数

// ミサイルデータの初期化
for ( var i = 0; i < maxMissileCount; i++ ){
// 状態 , x, y
missiles[i] = [false, 0, 0];
}

var enemies = []; // 敵データ配列
var enemyCenterPos = []; // 敵データ中心座標配列
var maxEnemyCount = 1; // 最大表示敵数

var enemyType = []; // 敵タイプデータ配列
// X有効範囲, Y有効範囲, 画像file名, 獲得スコア
enemyType[0] = [ 20, 20, "enemy_20x20.png", 1000];
enemyType[1] = [ 50, 50, "enemy_50x50.png", 500];
enemyType[2] = [ 80, 80, "enemy_80x80.png", 200];

// 敵データの初期化
for ( var i = 0; i < maxEnemyCount ; i++ ){
enemies[i] = createEnemy();
}

document.onkeydown = keyDown; //キーダウンの検知設定
document.onkeyup = keyUp; //キーアップの検知設定

//コンテキストの取得可否チェック
if(canvas.getContext){

context = canvas.getContext('2d');

window.onload = function() {
// 画像オブジェクト作成
selfBody = new Image();
enemyBody = new Image();
gameOverImg = new Image();
scoreImg = new Image();
bgImg1 = new Image();
bgImg2 = new Image();

// 画像ファイル名設定
selfBody.src = "rocket.png";
gameOverImg.src = "gameover.png";
scoreImg.src = "score.png";
bgImg1.src = "space1.jpg";
bgImg2.src = "space2.jpg";

// 背景画像データ設定
bgImgs = [ [bgImg1, 0], [bgImg2, 860], [bgImg1, 1720] ] ;

selfBody.onload = function(){
draw(context); //描画処理
startAnimation(); // 描画更新開始処理
}
}
}

// 描画更新処理
function startAnimation(){
setInterval(draw, 33);
}

// 描画処理
function draw(){
if ( playFlag ){
// CANVASをクリア
context.fillStyle = "rgb(255, 255, 255)";
context.fillRect(0, 0, frameWidth, frameHeight);
context.clearRect(0, 0, frameWidth, frameHeight);

drawSpace(); // 背景(宇宙)を描画

drawMissile(); // ミサイルを描画

move(); // ロケットの移動と描画
context.drawImage(selfBody, selfPosX, selfPosY);

drawEnemy(); // 敵の描画

judgementHitMissile(); // 当たり判定

drawScore(score); // スコアを描画

// ゲームオーバー判定
if( playFlag === false ){
// ゲームオーバー画像表示
context.drawImage(gameOverImg, (frameWidth/2)-(gameOverImg.width/2), (frameHeight/2)-(gameOverImg.height/2));
}
}
}

// ロケットの移動
function move(){
if ( keyStatus[0] ){ // UP
if ( selfPosY > 0 ){ selfPosY -= selfSpeed; }
}

if ( keyStatus[1] ){ // DOWN
if ( selfPosY < 400 ){ selfPosY += selfSpeed; }
}

if ( keyStatus[2] ){ // RIGHT
if ( selfPosX < 775 ){ selfPosX += selfSpeed; }
}

if ( keyStatus[3] ){ // LEFT
if ( selfPosX > 0 ){ selfPosX -= selfSpeed; }
}
}

// キーダウン
function keyDown(e){
if ( playFlag ){
if(e.keyCode === 38 ){ keyStatus[0] = true; } //UP KEY
else if(e.keyCode === 40 ){ keyStatus[1] = true; } //DOWN KEY
else if(e.keyCode === 39 ){ keyStatus[2] = true; } //RIGHT KEY
else if(e.keyCode === 37 ){ keyStatus[3] = true; } //LEFT KEY
else if(e.keyCode === 83 ){ missileLaunch(); } // ミサイルを発射
}
}

// キーアップ
function keyUp(e){
if(e.keyCode === 38 ){ keyStatus[0] = false;} //UP KEY
else if(e.keyCode === 40 ){ keyStatus[1] = false;} //DOWN KEY
else if(e.keyCode === 39 ){ keyStatus[2] = false;} //RIGHT KEY
else if(e.keyCode === 37 ){ keyStatus[3] = false;} //LEFT KEY
}

// 敵の描画
function drawEnemy(){
for( var i = 0; i < enemies.length; i++ ){
if( enemies[i][0] < -100 || enemies[i][1] < -100){
enemies[i] = createEnemy();
}
enemies[i][0] -= enemies[i][2];
enemies[i][1] += enemies[i][3];
enemyBody.src = enemies[i][4][2];
context.drawImage(enemyBody, enemies[i][0], enemies[i][1]);
}
}

// 敵データを初期化
function createEnemy(){
return [
getRandVal(910, 1000), // posX
getRandVal(10, 600), // posY
getRandVal(3, 15), // moveXspeed
-1 * getRandVal(1, 5), // moveYspeed
enemyType[getRandVal(0, 2)] // enemyTypeNum
];
}

// ミサイルを描画
function drawMissile(){
for ( var i = 0; i < missiles.length; i++ ){
if ( missiles[i][0] ){ // ミサイルが有効かどうか?
if( missiles[i][1] < 830 ){
missiles[i][1] += missileSpeed;

context.beginPath();

var missile = new Path2D();

missile.rect( missiles[i][1], missiles[i][2], 20, 2 );
context.fillStyle = "rgb(255, 255, 255)";
context.stroke(missile);
context.fill(missile);
} else {
missiles[i][0] = false; // ミサイルを無効化
}
}
}
}

// ミサイルを発射
function missileLaunch(){
for ( var i = 0; i < missiles.length; i++ ){
if( missiles[i][0] === false ){ // ミサイルが有効かどうか?
missiles[i][0] = true; // ミサイルを有効化
missiles[i][1] = selfPosX + 60;
missiles[i][2] = selfPosY + 40;

return;
}
}
}

// 乱数取得
function getRandVal(min, max){
return (Math.floor(Math.random() * (max - min + 1))) + min;
}

// 当たり判定(ミサイルと敵)
function judgementHitMissile(){
for ( var i = 0; i < enemies.length; i++ ){
// ミサイルと敵の当たり判定
for( var j = 0; j < missiles.length; j++ ){
if ( missiles[j][0] === true ){
// ミサイルのposXからエネミーのposXの差の絶対値が当たり判定値以内かどうか?
if( (enemies[i][0] < (missiles[j][1] + 20 )) && (missiles[j][1] < (enemies[i][0] + enemies[i][4][0]))){
// ミサイルのposYからエネミーのposYの差の絶対値が当たり判定値以内かどうか?
if( (missiles[j][2] > enemies[i][1]) && (missiles[j][2] < (enemies[i][1] + enemies[i][4][1]))){
// 当たり判定
missiles[j][0] = false;
score += enemies[i][4][3];
enemies[i] = createEnemy();
}
}
}

// 自機と敵の当たり判定
// 自機と敵の中心座標を計算
selfCenterPos[0] = selfPosX + 40;
selfCenterPos[1] = selfPosY + 40;
enemyCenterPos[0] = enemies[i][0] + (enemies[i][4][0] / 2); // center X
enemyCenterPos[1] = enemies[i][1] + (enemies[i][4][1] / 2); // center Y

if ( enemies[i][0] > 0 && enemies[i][1] > 0 ){
if(abs(abs(selfCenterPos[0]) - abs(enemyCenterPos[0])) < 100){
// ミサイルのposYからエネミーのposYの差の絶対値が当たり判定値以内かどうか?
if(abs(abs(selfCenterPos[1]) - abs(enemyCenterPos[1])) < 100){
// 当たり判定
// 厳密な当たり判定
var hitRange = ((enemies[i][4][0] / 2) + 40) * 0.7;
if(strictHitJudge(selfCenterPos, enemyCenterPos , hitRange)){
playFlag = false;
}
}
}
}
}
}
}

// スコアを表示
function drawScore(score){
// スコアラベルを描画
context.drawImage(scoreImg, 0, 0);
context.font = "30px Arial"; //フォントにArial,40pxを指定
context.fillStyle = "white";
context.fillText((("00000000"+score).slice(-8)),137,43); //テキストを塗り潰しで描画
}

// スコアを表示
function drawSpace(){
// スコアラベルを描画
for ( var i = 0; i < bgImgs.length; i++ ){
if( bgImgs[i][1] === -860){
if(bgImgs[i][0] === bgImg1 ){
bgImgs[i][0] = bgImg2;
} else if(bgImgs[i][0] === bgImg2 ){
bgImgs[i][0] = bgImg1;
}
bgImgs[i][1] = 1720;
}
context.drawImage(bgImgs[i][0], bgImgs[i][1], 0);
bgImgs[i][1]--;
}
}

// 絶対値を取得
function abs(value){
return Math.abs(value);
}

// 自機と敵の厳密当たり判定
function strictHitJudge(selfCenterPos, enemyCenterPos , hitRange){
var x = abs(abs(selfCenterPos[0]) - abs(enemyCenterPos[0]));
var y = abs(abs(selfCenterPos[1]) - abs(enemyCenterPos[1]));
var distance = Math.sqrt(Math.pow(x,2) + Math.pow(y,2));
if( hitRange > distance){ return true; } else { return false; };
}
</script>
<div id="link"></div>
</body>
</html>


遊んでみたい方はこちら

→シューティングゲーム