ピカチュウから学ぶオブジェクト指向 〜レベルアップ編〜 5
2020年9月1日 (最終更新日:2020/9/1 17:33)

ピカチュウから学ぶオブジェクト指向 〜レベルアップ編〜 5

 

第3回でレベルシステムを導入し、第4回では経験値システムの導入をしたので、今回はそれを合わせたレベルアップのシステムを導入します。

第1回から作成しているコードを使用しているので、もし最初から学習したい人は第1回の入門編をご覧ください。

 

 

レベルアップシステムの導入

 

レベルアップはゲームの中でも楽しみの一つです。人気ゲームのレベルアップ音は、それだけで高揚感を覚える、そんな一度聞いたら忘れない、もう一度その音を聞きたいと思ってしまうぐらいです。

そんなわくわくする要素を実現するには、今まで作成した機能を上手く組み合わせて行く必要があります。なので、レベルアップをした際に必要な処理をまとめながら、順番に作成してきましょう。

 

 

レベルアップに関する処理

 

現段階で作成した処理の中で、レベルアップに関係する処理は以下の通りです。

  • 経験値
    • 経験値の加算
    • 必要経験値を超えた際のレベル加算
  • レベル
    • 上限レベルの設定
    • レベルに応じた技の習得

 

それでは1つずつどういった処理が必要になるか、順番に見ていきましょう。

 

 

経験値とレベルの加算

 

レベルアップと言っても、直接レベルを上書きしたり、足すような処理をするわけではありません。レベルを上げるためには経験値が一定値以上満たされていなければならないからです。

そのため、レベルアップという処理が発生させるのは「経験値を得る」という処理になります。そのため、以下のメソッドをSet関係の格納トレイトに追加します。

 

