見出し画像

ゲームの定番!ジャンプアクションゲームを作ろう!クミタテ式プログラミングドリル(p5JavaScript / Processing)

-アクションゲームは「実は」難しい

今回作るのはゲームの定番、ジャンプアクションゲームです。
「まずはマリオのようなアクションゲームを作ってみようという」初心者も多いと思うのですが、実はアクションゲームって作るのも難しいし、面白くするのも難しいジャンルのゲームだったりします。
たくさんのギミック、たくさんのイラスト、たくさんのステージを用意してアクションゲームを彩ろうとしてしまいますが、多くの場合、マリオの亜種、マリオの同人ゲームのようなものになってしまいます。
今回作るアクションゲームは、シンプルな仕掛けを1つだけ用意しただけでゲームとして成立させています。
物量で面白くしがちなアクションゲームを、仕組みで面白くするゲームデザインのヒントになれば幸いです。

デモアプリはこちらから。https://openprocessing.org/sketch/1540413
Unityroom版はこちらから。https://unityroom.com/games/stepman2

- クミタテ式プログラミングドリルとは?

クミタテ式は英語のコードを、日本語で書かれた図解(通称、図解くん)を使って解説し、プラモデルのように図解通りにコードを組み立てていきながら学習する、プログラミング教材です。
プログラミング的思考を学ぶよりも、コードの書き方を優先的に学習することを目的としています。

- 動画を見ながら学習する

動画と並行しながら学習するとより効率的に学習できるようになっています。
たくさんのゲームジャンルのプログラミング方法をお伝えしているのでチャンネル登録もよろしくお願いします。

- 開発環境

OpenProcessingを使ったp5js、Processingを環境を前提としています。

- ハートを押す

この記事にいいね(ハート)をお願いします。
押すと何か良いことが起きます。


■[ここからスタート!]画面のサイズを決める

画面サイズを600x400の固定サイズにしましょう。


■背景を黄色で塗りつぶし続ける

drawの中で背景を黄色で塗りつぶし続けましょう。drawの中で毎回背景を塗りつぶすことでアニメーションが実現できます。


■プレイヤーとなる四角形を表示する

固定の位置 (300, 200) に四角形を表示します。


■プレイヤーの位置を変数にする

プレイヤーの位置座標を変数xとyで管理するようにします。


■プレイヤーを落下させる

変数にしたことでプレイヤーを動かすことができるようになりました。
プレイヤーをとりあえず下方向に落下させましょう。


■プレイヤーを着地させる

ゴールとしてはプレイヤーが床ブロックで着地するようにしますが、仮設として適当な位置で着地させておきます。


■加速度をつけて落下させる

今のままでは落下が不自然です。落下速度が一定なことが原因です。
加速度を管理する変数dyを用意し、加速度を増やして落下させましょう。


■キーを押したらジャンプさせる

ゴールとしては床ブロックでジャンプさせますが、仮設として、とりあえず何かしらのキーを押したらジャンプさせるようにします。
ここでは加速度dyを上方向に向けて20の強さで設定しましょう。


■ここから先は有料エリアです。

有料エリアでは、テキストの他、後半の動画と完成コードが付属しています。

■矢印キーで左右移動させる

左右キーでプレイヤーを横に移動させます。
横の移動についても加速度「dx」を用意して、加速度を操作することでプレイヤーを間接的に移動させます。座標を直接編集するのではなく、加速度を使って間接的に動かすことによって、プレイヤーがスムーズに横移動ができるようになります。


■床ブロックを10個敷き詰める

繰り返し処理forを使って10回処理をループさせ、ブロックを10個敷き詰めます。


■当たり判定を用意する

床とプレイヤーとの当たり判定をする前に、当たり判定をしてくれるオリジナル命令「isHit関数」を用意しましょう。

 - ヒント -
四角形同士の当たり判定アルゴリズムは、
が、相手の右側より、にある
が、相手の左側より、にある
が、相手の下側より、にある
が、相手の上側より、にある
の4つの条件を満たしたときに当たっているとみなせます。


■床に着地したらジャンプするようにする

オリジナル命令「isHit関数」を使って、床ブロックとの当たり判定をしましょう。
当たっていた場合、自動的にジャンプさせます。
また、仮設しておいた自動着地とキージャンプは消去します。


■配列を使ってブロック情報を管理する

着地したブロックを消すために、ブロックごとに情報を管理する必要があります。10個のブロックをまとめて管理するために登場するのが配列です。
配列slotListを用意し、着地したブロックの情報を0にすることでブロックを消去させます。
配列slotListはこちらで用意していますのでお使いください。

// p5.js
let slotList = [
  1, 1, 1, 1, 2, 2, 1, 1, 1, 1,
];

// Processing Java
int[] slotList = new int[]{
  1, 1, 1, 1, 2, 2, 1, 1, 1, 1,
};

■床ブロックのジャンプ力によって色を変える

