大人気?シリーズ「ピカチュウから学ぶオブジェクト指向」、今回はレベルシステムの導入編です。
第1回(基礎編)、第2回(クラス継承編)で作成したPokemonとPikachuのクラスを使用するので、最初から学習したい人はぜひご参考ください。
レベルシステムの導入
ポケモンのゲームと言えば、レベルシステムが楽しみの一つです。RPGゲームでは王道とも言えるレベルという概念は、育てることの楽しみや、強くなることで新しい技や変化が起こるという期待感を与えてくれます。
第3回では第1回、第2回で学んだオブジェクト指向の予習をしながら増設していきます。その上で、レベルという概念の初期準備をしていきましょう。
保守性を高める
第3回となれば、記述量も増えてきました。実際の開発でも役立つよう、本来のシステム開発により近い状態にすること意識して、以下の2点を意識しながら進めていきます。
ファイルの分割
コメントの追加
ファイルの分割
メソッドやプロパティが増えてくれば、それだけ記述量が多くなり、1ファイルの可視性は悪くなります。オブジェクト指向で使用するクラスは、基本的に1クラス1ファイル(クラス名.php)です。なので、今回作成したポケモンとピカチュウのクラスと実行ファイルを以下の階層で分割していきましょう。
/index.php(実行ファイル)
/Class/Pikachu.php(ピカチュウ※子)
/Class/Pokemon.php(ポケモン※親)
ファイルを分割すれば、それぞれで必要なファイルを読み込まなければなりません。
まずは、ピカチュウクラスのファイル(/Class/Pikachu.php)の最上部に以下の記述を追加します。
require_once('/Class/Pokemon.php');
extendsでポケモンのクラスを継承しているため、Pokemon.phpを読み込まなければなりません。それに対してポケモンクラスは読み込まれている継承元のため、現在は特に別ファイルを読み込む必要がありません。
次に、実行ファイル(/index.php)の最上部に以下の記述を追加します。
require_once('Class/Pikachu.php');
実行ファイルではピカチュウのインスタンス(実体)をするためにnew Pikachuと呼び出しています。そのため、実行ファイルではPikachu.phpを読み込まなければなりません。
コメントの追加
ファイル分けをすれば、次にコメントに記述を見直していきます。以下参考URLです。
コメントにも書き方があります。特にチーム開発をするのであれば、どういった記法をするのか共有して置かなければなりません。
※今回のポケモンに関しては、参考URLの書き方を完全に遵守しているわけではありませんが、比較的同じようにわかりやすくまとめていきます
# ピカチュウクラス内
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'ピカチュウ';
# ポケモンクラス内
/**
* 正式名称を取得する
* @return string
*/
public function getName()
{
return $this->name;
}
オブジェクト指向を使った開発の場合、多くのメソッドやプロパティを使用するため、それがどういった値を格納しており、引数や返り値などがひと目で分かるようにしておくことが保守性を高めることに繋がります。これはPHPに限らず他の言語についても同様です。
プログラミングに余裕が出てくれば、コメントの記法についても頭に入れておくことをオススメします。
初期レベルの実装
保守性について少し触れたところで、レベルシステムを実装するための開発を進めていきましょう。
ポケモンは捕まえた時(出現したとき)に初期レベルが設定されています。中には決まったレベルのポケモンもいますが、野生の場合は前後1〜2レベルぐらいのことが多いです。
そのためにも、まずは初期レベルの振れ幅をピカチュウのクラスへ追加しましょう。
ピカチュウのクラス(/Class/Pikachu.php)
/**
* 初期レベル
* @var array
*/
protected $default_level = [
7, 8, 9,
];
初期レベルはポケモンによって異なるため、親クラスではなくピカチュウクラスへ設定しています。今回は7〜9レベルの幅で出現させます。
次に、レベルをセットするためのメソッドをポケモンクラスに追加します。
ポケモンのクラス(/Class/Pokemon.php)
/**
* 現在のレベル
* @return integer
*/
protected $level;
次に、ポケモンが出現または捕まえた時(インスタンス時)に初期レベルを決定したいので、コンストラクタ(__constract)内に初期レベルを設定するメソッドを記述し、メソッドは全ポケモン共通で使用できるためポケモンクラスに用意します。
ポケモンのクラス(/Class/Pokemon.php)
<?php
// ポケモン
abstract class Pokemon
{
… 一部省略
/**
* レベルをセットする
* @return void
*/
protected function setLevel()
{
// 初期レベルからランダムで値を取得
$key = array_rand($this->default_level);
$this->level = $this->default_level[$key];
}
}
ピカチュウのクラス(/Class/Pikachu.php)
<?php
require_once('Class/Pokemon.php');
// ピカチュウ
class Pikachu extends Pokemon
{
… 一部省略
/**
* インスタンス作成時に実行される処理
*/
public function __construct()
{
$this->setLevel();
echo 'ピカチュウをゲットした';
}
}
コンストラクタ内に記述していたニックネーム設定用のメソッドを削除しました。こちらは後ほど呼び出す形式で実装しましょう。
追加で、呼び出した際にそのことがわかるように、echoを使って「ピカチュウをゲットした」というメッセージを出力しています。
では、初期レベルをセットしているポケモンクラス内のsetLevelのメソッドの処理を確認してみましょう。
/**
* レベルをセットする
* @return void
*/
protected function setLevel()
{
// 初期レベルからランダムで値を取得
$key = array_rand($this->default_level);
$this->level = $this->default_level[$key];
}
初期レベルを設定するメソッドは、外部から呼び出しをされては困りますので、アクセス修飾子はprotectedになります。
まずは、default_levelに格納されている数値(レベル)をランダムで取得するためにarray_randを使用します。
array_rand(PHP.net)
array_randの返り値はランダムで取得した値が格納されているキーを返却してくれます。なので、$this->default_levelに対して取得したキーを指定して、レベル($this->level)に格納します。
$this->level = $this->default_level[$key];
では、index.phpを以下の用に記述して出力結果を確認してみましょう。
出力ファイル(/index.php)
<?php
require_once('Class/Pikachu.php');
$pikachu1 = new Pikachu;
$pikachu2 = new Pikachu;
?>
<p>ピカチュウ1のレベル:<?=$pikachu1->getLevel()?></p>
<p>ピカチュウ2のレベル:<?=$pikachu2->getLevel()?></p>
出力結果は以下の通りです。
※一部改行して表示させています
# 出力結果
ピカチュウをゲットした
ピカチュウをゲットした
ピカチュウ1のレベル:8
ピカチュウ2のレベル:7
それぞれ初期レベルの範囲内で別の値が割り振られていますね。もちろん上記の設定では3通りしかないので、同じレベルのピカチュウがゲットできることもあります。
レベルに合わせた技の習得
レベルが高ければ、それに合わせて技を覚えている場合がありますね。初代(赤・青・緑)のピカチュウであれば、レベルが9になると「でんじは」という技を覚えます。なので、技リストに対して習得レベルを割り当てましょう。
ピカチュウのクラス(/Class/Pikachu.php)
/**
* レベルアップで覚える技
* @var array
*/
protected $level_move = [
[1, 'でんきショック'],
[1, 'なきごえ'],
[9, 'でんじは'],
[16, 'でんこうせっか'],
[26, 'スピードスター'],
[33, 'こうそくいどう'],
[43, 'かみなり'],
];
ピカチュウのクラスに対して、level_moveというプロパティを追加し、習得レベルと技名称を1行に格納しました。ここまでくればデータベースで管理するのが一般的ですが、今回は簡易のアプリケーションのため、現段階はPHPだけで実装していきます。
注意ポイントは、レベルはkeyとして割り振らず、名称と同様に値(value)として割り振っている点です。初期技や同レベルで習得できる技があると重複するというのが理由です。
次に、ピカチュウクラスに設定していたmoveプロパティ(技の一覧)の初期として技を入れる必要が無くなったので、空配列を初期値としてポケモンクラスへ移動しておきましょう。
ポケモンのクラス(/Class/Pokemon.php)
/**
* 覚えている技
* @var array
*/
protected $move = [];
次に、初期技をセットするためのメソッドを作成しましょう。こちらも全ポケモン共通で使用できるのでポケモンのクラスへ追加します。
ポケモンのクラス(/Class/Pokemon.php)
/**
* 技を覚える
* @return string
*/
public function setMove($move)
{
$this->move[] = $move;
if(count($this->move) > 4){
unset($this->move[0]);
}
}
… 一部省略
/**
* 初期技をセットする
* @return void
*/
protected function setDefaultMove()
{
foreach($this->level_move as list($level, $move)){
if($level <= $this->level){
// 現在レベル以下の技であれば習得
$this->setMove($move);
}else{
// 現在レベルを超えていれば処理終了
break;
}
}
}
まずは追加したsetDefaultMoveのメソッドから確認していきましょう。
foreach($this->level_move as list($level, $move)){
foreachを使用して、レベルで習得する技の一覧($this->level_move)を上から順に回していきます。asの後ろは$valのように値を取得せず、list関数を使用します。
list(PHP.net)
foreachで回す際に取得できる値は、レベルと技名称が格納された配列です。それを添え番で取り出すよりも名称のついた変数として取り出せる方が都合が良いので、今回はlistを使用しています。配列の左から順にlist内で指定した変数へ格納されるので、1回目であれば$levelには「1」、$moveには「でんきショック」が格納されています。
次にif文を使った条件分岐を使い、格納する技を決めていきます。
if($level <= $this->level){
// 現在レベル以下の技であれば習得
$this->setMove($move);
}else{
// 現在レベルを超えていれば処理終了
break;
}
まず、技レベル($level)と現在のレベル($this->level)を比較します。もし技レベルが現在のレベル以下であれば習得可能なので、技を覚えるためのメソッドであるsetMoveを使用します。
もし、技レベルが現在のレベルより高ければ、その時点で処理を終了します。
※ピカチュウのクラスに設定されているlevel_moveプロパティがレベル順に上から並んでいることを想定しています
初期技はレベルと同様にインスタンスを作成した時点で覚えて置かなければなりませんので、ピカチュウクラスのコンストラクタに追記しましょう。
ピカチュウのクラス(/Class/Pikachu.php)
/**
* インスタンス作成時に実行される処理
*/
public function __construct()
{
$this->setLevel();
$this->setDefaultMove();
echo 'ピカチュウをゲットした';
}
ここのポイントは、メソッドを呼び出す順番です。setDefaultMove(初期技を設定するメソッド)では現在のレベルを使用しています。なので、まずはsetLevel(レベルを設定するメソッド)を記述しておく必要があります。逆になってしまうと技が覚えられなくなってしまうので注意しましょう。
それでは、出力ファイル(index.php)に以下を記述して結果を確認してみましょう。
出力ファイル(/index.php)
※一部改行して表示しています
# 出力結果
ピカチュウをゲットした
ピカチュウをゲットした
ピカチュウ1のレベル:9
ピカチュウ1の技
array (
0 => 'でんきショック',
1 => 'なきごえ',
2 => 'でんじは',
)
ピカチュウ2のレベル:8
ピカチュウ2の技
array (
0 => 'でんきショック',
1 => 'なきごえ',
)
レベル9のピカチュウは「でんじは」を覚えており、レベル8のピカチュウは覚えていませんね。これで技に対するレベルの割り振りが完了しました。
以下最終コードです。
※第1回、第2回で作成したコードを一部修正しています
ポケモンのクラス(/Class/Pokemon.php)
<?php
// ポケモン
abstract class Pokemon
{
/**
* ニックネーム
* @return string(min:1 max:5)
*/
protected $nickname;
/**
* 現在のレベル
* @var integer
*/
protected $level;
/**
* 覚えている技
* @var array
*/
protected $move = [];
/**
* ニックネームを付ける
* @return string
*/
public function setNickname($nickname)
{
if(empty($nickname) || mb_strlen($nickname, 'UTF-8') > 5){
echo 'ニックネームは1〜5文字で入力してください';
return;
}
$this->nickname = $nickname;
}
/**
* 正式名称を取得する
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* ニックネームを取得する
* @return string
*/
public function getNickname()
{
if(empty($this->nickname)){
return $this->name;
}
return $this->nickname;
}
/**
* 覚えている技の一覧を取得する
* @return array
*/
public function getMove()
{
return $this->move;
}
/**
* 現在のレベルを取得する
* @return integer
*/
public function getLevel()
{
return $this->level;
}
/**
* 技を覚える
* @return string
*/
public function setMove($move)
{
$this->move[] = $move;
if(count($this->move) > 4){
unset($this->move[0]);
}
}
/**
* レベルをセットする
* @return void
*/
protected function setLevel()
{
// 初期レベルからランダムで値を取得
$key = array_rand($this->default_level);
$this->level = $this->default_level[$key];
}
/**
* 初期技をセットする
* @return void
*/
protected function setDefaultMove()
{
foreach($this->level_move as list($level, $move)){
if($level <= $this->level){
// 現在レベル以下の技であれば習得
$this->setMove($move);
}else{
// 現在レベルを超えていれば処理終了
break;
}
}
}
}
ピカチュウのクラス(/Class/Pikachu.php)
<?php
require_once('Class/Pokemon.php');
// ピカチュウ
class Pikachu extends Pokemon
{
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'ピカチュウ';
/**
* 初期レベル
* @var array
*/
protected $default_level = [
7, 8, 9,
];
/**
* レベルアップで覚える技
* @var array
*/
protected $level_move = [
[1, 'でんきショック'],
[1, 'なきごえ'],
[9, 'でんじは'],
[16, 'でんこうせっか'],
[26, 'スピードスター'],
[33, 'こうそくいどう'],
[43, 'かみなり'],
];
/**
* インスタンス作成時に実行される処理
*/
public function __construct()
{
$this->setLevel();
$this->setDefaultMove();
echo 'ピカチュウをゲットした';
}
}
まとめ
いかがだったでしょうか。
今回は「ピカチュウから学ぶオブジェクト指向 〜レベルシステムの導入〜」をご紹介しました。
クラスやメソッド、プロパティの使い方や呼び出し方は、何度も回数をこなせば自然と理解が深まっていきます。興味を持ってくれた人は、ぜひポケモンのゲームにある機能を増設してみたり、他のゲームを参考に練習していくと良いでしょう。
※販売には著作権等の問題を考慮しながら適切な判断をしてください
これからPHPを学習しようと考えている方は、ぜひ参考にしてみてくださいね。
第4回はコチラ(トレイト編 + 経験値システムの導入)