プログラミング

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

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

 

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

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

ピカチュウから学ぶオブジェクト指向 〜入門編〜 1 ピカチュウから学ぶオブジェクト指向 〜入門編〜 1

オブジェクト指向とは  オブジェクト指向プログラミング https://ja.wikipedia.org/wiki/オブジェクト指向プログラミング オブジェクト指向プログラミングとは、互いに密接な関連性を持つデータとメソッドをひとつにまとめてオブジェクトとし、それぞれ異なる性質と役割を持たせたオブジェクトの様々な定...

 

 

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

 

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

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

 

 

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

 

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

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

 

それでは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,
)

 

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

【PHP】可変長引数とは「点(ドット)3つ」 多次元連想配列の検索 【PHP】可変長引数とは「点(ドット)3つ」 多次元連想配列の検索

可変長引数とは  引数の数が決まっていない、状況に応じて複数の引数を指定したいときに、関数の引数で指定する点(ドット)3つのことです。PHP5.6以降で使用することができます。  PHPマニュアル 可変長引数リスト https://www.php.net/manual/ja/functions.arguments.php#functions.variable-arg-list ...

 

上記処理で取得した$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を学習中の方や、ゲームづくりに興味がある人は、ぜひ参考にしてくださいね。

 

注目の記事

【無料】早起きをして神戸へ行こう!「為になる雑談朝活」
イベント
三宮,朝活,神戸
【無料】早起きをして神戸へ行こう!「為になる雑談朝活」

  朝活を実施することになりましたので、その目的な概要をまとめました。 神戸三宮での開催を予定しておりますので、もしお近くにお住まいの方で日時が会いましたらご参加ください。 土日祝辺りで週1日程度の不定期開催を予定しています。学びにつながる、けど参加しやすい雑談形式ということを主としています...

連続の技習得編 オブジェクトをセッションへ格納 PHPポケモン57
プログラミング
PHP,PHPポケモン,ポケモン
連続の技習得編 オブジェクトをセッションへ格納 PHPポケモン57

セッション経由でのオブジェクト引き継ぎ 技習得の処理が整ってきたので、ここで連続技習得・連続レベルアップ時にも問題なく動作するように作り込んでいきます。ですが、現状のモーダルをレスポンスやメッセージと同様に、そのまま引き継いだとしてもエラーが発生します。 その原因がセッション経由でのオブジェク...

PHPポケモン「行動順判定+敵ポケモン攻撃編」25
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「行動順判定+敵ポケモン攻撃編」25

行動順の判定 ポケモンの行動順は以下の通りです。 技の優先度 すばやさの実数値(補正有り) 同速の場合は50%の乱数   この順番で比較を行い、先行後攻を決めます。ただし、これは両者ともに攻撃を選択した場合のみです。アイテムの使用や交代は技よりも優先されますし(※一部技を除く)、にげる...

Laravel7系でTraitのmakeコマンドを作成する方法
プログラミング
artisan,Laravel,Linux,PHP,Trait
Laravel7系でTraitのmakeコマンドを作成する方法

Laravel7がリリースされて、さっそくそれを使った開発の機会がやってきましたので、使用頻度の高いものをまとめていきたいと思います。 5系や6系の情報は多く出回っていますが、6系がLTSということもあって7系の情報は少なめですね。   今回は「Laravel7系でTraitのmakeコマンドを作成する方法」をご紹介します...

PHPポケモン「レベルアップ進化編」9
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「レベルアップ進化編」9

  PHPポケモン(旧:ピカチュウから学ぶオブジェクト指向)も第9回目となりました。前回GitHubよりコードを配布したので、これから学習しようと考えている人は、ぜひ参考にしてください。   最初からコードの内容について学びたい人は、ぜひ記念すべき第1回から順を追って取り組んでみてください。   ...

独立するならWordPress理解しておけばOK!プログラミングでフリーランスはこれ一つで成り立ちます
プログラミング
PHP,WordPress,フリーランス,独立
独立するならWordPress理解しておけばOK!プログラミングでフリーランスはこれ一つで成り立ちます

  プログラミングでフリーランスを目指すには、どの言語始めればいいの?   プログラミングの学習を始めたのに、それをどう活かせばよいか分からず、いざフリーランスで活動しようと思ってもイメージできずに断念してしまう人は多いです。 言語にも向き不向きがあるため、フリーランスとして活動するために向...

【税金は貰うもの】知って得する!お金を稼ぐための実践的な3要素
マーケティング
【税金は貰うもの】知って得する!お金を稼ぐための実践的な3要素

  「お金を稼ぐための基本的な3要素」に続く、「実践的な3要素」です。 前回は、単価、頻度、客数の関係を理解すれば、お金を稼ぐ仕組みがどのように成立しているのか、そしてどういったアプローチをすればお金を稼げるのかがわかりました。   今回は、直接お金に関係する実践的な3要素です。どう...

ビジュアル作り込み編(序) PHPポケモン66
プログラミング
PHP,PHPポケモン,ポケモン
ビジュアル作り込み編(序) PHPポケモン66

ビジュアルの作り込み 最近は内部の作り込みが多かったので、今回のPHPポケモンでは見た目を少しだけ作り込んでいきます。とはいっても、機能自体がそこまで揃っていないため、あくまで仮の見た目となります。 ある程度見た目が整っていると、プレイする側のモチベーションや楽しみも増えると思ったので、こちらは並...

カテゴリ

SEO対策 イベント デザイン ネットワーク ビジネスモデル フリーランス プログラミング マーケティング ライティング 動画編集 雑記

タグ

5G Adobe AfterEffects AI ajax amazon Animate api artisan atom Automator AWS Bluetooth CSS CVR description EC-CUBE4 ECショップ ESLint Facebook feedly foreach function Google Google AdSense Honeycode htaccess HTML IEEE 802.11ax Illustrator Instagram IoT JavaScript jQuery jQuery UI keyword LAN Laravel Linux MacBook MAMP meta MLM MySQL NoCode note OS OSI参照モデル Paypal Photoshop PHP phpMyAdmin PHPポケモン PremierePro rss SEO SEO対策 Sequel Pro Skype SNS SSH Symfony TCP/IP title Toastr Trait Twig Twitter UCC V系 WAN WebSub Wi-Fi wiki Windows WordPress XAMPP xml Xserver YouTube YouTuber Zoom アーティスト アウトプット アクセス層 アニメーション アフィリエイト イーブイ インターネット インプット エンジニア オブジェクト指向 お金配り クリック単価 クリック数 コミュニケーション能力 コロナ コンサルティング サムネイル システムエンジニア スタートアップ スタイルシート スパム データベース ディープフェイク デザイナー デザイン テレワーク ナンパ ニュース ネットワークモデル ノマドワーク バナー ピカチュウ ビジネス フィード フリーランス ブロガー ブログ プログラマー プログラミング プログラミング学習 プログラミング教育 プロトコル ホームページ制作 ポケモン マークアップ マーケティング メール リモートワーク レンダリング 三井住友 三宮 仕事依頼 児童デイ 児童デイサービス 児童発達支援 公開鍵 初心者 助成金 勉強法 営業 広告 広告収入 必勝マニュアル 放課後等デイサービス 朝活 楽天 深層学習 無線LAN 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力