マイペースなプログラミング日記

x86エミュレータやFPGA、WebGLにお熱なd-kamiがマイペースに書くブログ

簡単なシューティングゲーム作成 その1

HTML5Canvas使って簡単なシューティングゲームを作ろうと思っている。JavaScript初心者だがなんとかする。それで現在はプレイヤー機を操作できるところまでいった。まずはHTML、onloadでgameを呼び出し初期化と16msec毎に描画を行うようにしている。keydownとkeyupで移動方向の指定と解除、攻撃の設定と解除を行っている。HTMLのあるフォルダにimageフォルダを作り以下の画像を保存しておく

player.png

enemy1.png

  • 操作方法
    • W 上へ移動
    • A 後ろへ移動
    • S 下へ移動
    • D 前へ移動
    • Space ショット

となっている

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <script type="text/javascript" src="./util/Point.js"></script>
    <script type="text/javascript" src="./util/Rect.js"></script>
    <script type="text/javascript" src="./object/Player.js"></script>
    <script type="text/javascript" src="./object/PlayerBullet1.js"></script>
    <script type="text/javascript" src="./info/GameInfo.js"></script>
    <script type="text/javascript" src="./view/GameView.js"></script>
    <script type="text/javascript">
var IMAGE_NAMES = ["Player", "Enemy"];
var IMAGE_FILES = ["./image/player.png", "./image/enemy1.png"];

var view;

function game(){
    var canvas = document.getElementById("canvas");
    var context = canvas.getContext("2d");

    view = new GameView(context, 600, 400);
    view.loadImages(IMAGE_NAMES, IMAGE_FILES);
    
    setInterval(function(){
        view.draw();
    }, 16);
}

function keydown(event){
    var code = event.keyCode;

    switch(code){
        //'A' 後ろ
        case 65:
            view.onDirection(0x02);
            break;

        //'D' 前
        case 68:
            view.onDirection(0x01);
            break;

        //'W' 上
        case 87:
            view.onDirection(0x08);
            break;

        //'S' 下
        case 83:
            view.onDirection(0x04);
            break;

        //'space' 攻撃
        case 32:
            view.fire(true);
            break;
    }
}

function keyup(event){
    var code = event.keyCode;

    switch(code){
        //'A' 後ろ
        case 65:
            view.offDirection(0x02);
            break;

        //'D' 前
        case 68:
            view.offDirection(0x01);
            break;

        //'W' 上
        case 87:
            view.offDirection(0x08);
            break;

        //'S' 下
        case 83:
            view.offDirection(0x04);
            break;

        //'space' 攻撃
        case 32:
            view.fire(false);
            break;
    }
}

    </script>
  </head>
  <body onload="game()" onkeydown="keydown(event)" onkeyup="keyup(event)">
    <canvas id="canvas" width="600" height="400">Canvasをサポートしたブラウザで見てください</canvas>
  </body>
</html>

次にJavaScript。GameView.jsから。メインはdrawメソッド。現在はプレイヤー機とプレイヤー機が放った弾を描画している。

var GameView = function(context, width, height){
    var images = new Array();
    var isLoadedImage = false;
    var player;
    var gameInfo;

    this.draw = function(){
        if(!isLoadedImage){
            return;
        }

        context.fillStyle = 'rgb(00, 00, 00)';
        context.fillRect(0, 0, 600, 400);

        player.doAction(gameInfo);
        player.draw(context);

        for(i = 0; i < gameInfo.bullets.length; i++){
            var bullet = gameInfo.bullets[i];
            bullet.doAction(gameInfo);
            bullet.draw(context);
        }
    }
    
    this.loadImages = function(names, files){
        var count = 0;

        for(i = 0; i < files.length; i++){
            var image = new Image();
            image.src = files[i];
            images[names[i]] = image;

            image.onload = function(){
                count++;

                if(count == files.length){
                    init();
                }
            }
        }
    }
    
    this.onDirection = function(direction){
        player.onDirection(direction);
    }
    
    this.offDirection = function(direction){
        player.offDirection(direction);
    }
    
    this.fire = function(fire){
        player.fire = fire;
    }
    
    function init(){
        player = new Player(images["Player"]);
        gameInfo = new GameInfo();
        gameInfo.player = player;
        gameInfo.width = width;
        gameInfo.height = height;
        
        isLoadedImage = true;
    }
}

Player.js。doActionで移動をしてdrawで描画を行う。ライフや敵と衝突したときに敵に与えるダメージなどが保存される