床ブロックの情報配列であるslotListには、0でブロックを消去する目的以外にも、ジャンプ力を管理する役割があります。
アルファ値(透明度)を使ってジャンプ力ごとにブロックの色をかえていきます。


■床の色に応じてジャンプ力を変える

着地した床の色に応じてジャンプ力を変えていきましょう。
ここではシンプルに、「ブロック情報 x -15」というアルゴリズムにしてみます。


クミタテ式の設計図はここまでです。
以下の応用課題は自分で考えてプログラミングしてみましょう。

(★☆☆)課題1. 複数のブロックを同時に踏まないようにする

気づいた人もいるかもしれませんが、着地地点によっては2つのブロックを消去してしまう場合があります。これはちょうど2つのブロックと当たり判定が実行されてしまったことにあります。
2つ以上のブロックと当たり判定をしないために、ブロックに当たったら、そのブロックの上に座標を移動させます。

(★★☆)課題2.全てのブロックが消えたら新しいブロックたちを作る

全てのブロックが消えてしまったら新しいブロックを生成しましょう。
全てのブロックが消えているかの0 or 1を返すisClear関数、全てのブロック情報を書き換えるcreateSlotList関数を作ると良いでしょう。

(★☆☆)課題3.ゲームオーバーを作る

プレイヤーが床ブロックに着地できずに画面からいなくなった場合にゲームオーバーにしましょう。

(★☆☆)課題4.スコアを作る

ブロックに着地するたびにスコアを増やしましょう。

(★★☆)課題5.プレイヤーランクを作る

スコアに応じてプレイヤーランクを表示しましょう。
初めてあなたのゲームをプレイする人にとっては、自分のスコアが良いスコアなのか悪いスコアなのか判断がつきません。客観的な指標としてプレイヤーランクを用意してあげることで、プレイへのフィードバックができます。

参考:こちらの動画教材をマスターすることでプレイヤーランクをテーマに、関数のことがわかるようになっています。
https://youtu.be/wB7QpzVFYdk

オリジナルゲームに仕上げよう

このゲームの面白いポイントは、踏む順番が重要なパズル感のあるアクションだと言うことです。
アクションゲームというと、ついつい要素を足してギミックを盛りだくさんにして楽しさを作り出そうとしてしまいますが、このようにパズル感を加えることで少ない機能でも十分にアクションゲームとして機能します。

完成コード

let x = 300;
let y = 200;
let dy = 0;
let dx = 0;
let slotList = [
  1, 1, 1, 1, 2, 2, 1, 1, 1, 1,
];

let flag = 0;
let score = 0;

function setup() {
	createCanvas(600, 400);
}

function draw() {
	background(255, 200, 0);
	dy = dy + 1;
	y = y + dy;
	x = x + dx;
	fill(255, 255, 255);
	rect(x, y, 30, 30);
	
	for(let i=0; i<10; i++){
		if(slotList[i] > 0){
			let block_x = i * 60;
			let block_y = 380;
			let hit = isHit(block_x, block_y, 60, 20, x, y, 30, 30);
			if(hit == 1){
				dy = slotList[i] * -15;
				slotList[i] = 0;
				y = block_y - 30;
				if(isClear() == 1){
					createSlotList();
				}
				score = score + 1;
			}
			fill(255, 100, 0, slotList[i] / 2 * 255);
			rect(block_x, block_y, 60, 20);
		}
	}
	
	if(y > 400){
		flag = 1;
	}
	
	// スコアの表示
	textAlign(CENTER, CENTER);
	textSize(64);
	fill(100, 100, 100);
	text(score, 300, 100);
	
	// ランクの表示
	textAlign(CENTER, CENTER);
	textSize(16);
	fill(100, 100, 100);
	text("Rank " + getRank(score), 300, 150);
	
	if(flag == 1){
		textAlign(CENTER, CENTER);
		textSize(64);
		fill(255, 255, 255);
		text("GAME OVER", 300, 200);
	}
}


function getRank(score){
	let rank = "D";
	if(score > 1){
		rank = "C";
	}
	if(score > 5){
		rank = "B";
	}
	if(score > 10){
		rank = "A";
	}
	return rank;
}

function keyPressed(){
	if(keyCode == LEFT_ARROW){
		dx = -5;
	}
	if(keyCode == RIGHT_ARROW){
		dx = 5;
	}
}

function isHit(px, py, pw ,ph, ex, ey, ew, eh){
	if(px < ex + ew && px + pw > ex){
		if(py < ey + eh && py + ph > ey){
			return 1;
		}
	}
	return 0;
}

function createSlotList(){
	for(let i=0; i<10; i++){
		slotList[i] = int(random(1, 3));
	}
}
function isClear(){
	let count = 0;
	for(let i=0; i<10; i++){
		if(slotList[i] > 0){
			count = count + 1;
		}
	}
	if(count == 0){
		return 1;
	}
	return 0;
}

この記事が気に入ったらサポートをしてみませんか?