JSP/サーブレットでダイスコンプゲームを作成しよう!

JSP&Servlet

1回ダイスを振るのに300円かかる。1~6までのダイスの目をコンプリートするのにかかった回数と金額を表示するアプリを作成せよ。

今回はこのアプリをJSP/サーブレットを使って作成してみよう。今回はMVCに役割を分担し、データの保持にはセッションスコープを使っていく。

フォルダ構成

今回はエクリプスを使って作成していくのだがまずは全体のフォルダ構成を掲載しておく、迷子になりそうなときはいつでもここを参照してほしい

作成

1.エクリプスにて新規動的Webプロジェクトを作成しアプリ名をDiceCompとする。
2.以下からzipファイルをダウンロードし、展開したのち中に入っている画像7枚をWebContent/imagesフォルダにいれる(フォルダ構成参照)

3.今回作成するのは以下のような見た目を持つゲームだ。
Diceボタンを押すとサイコロが振られ、その出目が上のリストから消えていく。
下には何回目かという情報とここまでにいくら使ったかが表示されている。
すべてコンプすることができればクリアだ。

この見た目を形成するためには何が必要かを吟味し、まずはmodelを作成していく。

modelの作成

4.新規クラスからmodel.Dice.javaを以下のように作成する。

package model;

import java.io.Serializable;

public class Dice implements Serializable{
	private int[] diceStates= {0,0,0,0,0,0};//出目の状態を管理
	private int count=0;//何回目か
	private int total=0;//いくら消費しているか?
	private int nowDice=0;//一番新しい出目
	private String msg="Push this Button";
	public int[] getDiceStates() {
		return diceStates;
	}
	public void setDiceStates(int[] counts) {
		this.diceStates = counts;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}
	public int getTotal() {
		return total;
	}
	public void setTotal(int total) {
		this.total = total;
	}
	public int getNowDice() {
		return nowDice;
	}
	public void setNowDice(int nowDice) {
		this.nowDice = nowDice;
	}

}

必要な情報を持つBeanを作成している。

5.ダイスを振った際のロジッククラスを作成しよう。以下のようなmodel.DiceLogic.javaを作成する。

package model;

public class DiceLogic {
	public void throwDice(Dice dice) {
		final int fee= 300;
		int index=(int)(Math.random()*6);
		//statesチェック(0:一度も出ていない,1:初めて出た,2以上:過去に出た)
		int[] states=dice.getDiceStates();
		//前回初めてだった目を過去にステートを変える
		for(int i=0;i<states.length;i++) {
			if(states[i]==1) {
				states[i]=2;
			}
		}
		states[index]++;
		dice.setDiceStates(states);
		dice.setNowDice(index+1);
		dice.setCount(dice.getCount()+1);
		dice.setTotal(dice.getTotal()+fee);
		String msg=String.format("Count:%d Total:%d", dice.getCount(),dice.getTotal());
		//完了チェック
		boolean isFinished=true;
		//すべてのStateをチェックして0があった場合は未完了
		for(int n:dice.getDiceStates()) {
			if(n==0){
				isFinished=false;
				break;
			}
		}
		if (isFinished) {
			msg += " Complete! Try again!";
		}
		dice.setMsg(msg);
	}

}

出たダイスの目によってDiceインスタンスの状態を変更している。
このようにViewの生成に必要なパーツはBeanのフィールドに持たせ、ロジッククラスで値の生成を行うとよい。

controllerの作成

モデルができたのでコントローラを作っていこう。今回通信方法はget通信を用いる。
6.新規サーブレットからいかのようなcontroller.Main.javaを作成する。

package controller;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import model.Dice;
import model.DiceLogic;

@WebServlet("/Main")
public class Main extends HttpServlet {

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		//sessionインスタンス取得
		HttpSession session=request.getSession();
		//sessionからダイスインスタンスの取得を試みる
		Dice dice=(Dice)session.getAttribute("dice");
		//sessionに無い(初めて)かCompleteの文字が含まれていれば最初から
		if(dice == null || dice.getMsg().contains("Complete")) {
			//新規にダイスインスタンス作成
			dice=new Dice();
		}else {
			DiceLogic dl=new DiceLogic();
			//ダイスを振る処理を行う
			dl.throwDice(dice);
		}
		//sessionスコープにダイスインスタンスを詰める
		session.setAttribute("dice", dice);
		//フォーワード
		RequestDispatcher rd=request.getRequestDispatcher("/WEB-INF/view/main.jsp");
		rd.forward(request, response);
	}
}

