■はじめに
読んだらいい人、読んでほしい人
- ゲーム作ってみたい方。どうやって作ったの?が気になる方
- JavaScript触れるから、アドバイスしてやろうというお優しい方
こんなゲームを作りました↓
直線上に現れる敵をジャンプと攻撃で回避し、ハイスコアを狙うゲームです。
ゲーム作成のきっかけ
- プログラムをやってみたい → 何か自分で作ってみよう!
⇒何が楽しいかな → ゲーム?!
と短絡思考で着手しました。
■やったこと大雑把に
- youtubeで動画を見ながら、ゲーム制作の大体の構造を学ぶ
- テーマ、企画を考える
- コーディング、プログラミング
- ゲームバランス整える
- ビルド
- 事後作業(github,qitta)
以下、順に詳しく説明します。
1.youtubeで動画を見ながら、ゲーム制作の大体の構造を学ぶ
勉強させていただいた動画↓
Part1~6まであります。素地はこの動画を見て作成しました。
ゲームを組み立てる機能や設計について、ソースコードを交えながら説明してくれるのが
とても分かりやすかったです。
2. テーマ、企画を考える
ゲームの内容は、直線上に現れる敵をジャンプと攻撃で回避し、ハイスコアを狙います。
ちなみに、登場するプレイヤー、敵、味方は、私の家族をモチーフにしています(笑)
player… 母親
enemy1… 私 (眠そうなキャラ)
enemy2… 弟 (足が速いので、速いスピードでプレイヤー追い越し邪魔します)
enemy3… 父親 (よく寝ているので、母親に布団をもぎ取られます。布団が吹っ飛んだ~)
friend… 妹 (誰に対しても愛想がよく、優しいのでプレイヤーの体力を回復させます)
3.コーディング、プログラミング(ソースコードの備忘録)
全体に関してはGitHub上にアップロードしています。
ここでは一部のソースコードのみ紹介します。
ループ処理
setIntervalを用いて、16msに1回(60fps)update関数、draw関数が呼び出されるようにしています。また、gameoverになった際は、clearIntervalでループを止めています。
function fps(){
var fps = setInterval(()=> {
update();
draw();
if(scene == Scenes.GameOver){
console.log('stop');
clearInterval(fps);
}
},16);
}
キー入力
スペースでジャンプ、"F"キーで攻撃アクションが出力されるようにしています。
ジャンプ入力について、フレーム毎にy= speed + accelerationで変化を
つけました。ジャンプはy軸方向の移動かつy軸は下方向に正のため、初期値はspeedが負、accelerationが正になっています。
// スペースキーが押されたときの処理
if(e.key === ' '){
player.speed = -20;
player.acceleration =1.0;
}
攻撃アクションは、Fキーを押下した際に、plyaerとenemy3が一定距離以内の場合に発動し、enemy3を吹き飛ばします。
具体的な処理としては、場にいるすべての敵からenemy3だけに絞りこみ、playerとの距離が攻撃のあたり判定距離以内であるかを確認します。あたり判定有の場合、enemy3のy座標がスポーン時よりかなり高い位置に再設定されます。またおまけ要素として、playerのアニメーションが0.5秒間tornade要素つきに変更されるようにします。
// Fキーが押された時の処理
if(e.key === 'f' || e.key === 'F'){
for(const e of enemies){
if(e.type == 'enemy3'){
var diffX3 = (e.posx - player.posx)**2;
var diffY3 = (e.posy - player.posy)**2;
var distance3 = diffX3 + diffY3;
var r3 = (e.r+ player.r)**2;
var action_r = ((e.r + 150)+ player.r)**2;
if(distance3 > r3 && distance3 <= action_r ){
e.posy = 150;
e.speed = 20;
player.image.src = './image/player_tornade.png';
setTimeout(()=>{
player.image.src = "./image/player.png";
},"500");
}
敵の状態更新
ランダムな確率でenemy1~3とfriend1が、ランダムにスポーンされます。
point 1
必ずフレーム外から、enemy,friendがスポーンされます。
point 2
EnemyクラスからnewEnemyインスタンスを作成し、enemies配列に挿入し、敵を管理しています。
if (Math.random() < 0.01) {
const enemyType = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
const newEnemy = new Enemy();
newEnemy.type = enemyType.type;
newEnemy.image = new Image();
newEnemy.image.src = enemyType.src;
if(newEnemy.type == 'enemy2'||newEnemy.type == 'enemy3'||newEnemy.type == 'friend1'){
newEnemy.posx = Math.floor(Math.random() *1081)+1080;
}else if(newEnemy.type == 'enemy1'){
newEnemy.posx == 0;
}
newEnemy.posy = 600;
newEnemy.r = enemyType.r;
newEnemy.speed = enemyType.speed;
newEnemy.acceleration = enemyType.acceleration;
newEnemy.clash_flg = enemyType.clash_flg;
// 新しい敵をenemies配列に追加
enemies.push(newEnemy);
}
// 範囲外の敵を除外
enemies = enemies.filter((e) => (e.posx <= 1080) || (e.posx >= 0));
当たり判定
オブジェクトを円と仮定し、中心間の距離と半径の和の差の大小で衝突判定をおこないました。
player, friend, enemyにそれぞれ半径(r)を持たせ、三平方の定理を用いて距離を求めています。またオブジェクトに衝突フラグ(clash_flg)を持たせることで、スコア加算時に確認したり、複数回当たり判定が行われるのを防いでいます。
for(const e of enemies){
var diffX1 = (e.posx - player.posx)**2;
var diffY1 = (e.posy - player.posy)**2;
var distance1 = diffX1 + diffY1;
var r1 = (e.r+ player.r)**2;
if(player.heart < 3 && distance1 <= r1 && e.clash_flg == false && e.type == 'friend1'){
player.heart += 1;
e.clash_flg = true;
console.log('heal');
}else if(distance1 <= r1 && e.clash_flg == false && (e.type == 'enemy1' ||e.type == 'enemy2'||e.type == 'enemy3')){
player.heart -= 1;
e.clash_flg = true;
console.log('damage');
}
};
htmlとcss
主にシーンごとに画面上に表示する要素を入れ替えるために、使用しました。
createElement
… htmlタグを作る
setAttribute
… idをセットする
appendChild
… .htmlファイルに挿入する
const button_area = document.createElement("div");
button_area.setAttribute("id",button_area);
var button_area_style = button_area.style;
var button_area_styles = {
display: 'flex',
position: 'fixed',
width: '1080px',
}
for(var style in button_area_styles){
button_area_style[style] = button_area_styles[style];
}
document.body.appendChild(button_area);
4. ゲームバランス整える
ここは、確立やリスポーン地点の倍率などをよしなに調整しました。
5. ビルド
ふりーむに投稿しました。現在審査中です。
クリエイター登録して、およそ15分ほどでファイルのアップロードも可能でした!
6. 事後作業(github,qitta)
余談)githubに挙げるのにめちゃ苦労した
gitのインストールは事前にしていたが、リポジトリの意味が分からず、苦戦・・・
いろいろ調べた結果、SSHの設定ができてなかったために、commitでエラーが出てしまっていたという点でした。
■最後に
今回は、私が作っていて楽しい自己満足のゲームができてしまいました(笑)
アルゴリズムの基本の "K!" は知れたのではないかと満足です。
拙い説明、ソースコードのため、間違いや改善点は随時修正していこうと思います。
ちなみに、今回自己満足だけでなくQittaで記事を投稿しようと思った理由として、
qiitaに残す理由
- 今後の転職を見据えて自分のプログラムを磨きたい
- 言語化して忘れないようにしたい
- 自分の今の立ち位置を明確化する
がありました!引き続き精進していこうと思います。
以上、最後までお読みいただきありがとうございます。
■参考