var Player = function(image){
    this.image = image;
    this.position = new Point();
    this.rect = new Rect();
    
    this.life = 300;
    this.damage = 300;
    this.score = 10000;
    this.fire = false;
    
    var direction = 0;
    var fireCount = 0;
    
    this.draw = function(context){
        context.drawImage(this.image, this.position.x, this.position.y);
    }
    
    this.doAction = function(info){
        this.doMove();
        
        if(this.fire && fireCount == 0){
            var bullet = new PlayerBullet1();
            bullet.move(this.position.x + 60, this.position.y + 22);
            info.bullets.push(bullet);
            
            fireCount = 16;
        }
        
        if(fireCount > 0){
            fireCount--;
        }
    }
    
    this.doMove = function(){
        if((direction & 0x01) > 0){
            this.dmove(3, 0);
        }
        
        if((direction & 0x02) > 0){
            this.dmove(-3, 0);
        }
        
        if((direction & 0x04) > 0){
            this.dmove(0, 3);
        }
        
        if((direction & 0x08) > 0){
            this.dmove(0, -3);
        }
    }
    
    this.onDirection = function(input){
        direction |= input;
    }
    
    this.offDirection = function(input){
        direction &= ~input;
    }
    
    this.getRect = function(){
        this.rect.left = this.position.x + 8;
        this.rect.top = this.position.y + 14;
        this.rect.right = this.position.x + 68;
        this.rect.bottom = this.position.y + 30;
        
        return this.rect;
    }
    
    this.move = function(x, y){
        this.position.move(x, y);
    }
    
    this.dmove = function(dx, dy){
        this.position.dmove(dx, dy);
    }
    
    this.applyDamage = function(damage){
        this.life -= damage;
        this.life = this.life < 0 ? 0 : this.life;
        
        return this.life > 0;
    }

    this.intersect = function(object){
        return this.getRect().intersect(object.getRect());
    }
}

PlayerBullet1.js。プレイヤー機の弾を表している。敵に当たったとき敵に与えるダメージなどがある

var PlayerBullet1 = function(){
    this.position = new Point();
    this.rect = new Rect();
    this.life = 10000;
    this.damage = 100;
    this.score = 100000;

    this.doAction = function(info){
        this.position.dmove(8, 0);
    }
    
    this.move = function(x, y){
        this.position.move(x, y);
    }
    
    this.draw = function(context){
        context.fillStyle = 'rgb(255, 255, 0)';
        context.beginPath();
        context.arc(this.position.x + 1, this.position.y + 1, 2, 0, 360, true);
        context.closePath();
        context.fill();
    }

    this.getRect = function(){
        this.rect.left = this.position.x;
        this.rect.top = this.position.y;
        this.rect.right = this.rect.left + 2;
        this.rect.bottom = this.rect.top + 2;
    }
    
    this.applyDamage = function(damage){
        this.life -= damage;
        this.life = this.life < 0 ? 0 : this.life;
        
        return this.life > 0;
    }

    this.intersect = function(object){
        return this.getRect().intersect(object.getRect());
    }
}

GameInfo.js。ここに登録されている弾や敵を描画する。また、弾や敵の情報を渡す役目もある

var GameInfo = function(){
    this.bullets = new Array();
    this.enemies = new Array();
    this.enemyBullets = new Array();
    this.width = 0;
    this.height = 0;
}

次にPoint.js。座標を格納しているだけ

var Point = function(){
    this.x = 0.0;
    this.y = 0.0;

    this.move = function(x, y){
        this.x = x;
        this.y = y;
    }
    
    this.dmove = function(dx, dy){
        this.x += dx;
        this.y += dy;
    }
}

Rect.jsは長方形を定義している。座標の他に長方形同士が交差しているかどうかを返すintersectを定義している

var Rect = function(){
    this.left = 0;
    this.top = 0;
    this.right = 0;
    this.bottom = 0;
    
    this.set = function(left, top, right, bottom){
        this.left = left;
        this.top = top;
        this.right = right;
        this.bottom = bottom;
    }
    
    this.intersect = function(rect){
        if(this.left <= rect.left && rect.left <= this.right && this.top <= rect.top && rect.top <= this.bottom){
            return true;
        }
        
        if(this.left <= rect.right && rect.right <= this.right && this.top <= rect.top && rect.top <= this.bottom){
            return true;
        }
        
        if(this.left <= rect.left && rect.left <= this.right && this.top <= rect.bottom && rect.bottom <= this.bottom){
            return true;
        }
        
        if(this.left <= rect.right && rect.right <= this.right && this.top <= rect.bottom && rect.bottom <= this.bottom){
            return true;
        }
        
        return false;
    }
}