Set関係の格納トレイト(/Trait/SetTrait.php
/**
* 経験値をセット(取得)する
* @return void
*/
public function setExPoint($ex_point)
{
    $this->ex_point += $ex_point;
    echo '<p>'.$this->getNickname().'は経験値を'.$ex_point.'ポイント手に入れた!</p>';
}

 

「経験値をゲットする」と考えると、get〜というメソッドを使いたくなってしまいますが、得た経験値を現在の経験値に加算(セットする)のがプログラムとしての役割になるため、setExPointというメソッドを作成します。

 

ポイントは、アクセス修飾子を(public)にしておくことです。経験値の取得は、現状外部(index.php)からアクセスすることになるからです。

 

次にメソッド内部の処理を見てみましょう。

$this->ex_point += $ex_point;

 

取得した経験値を、現在所有している経験値に加算して格納する必要があるので、+=という代入演算子を使用しています。 

 

ただ経験値を加算しただけではレベルに反映されません。そのため、レベルアップのトリガーとなる、次のレベルに必要な経験値との比較を行い、必要な経験値を満たしていればレベルアップをさせます。 

 

Set関係の格納トレイト(/Trait/SetTrait.php
/**
* 経験値をセット(取得)する
* @return void
*/
public function setExPoint($ex_point)
{
    $this->ex_point += $ex_point;
    echo '<p>'.$this->getNickname().'は経験値を'.$ex_point.'ポイント手に入れた!</p>';
    // 次のレベルに必要な経験値を取得
   if($this->getReqLevelUpExPoint() <= $ex_point){
       /**
       * 次のレベルに必要な経験値を超えている場合
       */
       $this->level++;
       echo '<p>'.$this->getNickName().'のレベルは'.$this->level.'になった!</p>';
   }
}

 

それでは経験値をセットして出力結果を確認してみましょう。

 

出力ファイル(/index.php
<?php
require_once('Class/Pikachu.php');
 
$pikachu = new Pikachu;
echo '<p>現在のレベル:'.$pikachu->getLevel().'</p>';
$pikachu->setExPoint(300);
$details = $pikachu->getDetails();
?>
 
<?php foreach($details as $key => $val): ?>
<p><?=$key?>:<?=$val?></p>
<?php endforeach; ?>
# 出力結果

ピカチュウをゲットした
 
現在のレベル:9
 
ピカチュウは経験値を300ポイント手に入れた!
 
ピカチュウのレベルは10になった!
 
正式名称:ピカチュウ
ニックネーム:ピカチュウ
現在のレベル:10
覚えている技:でんきショック,なきごえ,でんじは
現在の経験値:1029
レベルアップに必要な経験値:302

 

捕まえた時点でのレベルが9で、300ポイントの手に入れてレベルアップをして10になりましたね。正常に動作しているので一旦これでOKとしておきましょう。

 

 

レベル上限値の設定

 

多くのゲームではレベルに上限値が設けられています。ディ○ガイアなどのように「億?」という桁外れなものも確かに存在しますが、大抵の上限値が99や100となっています。ポケモンに関しては最大値は100のため、同じように上限値を設定しておきます。

 

Set関係の格納トレイト(/Trait/SetTrait.php
/**
* 経験値をセット(取得)する
* @return void
*/
public function setExPoint($ex_point)
{
    $this->ex_point += $ex_point;
    echo '<p>'.$this->getNickname().'は経験値を'.$ex_point.'ポイント手に入れた!</p>';
    if($this->level >= 100){
        // レベルアップ処理は不要
        $this->ex_point += $ex_point;
        return;
    }
 
    // 次のレベルに必要な経験値を取得
    if($this->getReqLevelUpExPoint() <= $ex_point){
        /**
        * 次のレベルに必要な経験値を超えている場合
        */
        $this->level++;
        echo '<p>'.$this->getNickName().'のレベルは'.$this->level.'になった!</p>';
    }
}

 

レベルが100以上であれば、経験値を加算する処理だけ実行させてreturnで処理を中断させています。「$this->level === 100」としても構いませんが、もし何かの手違いで100オーバーの数値が設定されてしまった際にレベルアップが上限なく続けられてしまうため、それを回避するためにも、今回は「>=」を使用しています。

 

さらに、次のレベルに必要な経験値の取得メソッド(getReqLevelUpExPoint)にも処理を追記しておきます。

 

Get関係の格納トレイト(/Trait/SetTrait.php
/**
* 次のレベルに必要な経験値
* @return integer
*/
public function getReqLevelUpExPoint()
{
    if($this->level >= 100){
        return 0;
    }
    return ($this->level + 1) ** 3 - $this->ex_point;
}

 

同じくレベルが100以上であればの条件分岐を追加しました。100になれば次のレベルまでの経験値を計算する必要がないので、そのまま0という数値を返却しています。

これで、レベルの上限値の処理についてはOKとしておきましょう。

 

 

技の習得

 

ポケモンのゲームでは、レベルが上がれば、それに応じて技を覚えていきます。今回のピカチュウの場合であれば、8で捕まえたピカチュウが9レベルに上がった際には、新しく「でんじは」を覚えてもらう必要があります。

なので、レベルが上がればそのレベルに応じた技を検索して習得するという処理を追加しましょう。

 

技を習得する処理は、記述量が少し多くなるので新しく「actionLevelUp」というメソッドを作成します。これはsetgetどちらにも該当しないため、ポケモンのクラスに記述しましょう。

 

ポケモンのクラス(/Class/Pokemon.php
/**
* レベルアップ処理
* @var integer
*/
protected function actionLevelUp()
{
    // レベルアップ
    $this->level++;
    echo '<p>'.$this->getNickName().'のレベルは'.$this->level.'になった!</p>';
 
    // レベルアップして覚えられる技があれば習得する
    $level_move_keys = array_keys(array_column($this->level_move, 0), $this->level);
    foreach($level_move_keys as $key){
        $move_name = $this->level_move[$key][1];
        $this->setMove($move_name);
        echo '<p>'.$move_name.'を覚えた!</p>';
    }
}

 

Set関係の格納トレイト(/Trait/SetTrait.php
/**
* 経験値をセット(取得)する
* @return void
*/
public function setExPoint($ex_point)
{
    $this->ex_point += $ex_point;
    echo '<p>'.$this->getNickname().'は経験値を'.$ex_point.'ポイント手に入れた!</p>';
    if($this->level >= 100){
        // レベルアップ処理は不要
        $this->ex_point += $ex_point;
        return;
    }
 
    // 次のレベルに必要な経験値を取得
    if($this->getReqLevelUpExPoint() <= $ex_point){
        /**
        * 次のレベルに必要な経験値を超えている場合
        */
        $this->actionLevelUp();
    }
}

 

setExPointのメソッド内に記述していたレベルアップ処理を、新しく作成したactionLevelUpというメソッドへ移動して、レベルアップ時にはactionLevelUpを呼び出すようにしました。

 

それでは新しく追加したレベルアップ用のメソッド(actionLevelUp)の中身を見ていきましょう。

// レベルアップ
$this->level++;
echo '<p>'.$this->getNickName().'のレベルは'.$this->level.'になった!</p>';

 

最初の処理は先程と変わっていませんね。レベルを加算して、レベルアップした旨をメッセージで出力しています。

次に、アップしたレベルに該当する技が存在しているかを確認します。

// レベルアップして覚えられる技があれば習得する
$level_move_keys = array_keys(array_column($this->level_move, 0), $this->level);

 

array_keysでは、第1引数で設定された配列から、第2引数に設定された値を検索して、そのキーを配列として返却します。わかりやすく表現したコードが以下の通りです。

<?php

$pokemon = [
    0 => 'ピカチュウ',
    1 => 'ゼニガメ',
    2 => 'ヒトカゲ',
    3 => 'ピカチュウ',
];

var_export( array_keys($pokemon, 'ピカチュウ') ); 
# 出力結果

array (
  0 => 0,
  1 => 3,
) 

 

ピカチュウが格納されているキーのが返ってきていますね。

※フシギダネが嫌いなわけではありません、たまたまです

 

今回、array_keysの第1引数(検索する配列)には以下を使用しています。

array_column($this->level_move, 0)

 

array_columnでは、多次元配列から指定されたキーだけを抜き取り、配列にして取得しています。今回使用した$this->level_moveであれば、0番目には習得レベルが入っているため、習得レベルだけを配列として取り出しています。わかりやすく、コードをばらして確認してみましょう。

<?php

// 現在のレベル
$level = 9;
 
// 習得技一覧
$level_move = [
    [1, 'でんきショック'],
    [1, 'なきごえ'],
    [9, 'でんじは'],
    [16, 'でんこうせっか'],
    [26, 'スピードスター'],
    [33, 'こうそくいどう'],
    [43, 'かみなり'],
];
 
// 習得技一覧からレベル部分だけを取得
$level_list = array_column($level_move, 0);
// $level_moveのキー(添字)と$level_listのキー(添字)は一致している
var_export($level_list); #1
 
// レベルリストから、現在のレベルに該当する技情報が格納されているキー(添字)を取得
$keys = array_keys($level_list, $level);
// $level_moveのレベル9で格納されている場所が配列で取得されている
var_export($keys); #2
# 1部分の出力結果
// 習得技一覧からレベル部分だけを取得
array (
  0 => 1,
  1 => 1,
  2 => 9,
  3 => 16,
  4 => 26,
  5 => 33,
  6 => 43,
)
 
# 2部分の出力結果
// レベルリストから、現在のレベルに該当する技情報が格納されているキー(添字)を取得
array (
  0 => 2,
)

 

もし難しい場合は、過去記事で説明をしているのでそちらを参考にしてください。

 

上記処理で取得した$level_move_keysには、$this->level_moveの該当キーが配列として格納されています(該当するレベルがなければ空の配列が返ってきます)。同レベルで覚えられる技があれば複数キー返ってくるため、foreachを使って1つずつ処理します。

foreach($level_move_keys as $key){
    $move_name = $this->level_move[$key][1];
    $this->setMove($move_name);
    echo '<p>'.$move_name.'を覚えた!</p>';
}

 

取得した値($key)を$this->level_moveに当てはめ、1をセットして技名の部分を取得します。あとは以前作成したsetMoveのメソッドを使用して、覚えている技の一覧にセットしています。

 

Set格納用のトレイト(/Trait/SetTrait.php
/**
* 技を覚える
* @return string
*/
protected function setMove($move)
{
    $this->move[] = $move;
    if(count($this->move) > 4){
        unset($this->move[0]);
        // 技の添字を採番する
        $this->move = array_values($this->move);
    }
}

※覚えられる技リストを削除したため、setMoveのアクセス修飾子をprotectedへ変更しました

 

unsetのあとに添字の採番処理を追加しています。もし連続して技を覚えた場合、採番されていなければ$this->move[0]が見つからず、4つ以上の技を覚えられてしまうからです。

 

それでは、出力結果を確認してみましょう。

 

出力ファイル(/index.php
<?php

require_once('Class/Pikachu.php');
 
$pikachu = new Pikachu;
echo '<p>現在のレベル:'.$pikachu->getLevel().'</p>';
$pikachu->setExPoint(300);
$details = $pikachu->getDetails();
?>
 
<?php foreach($details as $key => $val): ?>
<p><?=$key?>:<?=$val?></p>
<?php endforeach; ?> 
# 出力結果
 
ピカチュウをゲットした
現在のレベル:8
 
ピカチュウは経験値を300ポイント手に入れた!
 
ピカチュウのレベルは9になった!
でんじはを覚えた!
 
正式名称:ピカチュウ
ニックネーム:ピカチュウ
現在のレベル:9
覚えている技:でんきショック,なきごえ,でんじは
現在の経験値:812
レベルアップに必要な経験値:188

 

レベル8のピカチュウをゲットして、レベルアップに必要な経験値がセットされていれば、「でんじは」を覚えてくれましたね。

これでレベルアップによる技の習得処理が完成です。

 

 

経験値計算のための3つの方法

 

順番に必要な処理を作成していきましたが、現段階では大きな欠陥があります。それは、現在の仕組みでは1レベルのアップにしか対応が出来ていないという点です。

試しに、大量の経験値をセットしてみましょう。

 

出力ファイル(/index.php
<?php

require_once('Class/Pikachu.php');
 
$pikachu = new Pikachu;
echo '<p>現在のレベル:'.$pikachu->getLevel().'</p>';
$pikachu->setExPoint(1000000);
$details = $pikachu->getDetails();
?>
 
<?php foreach($details as $key => $val): ?>
<p><?=$key?>:<?=$val?></p>
<?php endforeach; ?>
# 出力結果
 
ピカチュウをゲットした
現在のレベル:9
 
ピカチュウは経験値を1000000ポイント手に入れた!
 
ピカチュウのレベルは10になった!
 
正式名称:ピカチュウ
ニックネーム:ピカチュウ
現在のレベル:10
覚えている技:でんきショック,なきごえ,でんじは
現在の経験値:1000729
レベルアップに必要な経験値:-999398

 

レベル100になるために必要な100万の経験値をセットしましたが、レベルは1しか上がらず10のままです。もちろん技も覚えていません。そして経験値は次のレベルの11に十分足りているため、数値がマイナスとなっています

 

このようにならないためには、レベルアップ後に次のレベルまでの経験値が満たされていれば再度レベルアップをするという処理を入れなければなりません。

そのためには、3つの計算方法があります。

立方根の活用

必要経験値数を段階的に加算

getReqLevelUpExPointを利用

 

  

立方根の活用

 

取得した経験値から逆算してレベルを求めるという方法が1つです。それには立方根の計算を使用します。

先程のように100万の経験値を得た場合、ピカチュウの取得した経験値は100万512になります。そこから、3乗を逆算(立方根)して小数点をカットすれば該当レベルが導きだせます。100を超過することは無いため、もし算出された値が100以上であれば100までで制限を設けてレベルアップ処理をすれば問題ありません。

レベル = floor3√合計経験値)

※もし100以上なら100で計算する

 

あとは、算出したレベルから現在のレベルを引いて、その回数分レベルアップ処理をするだけです。

 

しかし、これには大きな問題がありますPHPでは立方根を求める関数が標準で準備されていないという点です。 

 

GMP関数というものはありますが、これを利用するためにはライブラリのインストールが必要になります。もし本格的に開発する場合は便利な関数が準備されているかも知れませんが、今回の用に学習目的には向いていません。

もし試して見たい人は、ぜひインストールをして使ってみてください。

  

 

必要経験値数を段階的に加算

 

2つ目は、経験値を一気に足して計算するのではなく、段階的に足しながらレベルアップの判定をする方法です。コードの例を見てみましょう。

 

Set関係の格納トレイト(/Trait/SetTrait.php
/**
* 経験値をセット(取得)する
* @param integer $ex_point
* @param boolean $ex_message
* @return void
*/
public function setExPoint($ex_point, $ex_message=true)
{
    if($ex_message){
        echo '<p>'.$this->getNickname().'は経験値を'.$ex_point.'ポイント手に入れた!</p>';
    }
   
    if($this->level >= 100){
        // レベルアップ処理は不要
        $this->ex_point += $ex_point;
        return;
    }
   
     // 次のレベルに必要な経験値を取得
    $next_ex = $this->getReqLevelUpExPoint();
    if($next_ex <= $ex_point){
        /**
        * 次のレベルに必要な経験値を超えている場合
        */
        // レベルアップに必要な経験値をセット
        $this->ex_point += $next_ex;
   
        // 経験値の超過分を計算
        $residue_ex = $ex_point - $next_ex;
        $this->actionLevelUp();
   
        // 再度このメソッドを、残った経験値で実行
        $this->setExPoint($residue_ex, false);
   
    }else{
        /**
        * 次のレベルに必要な経験値を超えていない場合
        */
        // 経験値をセット
        $this->ex_point += $ex_point;
   
    }
}

 

引数が新たに追加されている点は後ほど説明します。まず変更箇所として、最初の行で経験値を加算していた処理を削除しています。

 

それでは、大きく記述が追加された「次のレベルに必要な経験値を取得」とコメントしている部分を見ていきましょう。

$next_ex = $this->getReqLevelUpExPoint();
if($next_ex <= $ex_point){
    /**
    * 次のレベルに必要な経験値を超えている場合
    */
    // レベルアップに必要な経験値をセット
    $this->ex_point += $next_ex;
   
    // 経験値の超過分を計算
    $residue_ex = $ex_point - $next_ex;
    $this->actionLevelUp();
   
    // 再度このメソッドを、残った経験値で実行
    $this->setExPoint($residue_ex, false);
   
}else{
    /**
    * 次のレベルに必要な経験値を超えていない場合
    */
    // 経験値をセット
    $this->ex_point += $ex_point;
   
}

 

まず、次のレベルに必要な経験値を再利用するため、$next_exという変数へ格納しています。レベルアップに必要な経験値を超えていた場合は、取得した経験値をそのまま加算せずに、$next_exを加算しています。そして新しく追加した$residue_exには、取得した経験値から加算した分を引いた分を格納します。

レベルアップのメソッド(actionLevelUp)を実行したら、再度自らのメソッドを$residue_exを引数として呼び出してループさせています。もしレベルアップに必要な経験値が足りなくなれば、else内の処理に移行して、残経験値を加算して処理を終えます。

/**
* 次のレベルに必要な経験値を超えていない場合
*/
// 経験値をセット
$this->ex_point += $ex_point;

 

メソッドの再コール(ループ)した際の挙動として、最初に戻り、追加された引数を見てみましょう。

public function setExPoint($ex_point, $ex_message=true)
{
    if($ex_message){
        echo '<p>'.$this->getNickname().'は経験値を'.$ex_point.'ポイント手に入れた!</p>';
    }

 

第2引数ではメッセージの有無を判定しています。「経験値を手に入れた!」というメッセージは1度表示されれば十分です。しかし、メソッドをループさせている以上はその有無を判定しなければなりません。第2引数を判定して、メッセージの出力を条件分岐していれば、余計なメッセージは出力されなくて済みますね。

 

では出力結果を確認してみましょう。

 

出力ファイル(/index.php
<?php

require_once('Class/Pikachu.php');
 
$pikachu = new Pikachu;
echo '<p>現在のレベル:'.$pikachu->getLevel().'</p>';
$pikachu->setExPoint(5000);
$details = $pikachu->getDetails();
   
?>
 
<?php foreach($details as $key => $val): ?>
<p><?=$key?>:<?=$val?></p>
<?php endforeach; ?>
# 出力結果
 
ピカチュウをゲットした
現在のレベル:9
 
ピカチュウは経験値を5000ポイント手に入れた!
 
ピカチュウのレベルは10になった!
 
ピカチュウのレベルは11になった!
 
ピカチュウのレベルは12になった!
 
ピカチュウのレベルは13になった!
 
ピカチュウのレベルは14になった!
 
ピカチュウのレベルは15になった!
 
ピカチュウのレベルは16になった!
でんこうせっかを覚えた!
 
ピカチュウのレベルは17になった!
 
正式名称:ピカチュウ
ニックネーム:ピカチュウ
現在のレベル:17
覚えている技:でんきショック,なきごえ,でんじは,でんこうせっか
現在の経験値:5729
レベルアップに必要な経験値:103

 

レベル16になったタイミングで「でんこうせっか」を覚えてくれました。これで大量の経験値を得た場合でも問題なくレベルアップして技を習得してくれます。

 

 

 

getReqLevelUpExPointを利用

 

最後は、getReqLevelUpExPointのメソッドを利用する方法です。最初の出力で分かったように、次のレベルに必要な経験値が超過していれば、getReqLevelUpExPoint(次のレベルに必要な経験値)はマイナスになります。これを利用して、whileを使ってループを組めば、先程の用に段階的に加算してくよりも記述量を少なくすることができます。

 

Set格納用のトレイト(/Trait/SetTrait.php
/**
* 経験値をセット(取得)する
* @param integer $ex_point
* @return void
*/
public function setExPoint($ex_point)
{
    $this->ex_point += $ex_point;
    echo '<p>'.$this->getNickname().'は経験値を'.$ex_point.'ポイント手に入れた!</p>';
    if($this->level >= 100){
        // レベルアップ処理は不要
        $this->ex_point += $ex_point;
        return;
    }
 
    // 次のレベルに必要な経験値を取得
    if($this->getReqLevelUpExPoint() <= $ex_point){
        /**
        * 次のレベルに必要な経験値を超えている場合
        */
        $this->actionLevelUp();
        while($this->getReqLevelUpExPoint() < 0){
            $this->actionLevelUp();
        }
    }
 
}

 

まず、当初の予定通り最初に経験値をすべて加算して、レベルアップ処理を行います。追加されたレベルアップ処理の次を確認してみましょう。

while($this->getReqLevelUpExPoint() < 0){
    $this->actionLevelUp();
}

 

whileで条件に一致すればループをさせています。ループさせる処理はレベルアップのメソッド(actionLevelUp)です。

ループ条件は、getReqLevelUpExPoint(次のレベルに必要な経験値)が0より小さいかどうかです。もし経験値が超えていれば、マイナスが返ってくるため、ループでレベルアップの処理を実行します。正の数が返ってくれば、その時点で処理が終了します。100になった場合は0が返ってくるため、同じく処理が終了します。

では、出力結果をみてみましょう。

 

出力ファイル(/index.php
<?php
require_once('Class/Pikachu.php');
 
$pikachu = new Pikachu;
echo '<p>現在のレベル:'.$pikachu->getLevel().'</p>';
$pikachu->setExPoint(50000);
$details = $pikachu->getDetails();

?>
 
<?php foreach($details as $key => $val): ?>
<p><?=$key?>:<?=$val?></p>
<?php endforeach; ?>
# 出力結果

ピカチュウをゲットした
現在のレベル:7
 
ピカチュウは経験値を50000ポイント手に入れた!
 
ピカチュウのレベルは8になった!
 
ピカチュウのレベルは9になった!
でんじはを覚えた!
 
ピカチュウのレベルは10になった!
 
ピカチュウのレベルは11になった!
 
ピカチュウのレベルは12になった!
 
ピカチュウのレベルは13になった!
 
ピカチュウのレベルは14になった!
 
ピカチュウのレベルは15になった!
 
ピカチュウのレベルは16になった!
でんこうせっかを覚えた!
 
ピカチュウのレベルは17になった!
 
ピカチュウのレベルは18になった!
 
ピカチュウのレベルは19になった!
 
ピカチュウのレベルは20になった!
 
ピカチュウのレベルは21になった!
 
ピカチュウのレベルは22になった!
 
ピカチュウのレベルは23になった!
 
ピカチュウのレベルは24になった!
 
ピカチュウのレベルは25になった!
 
ピカチュウのレベルは26になった!
スピードスターを覚えた!
 
ピカチュウのレベルは27になった!
 
ピカチュウのレベルは28になった!
 
ピカチュウのレベルは29になった!
 
ピカチュウのレベルは30になった!
 
ピカチュウのレベルは31になった!
 
ピカチュウのレベルは32になった!
 
ピカチュウのレベルは33になった!
こうそくいどうを覚えた!
 
ピカチュウのレベルは34になった!
 
ピカチュウのレベルは35になった!
 
ピカチュウのレベルは36になった!
 
正式名称:ピカチュウ
ニックネーム:ピカチュウ
現在のレベル:36
覚えている技:でんじは,でんこうせっか,スピードスター,こうそくいどう
現在の経験値:50343
レベルアップに必要な経験値:310

 

先程と同じように期待した結果を得ることができましたね。このように、同じような結果であったとしても、記述量や計算量を少なくする方法があります。どちらの方が良いかは、実際に試しながら、またはどういった処理がされているのかを踏まえて判断するようにしましょう。

 

 

第5回終了時点での最終コード

 

以下、現在の最終コードです(index.phpを除く)

※一部見直しをして修正している箇所や、他との関係も考えて削除した機能もあります

 

/Class/Pokemon.php
<?php
 
require_once('Trait/SetTrait.php');
require_once('Trait/GetTrait.php');
 
// ポケモン
abstract class Pokemon
{
    use SetTrait;
    use GetTrait;
 
    /**
    * ニックネーム
    * @return string(min:1 max:5)
    */
    protected $nickname;
 
    /**
    * 現在のレベル
    * @var integer(min:2 max:100)
    */
    protected $level;
 
    /**
    * 覚えている技
    * @var array
    */
    protected $move = [];
 
    /**
    * 経験値
    * @var integer
    */
    protected $ex_point;
 
    /**
    * レベルアップ処理
    * @var integer
    */
    protected function actionLevelUp()
    {
        // レベルアップ
        $this->level++;
        echo '<p>'.$this->getNickName().'のレベルは'.$this->level.'になった!</p>';
 
        // レベルアップして覚えられる技があれば習得する
        $level_move_keys = array_keys(array_column($this->level_move, 0), $this->level);
        foreach($level_move_keys as $key){
            $move_name = $this->level_move[$key][1];
            $this->setMove($move_name);
            echo '<p>'.$move_name.'を覚えた!</p>';
        }
 
    }
 
}

 

/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->setDefaultExPoint();
        $this->setDefaultMove();
        echo '<p>ピカチュウをゲットした</p>';
    }
 
}

 

/Trait/GetTrait.php
<?php
trait GetTrait
{
 
    /**
    * 詳細を取得する
    * @return integer
    */
    public function getDetails()
    {
        return [
            '正式名称' => $this->getName(),
            'ニックネーム' => $this->getNickName(),
            '現在のレベル' => $this->getLevel(),
            '覚えている技' => implode(',', $this->getMove()),
            '現在の経験値' => $this->getExPoint(),
            'レベルアップに必要な経験値' => $this->getReqLevelUpExPoint(),
        ];
    }
 
    /**
    * 正式名称を取得する
    * @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 integer
    */
    public function getExPoint()
    {
        return $this->ex_point;
    }
 
    /**
    * 次のレベルに必要な経験値
    * @return integer
    */
    public function getReqLevelUpExPoint()
    {
        if($this->level >= 100){
            return 0;
        }
        return ($this->level + 1) ** 3 - $this->ex_point;
    }
 
}

 

/Trait/SetTrait.php
<?php
trait SetTrait
{
 
    /**
    * ニックネームを付ける
    * @return string
    */
    public function setNickname($nickname)
    {
        if(empty($nickname) || mb_strlen($nickname, 'UTF-8') > 5){
            echo '<p>ニックネームは1〜5文字で入力してください</p>';
            return;
        }
        $this->nickname = $nickname;
    }

    /**
    * レベルをセットする
    * @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;
            }
        }
    }
 
    /**
    * 技を覚える
    * @return string
    */
    protected function setMove($move)
    {
        $this->move[] = $move;
        if(count($this->move) > 4){
            unset($this->move[0]);
            // 技の添字を採番する
            $this->move = array_values($this->move);
        }
    }
 
    /**
    * 初期経験値をセットする
    * @return void
    */
    protected function setDefaultExPoint()
    {
        $this->ex_point = $this->level ** 3;
    }
 
    /**
    * 経験値をセット(取得)する
    * @param integer $ex_point
    * @return void
    */
    public function setExPoint($ex_point)
    {
        $this->ex_point += $ex_point;
        echo '<p>'.$this->getNickname().'は経験値を'.$ex_point.'ポイント手に入れた!</p>';
        if($this->level >= 100){
            // レベルアップ処理は不要
            $this->ex_point += $ex_point;
            return;
        }
 
        // 次のレベルに必要な経験値を取得
        if($this->getReqLevelUpExPoint() <= $ex_point){
            /**
            * 次のレベルに必要な経験値を超えている場合
            */
            $this->actionLevelUp();
            while($this->getReqLevelUpExPoint() < 0){
                $this->actionLevelUp();
            }
        }
 
    }
 
}

 

 

まとめ

 

いかがだったでしょうか。

今回は「ピカチュウから学ぶオブジェクト指向 〜レベルアップ編〜」をご紹介しました。

余談ですが、初代のポケモンでは一気にレベルアップした場合はその間に覚えるはずの技をスキップしてしまいます。なので、今回はそれも考慮した内容に仕上げました。

 

PHPを学習中の方や、ゲームづくりに興味がある人は、ぜひ参考にしてくださいね。

 

Copyright © 2016-2020 YQUAL All Rights Reserved.