パズル&クエスト with ラブライブ の解説


# はじめに
これは香川大学工学部サークルSLP(学生プログラミング研究所)のアドベントカレンダー 20日目の記事です。
今回は、OSC広島2017の学生LTで発表した自作ゲームのパズル＆クエスト with ラブライブについて書いていきたいと思います!
初のアドベントカレンダーなので少し緊張しながら記事を書いています^^

# パズル＆クエスト with ラブライブとは
パズル＆クエスト with ラブライブとは、某有名スマホパズルゲームと、某有名RPGゲームを組み合わせ、ラブライブ要素を盛り込むことで作成した「僕が考える最高に面白いラブライブゲーム」です。ちなみに開発言語はJavaで、Eclipseを駆使して作成しました。

ルールはとても簡単です。
マウスを操作して、ドロップ(ラブライブのキャラが描かれた丸いもの)を並び替え、コンボ(同じ色の3つ以上の連)を組んでいきます。
そして組んだコンボ数が、コンボノルマ数よりも大きければ、相手にダメージを与えることができます。逆にコンボノルマ数よりも小さいと、逆にダメージを受けてしまいます。自分のHPが0になる前に相手のHPを0にすればゲームクリアとなります。

なんて単純で面白いゲームなんだろう!!

![aa.png](https://qiita-image-store.s3.amazonaws.com/0/224661/a4d712ed-1822-cd3d-3448-1cc96853ddfb.png)

# 2つのゲームモード
ラブライブにはμ's(ミューズ)とAqours(アクア)という2つのグループがあることを知っていますか??
μ'sが初代ラブライブの主人公グループであり、Aqoursが現在アニメ2期放送中のラブライブサンシャインの主人公グループなのです。
どちらか1つを選ぶわけにもいかなかったので、このゲームにもμ'sモードとAqoursモードを実装しました。下の画像はタイトル画面になります。左側がμ'sモード、右側がAqoursモードになります。
ラブライブのロゴをクリックすることで、モードを切り替えることができます。
キャラクターが飛び出しているドアをクリックすると、難易度選択画面に進むことができます。
さらにこのドアは、マウスが乗っている間は開き、マウスが載っていないと閉じてしまうのです。
まぁマウスホバー時の画像と、それ以外の時の画像を入れ替えてるだけなんですけどね
![bb.png](https://qiita-image-store.s3.amazonaws.com/0/224661/a63f0033-8f79-b27c-a137-0f5804eddc60.png)

ラブライブのロゴは、実はボタンになっており、ボタン上でロゴの画像を表示させ、ボタンの枠を非表示にしています。重要なのは、`this.setContentAreaFilled(false);`と`this.setBorderPainted(false);`です。この2つの処理によりボタンを非表示にすることができるのです。

```java:ModeSelectButton.java
	public ModeSelectButton(String aqours,String muse, TitleButton titleButton) {
	    super();
	    this.titleButton = titleButton;
	    aqoursURL = getClass().getClassLoader().getResource(aqours);
	    museURL = getClass().getClassLoader().getResource(muse);
	    this.setIcon(new ImageIcon(museURL));   // 画像をボタン上に張り付け
		this.setContentAreaFilled(false);     // 背景を透明に
		this.setBorderPainted(false);         // ボタンの枠を非表示
		addMouseListener(this);
		this.SelectSound = new SoundPlayer("select.wav");
	}
```

# ドロップ作成
ドロップを作成する際に1つ重要なポイントがあります。
それは、今回使用するドロップの種類は5種類であることです。これは某スマホパズルゲームが5種類+回復の計6種類であり、回復はこのゲームでは使用しないためです。しかし、ラブライブのキャラクターは1グループ9人なのです。

さぁこれは困ったことになったぞ…

そこで、9人を4：4：1に分け、余った1人を両方のグループに所属させることで、5：5に分けることにしました。
下の表がドロップのグループになります。紫のドロップ(津島善子、東條希)を2人として扱うことで、5人グループを2つ作成しています。
そして、ゲームを始めるときにランダムでどちらかのグループを決定することで、全員分のドロップで遊べるようにしています。

| μ's1グループ      |       μ's2グループ | Aqours1グループ      |       Aqours2グループ | 
|:----------------:|:-----------------:|:----------------:|:-----------------:|
|![Nozomi.png](https://qiita-image-store.s3.amazonaws.com/0/224661/d5c4c66c-bc07-6479-994a-80c9942e6e44.png)|![Nozomi.png](https://qiita-image-store.s3.amazonaws.com/0/224661/2a17b493-edd0-2c12-d879-e5291fa82254.png)| ![Yoshiko.png](https://qiita-image-store.s3.amazonaws.com/0/224661/aa0967f8-8ea7-3eee-6903-4e78e08cb4bc.png) | ![Yoshiko.png](https://qiita-image-store.s3.amazonaws.com/0/224661/1c1b0b9c-a9a8-aebe-7da2-7879308fc32a.png) 
|![Niko.png](https://qiita-image-store.s3.amazonaws.com/0/224661/b71648f6-ce46-4461-b672-6735614c8859.png)|![Maki.png](https://qiita-image-store.s3.amazonaws.com/0/224661/d6821dbf-336e-fdd1-2f19-c6cf76d5e406.png)| ![Daiya.png](https://qiita-image-store.s3.amazonaws.com/0/224661/259a259f-ac89-db34-5908-c6d47105da77.png) |              ![Ruby.png](https://qiita-image-store.s3.amazonaws.com/0/224661/07a40133-8bb9-c056-aefb-0936ec31178d.png) | 
|![Honoka.png](https://qiita-image-store.s3.amazonaws.com/0/224661/3cc7e0b9-a8bd-25d6-3dba-64d4ccc1c208.png)|![Rin.png](https://qiita-image-store.s3.amazonaws.com/0/224661/cb8f8acf-a99b-954d-15cc-39c40ebbb81e.png)|             ![Chika.png](https://qiita-image-store.s3.amazonaws.com/0/224661/781d8d4e-0309-95c8-638f-e86af27afce5.png)  |             ![Hanamaru.png](https://qiita-image-store.s3.amazonaws.com/0/224661/c561587a-b53a-6980-3947-e1c705b96c3f.png)| 
|![Hanayo.png](https://qiita-image-store.s3.amazonaws.com/0/224661/88cbf474-fe10-eadd-d3fd-015fc64ff969.png)|![Kotori.png](https://qiita-image-store.s3.amazonaws.com/0/224661/636c53f9-fb07-6866-4787-ef79bbbced30.png)| ![Kanan.png](https://qiita-image-store.s3.amazonaws.com/0/224661/f518c166-eee0-3881-37bd-cd413f5ac84e.png)|             ![Mari.png](https://qiita-image-store.s3.amazonaws.com/0/224661/2f23c269-837b-6e8a-17e7-7b1f0e8309c5.png)|
|![Eri.png](https://qiita-image-store.s3.amazonaws.com/0/224661/4dbf0086-2b7b-a23c-d69b-e3e28c3137b4.png)|![Umi.png](https://qiita-image-store.s3.amazonaws.com/0/224661/d556eea5-1dc1-7006-2870-b05532edd314.png)| ![Riko.png](https://qiita-image-store.s3.amazonaws.com/0/224661/d683769b-b597-2656-0dac-701e9cb26458.png)|           ![You.png](https://qiita-image-store.s3.amazonaws.com/0/224661/10ff6424-e19d-3c77-8718-48470c6ccc30.png)|

#敵キャラ作成
敵キャラはとても簡単でした。いい感じの敵キャラ画像をとってきて、色を変えて、目を隠すだけです。誰でもできますね!

# ゲームの流れ
ゲームの流れは以下のようになります。
1. タイトル画面でμ'sモード、Aqoursモードの選択
2. 難易度選択画面で、EASY,NORMAL,HARDの選択
3. ゲーム画面でゲームをプレイ
4. ゲームクリア OR ゲームオーバー
5. 難易度選択画面に戻る

ゲームの管理を行うために列挙型のGameSceneを用意しました。
コードを見てもわかる通り2つのモードを同時に管理する方法がわからず、
タイトル画面を表すSceneや選択画面を表すSceneを2つずつ用意してしまいました。

``` java:GameScene.java
public enum GameScene {
	title_aqours,     // タイトル画面(Aqoursモード)
	title_muse,       // タイトル画面(μ'sモード)
	select_aqours,    // 選択画面(Aqoursモード)
	select_muse,      // 選択画面(μ'sモード)
	game_aqours,      // ゲーム画面(Aqoursモード)
	game_muse,        // ゲーム画面(μ'sモード)
	gameClear_aqours, // ゲームクリア時(Aqoursモード)
	gameClear_muse,   // ゲームクリア時(μ'sモード)
	gameOver_aqours,  // ゲームオーバー時(Aqoursモード)
	gameOver_muse,    // ゲームオーバー時(μ'sモード)
	combo_aqours,     // コンボ中(Aqoursモード)
	combo_muse        // コンボ中(μ'sモード)
}
```

# 難易度選択画面でEASY、NORMAL、HARDの選択
難易度は、EASY、NORMAL、HARDの3つを用意しました。この難易度によって、コンボノルマ数や敵のHPが変化します。
![cc.png](https://qiita-image-store.s3.amazonaws.com/0/224661/c4795e55-0d6f-e581-6af6-158684c47221.png)

# ゲーム画面の実装
いよいよゲーム画面です。
ゲーム画面では以下のクラスを定義しています。
- Boardクラス ドロップの盤面のクラス
- Fieldクラス ゲーム画面上部のクラス
- Dropクラス ドロップを管理するクラス
- PlayerFrameクラス プレイヤーの情報を表示するクラス(画面左上)
- TextFrameクラス コンボノルマ数を表示するクラス
- FieldManagerクラス Fieldクラスの管理、BoardManagerクラスとのやり取りを行うクラス
- BoardManagerクラス Boardクラスの管理 Fieldクラスとのやり取りを行うクラス

さすがに全部は説明できないので、Boardクラスの一部を説明します。

# Boardクラス
Boardクラスはドロップの盤面を表すクラスです。
このクラスではDrop型のArrayListのArrayListを用意し、Dropを管理します。
コンストラクタではランダムで0から4の値を取得し、その値に応じてどの色のドロップを生成するかを決定します。


```java:board.java
public Board() { 
        this.width = GameEnvironment.BOARDWIDTH;               // 盤面の横のドロップの数を取得するよ 
	    this.height = GameEnvironment.BOARDHEIGHT;              // 盤面の縦のドロップの数を取得するよ
		this.dropDiameter = GameEnvironment.DROPDIAMETER;            // ドロップの大きさを取得するよ
    	 board = new ArrayList<ArrayList<Drop>>();        // Dropクラスの2次元配列を定義するよ
    	// this.setBounds(0, this.environment.getBoardPosition(), this.width * this.dropDiameter, this.height * this.dropDiameter);  // 指定した位置と大きさで盤面を表示するよ
 
    	 // 二重for文で二次元配列にDropクラスの子クラス(火、水、木、闇、光)を入れていくよ
    	 for (int k1 = 0; k1 < this.height; k1++) {
    		 this.board.add(new ArrayList<Drop>());       // Dropクラスの配列の中にDropクラスの配列を作るよ
    		 for (int k2 = 0; k2 < this.width; k2++) {
    			 // ランダムな値によってどのドロップにするか決めるよ
    			 switch(random()) {
    			 case 0: this.board.get(k1).add(new FireDrop(this.dropDiameter / 2,this.dropDiameter / 2)); break;  // ランダムの値が0だったら火ドロップを格納するよ 
    			 case 1: this.board.get(k1).add(new WaterDrop(this.dropDiameter / 2,this.dropDiameter / 2)); break; // ランダムの値が1だったら水ドロップを格納するよ   
    			 case 2: this.board.get(k1).add(new WoodDrop(this.dropDiameter / 2,this.dropDiameter / 2)); break;  // ランダムの値が2だったら木ドロップを格納するよ
    			 case 3: this.board.get(k1).add(new DarkDrop(this.dropDiameter / 2,this.dropDiameter / 2)); break;  // ランダムの値が3だったら闇ドロップを格納するよ
    			 case 4: this.board.get(k1).add(new LightDrop(this.dropDiameter / 2,this.dropDiameter / 2)); break; // ランダムの値が4だったら光ドロップを格納するよ
    			 }
    		 }
    	 }
    }
```

# ゲームクリア OR ゲームオーバー
自分のHPが0になる前に敵のHPを0にすればゲームクリア、逆に敵のHPが0になる前に自分のHPが0になればゲームオーバーになります。
某有名RPGゲームであれば、HPが0になると勇者(プレイヤー)は死んでしまいます。
しかし、ラブライブに「死」なんて物騒な要素を入れるわけにはいけません。
このゲームでは、ゲームオーバーになるとラブライブで予選敗退してしまいます。
ゲームクリアだと、ラブライブで優勝となります。
ゲームクリア時の表示される画像は、ゲーム制作時に流行っていた某熱盛です。
![dd.png](https://qiita-image-store.s3.amazonaws.com/0/224661/efcc3fa1-e4a1-8dd9-3345-7fbbda86fa01.png)

# おわりに
いかがだったでしょうか。初めての投稿ということで、読みにくかったかもしれませんがこのゲームを少しでもおもしろそうだと感じていただければ嬉しいです。
ソースコードを知りたい方はGithubにあげていますので、ぜひ見てください!
これからもラブライブ関係のアプリを作っていきますのでよろしくお願いします。
最後に、ゲームの様子をのせて筆をおきたいと思います。(ここでおくのはキーボードですがｗ)
![love.gif](https://qiita-image-store.s3.amazonaws.com/0/224661/102e0cf1-6766-03af-374f-cfb4b4836427.gif)

[github-takuminish](https://github.com/takuminish)