初回かどうかで場合分けをし、インスタンスをSessionスコープに詰めてフォワードしている。今回はリクエストをまたいで情報を保持する必要があるのでSessionスコープが最適だ。

viewの作成

ではviewを作成していこう。今回このファイルは直接リクエストされると困るのでWEB-INFフォルダの中にviewフォルダを作成しそこに格納する。

7.WEB-INFフォルダの中にviewフォルダを新規作成し、その中にmain.jspを以下のように作成する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8" import="model.Dice"%>
<%
	Dice dice=(Dice)session.getAttribute("dice");
	int[] states=dice.getDiceStates();
%>
<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
	<title>title</title>
	<link rel="stylesheet" href="css/main.css"/>
	<link rel="preconnect" href="https://fonts.gstatic.com">
	<link href="https://fonts.googleapis.com/css2?family=Audiowide&display=swap" rel="stylesheet">
</head>
<body>
	<div id="dices">
	<% for(int i=0;i<states.length;i++){%>
		<% if(states[i]<= 1){%>
		<img src="images/dice<%=i+1 %>.png" class="<%=states[i]==1?"diceOut":""%>">
		<%} %>
	<%} %>
	</div>
	<div id="dice">
	<%if(dice.getNowDice() !=0){ %>
		<img src="images/dice<%=dice.getNowDice() %>.png" class="diceIn">
	<%} %>
	</div>
	<form action="/DiceComp/Main">
		<button type="submit">Dice!<span class="result"><%=dice.getMsg() %></span></button>
	</form>
</body>
</html>

Sessionスコープからインスタンスを取り出し、その情報をもとにviewを形成している。このviewにスタイルを当てていこう

8.WebContentの中にcssフォルダを作成し、その中にmain.cssを以下のように作成する。

body {
	background: url(../images/bg.jpg) #efcfa9;
	background-size: cover;
	height: 100vh;
	display: flex;
	flex-direction: column;
	justify-content: space-around;
	align-items: center;
}

#dices {
	display: flex;
	justify-content: space-around;
	padding-bottom: 3vh;
	border-bottom: 3px dotted white;
}

#dices img {
	width: 12vw;
	height: 12vw;
	max-width: 170px;
	margin: 2vmin;
}

#dice img {
	/*最初は透明にして見えないようにしておく*/
	opacity: 0;
}

button {
	font-family: 'Audiowide', cursive;
	width: 80vw;
	font-size: 10vmin;
}

button:hover {
	cursor: pointer;
}

button span {
	font-size: 5vmin;
	opacity: 0;
}
.diceHidden {
	display: none;
}

.diceOut {
	animation-name: diceOut;
	animation-delay: 1.5s;
	animation-duration: 2s;
	animation-fill-mode: forwards;
}

@keyframes diceOut {
	to{
		width:  0;
		margin: 0;
		opacity: 0;
	}
}
.diceIn {
	animation-name: diceIn;
	animation-delay: 0.5s;
	animation-duration: 1s;
	animation-fill-mode: forwards;
}

@keyframes diceIn {
	from {
		width:0;
		height: 0;
		opacity: 0;
		transform: rotateX(0);
	}
	to {
		width: 15vw;
		height: 15vw;
		opacity: 1;
		transform: rotateX(360deg);
	}
}
.result {
	display: block;
	animation-name: result;
	animation-delay: 1.5s;
	animation-duration: 1s;
	animation-fill-mode: forwards;
}

@keyframes result {
	from {
		opacity:0;
	}

	to {
		opacity: 1;
	}
}

このHtml/CSSに関してはこちらの記事を参考にしてもらいたい。

完成

以上で完成だ。最短6回でのコンプを目指して遊んで見てほしい。
Javaで作成するのであればアプリケーションスコープを使って何回でクリアできたのかを集計していくと面白そうだ。ぜひチャレンジしてほしい。

関連記事

コメント

タイトルとURLをコピーしました