プログラミング

ピカチュウから学ぶオブジェクト指向 〜トレイト編〜 4

PHP PHPポケモン オブジェクト指向 ポケモン
ピカチュウから学ぶオブジェクト指向 〜トレイト編〜 4

 

ピカチュウから学ぶオブジェクト指向の第4弾は「トレイト(trait)の活用」についてです。更に、レベルシステムを導入すれば欠かせない経験値システムも合わせて実装します。

第3回からの続きとなりますので、もし前回をまだ見ていない人は是非ご参考ください。

ピカチュウから学ぶオブジェクト指向 〜レベルシステム導入編〜 3 ピカチュウから学ぶオブジェクト指向 〜レベルシステム導入編〜 3

  大人気?シリーズ「ピカチュウから学ぶオブジェクト指向」、今回はレベルシステムの導入編です。   第1回(基礎編)、第2回(クラス継承編)で作成したPokemonとPikachuのクラスを使用するので、最初から学習したい人はぜひご参考ください。 [shortcode_url_og url="https://s-yqual.com/blog/1035"...

 

それでは今回もピカチュウと一緒に、楽しくプログラミング、オブジェクト指向を学習しましょう。

 

 

トレイト(trait)とは

 

 

トレイトは、PHP のような単一継承言語でコードを再利用するための仕組みのひとつです。 トレイトは、単一継承の制約を減らすために作られたもので、 いくつかのメソッド群を異なるクラス階層にある独立したクラスで再利用できるようにします。 トレイトとクラスを組み合わせた構文は複雑さを軽減させてくれ、 多重継承や Mixin に関連するありがちな問題を回避することもできます。

 

トレイトはクラスと似ていますが、トレイトは単にいくつかの機能をまとめるためだけのものです。 トレイト自身のインスタンスを作成することはできません。 昔ながらの継承に機能を加えて、振る舞いを水平方向で構成できるようになります。 つまり、継承しなくてもクラスのメンバーに追加できるようになります。

 

 

だそうです。簡単にまとめるなら

継承しなくても使えるよ!インスタンスも作れないよ!なクラス

 といったところでしょう。

汎用性のあるメソッドをまとめておくイメージです。今回作っているようなポケモンのクラスでも使え、アイテムというクラスでも使えるようなメソッドも場合によっては必要になります。そういったものを毎回書かなくても良いようにまとめて置くのがトレイトの本来の使い方です。

 

 

トレイトの活用

 

前回(第3回)でファイルを分割しましたが、それでも記述量は多くなってきました。特にポケモンクラス内のメソッドが回数を重ねるごとに増えていきます。そこで、今回はトレイト(trait)を使って管理をします。

 

現在ポケモンクラス内にあるメソッドでは「get〜」という取得用のメソッドと、「set〜」という値を格納するようのメソッドで構成されています。なので、get関係set関係をそれぞれまとめて置くように「SetTrait」と「GetTrait」を作成しましょう。作成箇所は以下の通りです。

/Trait/GetTrait.php
/Trait/SetTrait.php

 

それではそれぞれのファイル内を見ていきましょう。

 

Get関係の格納トレイト(/Trait/GetTrait.php
<?php
trait GetTrait
{
 
    /**
    * 正式名称を取得する
    * @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;
    }
 
}

 

Set関係の格納トレイト(/Trait/SetTrait.php
<?php
trait SetTrait
{
    /**
    * ニックネームを付ける
    * @return string
    */
    public function setNickname($nickname)
    {
        if(empty($nickname) || mb_strlen($nickname, 'UTF-8') > 5){
            echo 'ニックネームは1〜5文字で入力してください';
            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
    */
    public function setMove($move)
    {
        $this->move[] = $move;
        if(count($this->move) > 4){
            unset($this->move[0]);
        }
    }
 
}

 

それぞれgetsetのメソッドを移動してきました。それではトレイトの宣言部分について確認しましょう。

trait GetTrait

 

クラスとの大きな違いは最初の行にあります。クラスの場合はclassと宣言してからクラス名をつけますが、トレイトの場合はtraitと宣言してトレイト名をつけます。その他の記述方法はほとんど同じですが、宣言が違うということを覚えておきましょう。そして最初の説明でも述べたように、トレイトはインスタンス化(実体化)することができません

 

 

トレイトの読み込み

 

次に、クラスに対してトレイトを読み込む方法を見ていきましょう。今回はどちらもポケモンクラス内で使用していたメソッドなので、ポケモンクラス上で作成した2つのトレイトを読み込みます。

 

ポケモンのクラス(/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 = [];
 
}

 

require_onceで使用するトレイトファイルを読み込みクラス内でトレイトの使用を宣言しています。

use SetTrait;
use GetTrait;

 

注意点は、useをクラス内で使用するということです。現在はまだ紹介していませんが、名前空間やオートローダーを使用するとクラスの前にuseをつけて必要ファイルを読み込みます。それに対して、トレイトの使用宣言はクラス内で行います。

 

これでポケモンのクラスで各トレイトに割り当てたメソッドが使用できるようになりました。ポケモンのクラスで使用できるということは、それを継承しているピカチュウのクラスでも呼び出すことができます。出力用ファイル(index.php)に記述して確認してみましょう。

 

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

require_once('Class/Pikachu.php');

$pikachu = new Pikachu;
$pikachu->setNickname('ピカイチ');

?>

<p>ニックネーム:<?=$pikachu->getNickname()?></p>

 

出力結果は以下の通りです。

# 出力結果
 
ピカチュウをゲットした
 
ニックネーム:ピカイチ

 

 

経験値システムの導入

 

レベルシステムにとって欠かせないのが経験値システムです。その初期準備として、初期経験値の計算と次のレベルアップに必要な経験値の算出を実装します。

そのために、まずポケモンの経験値の仕組みを見てみましょう。 

 

今回はリンク先の「経験レベルと必要経験値の換算表」という部分を参考にします。ポケモンによって異なるようですが、今回は最も単純なピカチュウの経験値タイプでもある「100万型」を実装します。

 

100万型経験値の計算方法は以下の通りです。

必要経験値 = Lv^3

 

もう少しわかりやすく表現すると

必要経験値 = レベル x レベル x レベル

 

上記の式が成り立ちます。PHP(5.6以降)であれば**演算子が使えるので、今回作成しているポケモンシステムであれば、以下の式で現在のレベルに必要な経験値を算出します。

$this->level ** 3

 

 

デフォルト経験値

 

ポケモンは捕まえた時点で、そのレベルに応じた経験値を所有していなければなりません。そのために、経験値を格納しておくプロパティ、デフォルト経験値の算出用メソッド、現在の経験値を取得するメソッドの3つを用意します。

 

  1. 経験値を格納するプロパティ
    ポケモンのクラス(/Class/Pokemon.php)
  2. デフォルトの経験値を算出するメソッド
    Set格納トレイト(/Trait/SetTrait.php) 
  3. 現在の経験値を取得するメソッド
    Get格納トレイト(/Trait/GetTrait.php)

 

ポケモンのクラス(/Class/Pokemon.php
/**
* 経験値
* @var integer
*/
protected $ex_point;

 

Set格納トレイト(/Trait/SetTrait.php
/**
* 初期経験値をセットする
* @return void
*/
protected function setDefaultExPoint()
{
    $this->ex_point = $this->level ** 3;
}

 

Get格納トレイト(/Trait/GetTrait.php
/**
* 現在の経験値を取得する
* @return integer
*/
public function getExPoint()
{
    return $this->ex_point;
}

 

setDefaultExPointのメソッドは、ポケモンを捕まえた時点で実行される必要がありますので、ピカチュウのクラスのコンストラクタで実行しましょう。

 

ピカチュウのクラス(/Class/Pikachu.php
/**
* インスタンス作成時に実行される処理
*/
public function __construct()
{
    $this->setLevel();
    $this->setDefaultExPoint();
    $this->setDefaultMove();
    echo 'ピカチュウをゲットした';
}

 

setDefaultExPoint内でレベルのプロパティ($this->level)が使用されています。なので実行するタイミングはsetLevelのあとでなければなりません

 

では、ピカチュウのインスタンスを作成して、経験値を実行ファイルで確認してみましょう。

 

実行ファイル(/index.php
# 出力結果(1回目)
 
ピカチュウをゲットした
 
現在のレベル:8
現在の経験値:512
# 出力結果(2回目)
 
ピカチュウをゲットした
 
現在のレベル:9
現在の経験値:729

 

換算表で確認してみると、レベル8では512レベル9では729となっているため正常に算出出来ていることがわかります。 

 

これで、初期経験値の設定は完了です。

 

 

次のレベルまで

 

ポケモンの経験値で気になるのは、現在の値よりも次のレベルまでに必要な値です。なので、次のレベルまでの値を算出するメソッドをGet格納トレイトに追加します。

 

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

 

次のレベルまでに必要な経験値の算出方法は以下の通りです。

次のレベルまでに必要な経験値

= 次のレベルで必要な経験値 現在の経験値

 

現在の経験値は$this->ex_pointに格納されています。次のレベルで必要な経験値は以下の式で算出できます。

次のレベルで必要な経験値

= (現在のレベル + 1)の3乗

 

1列にまとめているので複雑な式に見えてしまうかも知れませんが、やっていることは至って単純です。算出式が、以下の通りです。

($this->level + 1) ** 3 - $this->ex_point

 

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

 

実行ファイル(/index.php
<?php

require_once('Class/Pikachu.php');

$pikachu = new Pikachu;

?>

<p>現在のレベル:<?=$pikachu->getLevel()?></p>
<p>現在の経験値:<?=$pikachu->getExPoint()?></p>
<p>次のレベルまでに必要な経験値:<?=$pikachu->getReqLevelUpExPoint()?></p>
# 出力結果
 
ピカチュウをゲットした
 
現在のレベル:7
現在の経験値:343
次のレベルまでに必要な経験値:169

 

現在の経験値(343)と次のレベルまでに必要な経験値(169)を足すと、レベル8で必要な経験値(512)になるので正常に出力されていることがわかります。

 

これで、次のレベルまでに必要な経験値の出力設定が完了です。

 

 

詳細の取得

 

機能が増えてくれば、一つずつ出力するのは一苦労です。なので、一気に詳細を出力できるようなメソッドを作成します。Get格納トレイトに以下を追加しましょう。

 

Get格納トレイト(/Trait/GetTrait.php
/**
* 詳細を取得する
* @return integer
*/
public function getDetails()
{
    return [
        '正式名称' => $this->getName(),
        'ニックネーム' => $this->getNickName(),
        '現在のレベル' => $this->getLevel(),
        '覚えている技' => implode(',', $this->getMove()),
        '現在の経験値' => $this->getExPoint(),
        'レベルアップに必要な経験値' => $this->getReqLevelUpExPoint(),
    ];
}

 ※配列キーに日本語が用いられることはほとんど有りませんが、今回はわかりやすさ重視のために日本語を使用しています。気になる人は値として格納してlistで取得するなどしてください

 

それでは返り値として準備された配列を見てみましょう。

return [
    '正式名称' => $this->getName(),
    'ニックネーム' => $this->getNickName(),
    '現在のレベル' => $this->getLevel(),
    '覚えている技' => implode(',', $this->getMove()),
    '現在の経験値' => $this->getExPoint(),
    'レベルアップに必要な経験値' => $this->getReqLevelUpExPoint(),
];

 

返却する内容は至って単純です。それぞれの取得用メソッドに見出しとなるキーを割り振っています。「覚えている技」は返り値が配列のため、そのまま文字列として出力できるようにimplodeを使用しています。 

 

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

 

実行ファイル(/index.php
<?php

require_once('Class/Pikachu.php');
 
$pikachu = new Pikachu;
$pikachu->setNickname('ピカイチ');
$details = $pikachu->getDetails();

?>
 
<?php foreach($details as $key => $val): ?>
<p><?=$key?>:<?=$val?></p>
<?php endforeach; ?>

 

ピカチュウをインスタンス化(実体化)させて、「ピカイチ」というニックネームをつけました。

詳細の取得メソッド(getDetails)は配列で返ってくるため、$detailsという変数に格納してforeachで見出し($key)と一緒に取り出しています。

 

出力結果は以下の通りです。

# 出力結果
 
ピカチュウをゲットした
 
正式名称:ピカチュウ
ニックネーム:ピカイチ
現在のレベル:8
覚えている技:でんきショック,なきごえ
現在の経験値:512
レベルアップに必要な経験値:217

 

正常に取得できていますね。

一括取得のメソッドがあれば、すぐにインスタンスの中身が確認ができるため便利です。

 

 

まとめ

 

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

今回は「【PHP】ピカチュウから学ぶオブジェクト指向 〜トレイト編 & 経験値システム導入〜」をご紹介しました。

 

トレイト(trait)の使い方はプロジェクトによっても様々ですが、今回紹介したようにファイル内のメソッドを分割して管理するために用いることも可能です。継承する必要が無いため、複数のクラスで使いまわしするメソッド格納用として使えれば、より保守性も高まり管理手間が少なくなります。

※今回はあくまでトレイトの使い方の説明用として用いているので、それぞれのプロジェクトに合わせた使い方をしてください

 

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

 

注目の記事

進化直後の技習得編 PHPポケモン 65
プログラミング
PHP,PHPポケモン,ポケモン
進化直後の技習得編 PHPポケモン 65

進化直後の技習得 パーティー機能を導入に合わせて、至る場所を修正することになり、合わせて未実装だった機能を導入していきます。 見た目にはわからない部分や、とある条件が重ならなければ起こらない部分の作り込みが多いため、プレイユーザー目線からすると少し面白みが無いかも知れません。 ですが、そういっ...

バーアニメーションの不具合対応編 PHPポケモン 54
プログラミング
PHP,PHPポケモン,ポケモン
バーアニメーションの不具合対応編 PHPポケモン 54

最近は細々した対応が多くて「早く次のステップに進めよ!」と思っている方も多いでしょう。 ご意見ごもっともですが、残っている対応が意外にも追加しなければいけない処理が多く苦戦しているのが実情だったりします。そして、そのおかげか試行回数が多くなり見過ごしていた不具合や修正不備がポロポロ出てきていま...

データ軽量化編 β版へ向けて  PHPポケモン 90
プログラミング
PHP,PHPポケモン,ポケモン
データ軽量化編 β版へ向けて PHPポケモン 90

β版の実装に向けて 大型アップデートにより、ある程度機能改善や実装箇所も増えてきましたが、それと同時に次の段階への移行が本格的に見えてきました。それがβ版です。   PHPポケモンは2020年12月現在α版となっており、完全な試作段階のWEBアプリケーションです。セーブ機能はなく、セッションの有効期...

SEOで強いURLとは?安易に決めると危険だった!「よく分かるSEO対策」パーマリンク編
SEO対策
WordPress,ブログ
SEOで強いURLとは?安易に決めると危険だった!「よく分かるSEO対策」パーマリンク編

  パーマリンクの設定ってSEO対策に関係あるの?   意外と気になっているポイントではないでしょうか。実は、URLとSEO対策には全くの無関係ではなく、安易に決めてしまうとせっかく積み上げた資産的価値を台無しにしてしまうかも知れない危険なポイントでもあるのです。   今回は、ブログの収益化を目指...

戦闘不能による交代編 PHPポケモン84
プログラミング
PHP,PHPポケモン,ポケモン
戦闘不能による交代編 PHPポケモン84

戦闘不能による交代 ポケモンが戦闘不能になった際、もし手持ちに戦える状態のポケモンが残っていれば「交代」か「逃げる」の2択になります。今までは手持ちポケモンが1匹のみで判定を行っていたので、今回は交代の選択肢も含めて勝負の判定の見直しをしていきます。   パーティーを含めた勝負判定 味方または...

本当に価値のある宣伝方法 〜多くの人が実践している【間違った努力】とは〜
マーケティング
Facebook,note,Twitter,YouTube,集客
本当に価値のある宣伝方法 〜多くの人が実践している【間違った努力】とは〜

  YouTubeやTwitter、Facebookやnoteなど、いろんなサービスを使った集客方法があります。集客という視点ではなく、それ自体に人を集めたいという人も多いでしょう。   フォロワーやチャンネル登録者数を増やすために、多くの人が様々な取り組みをしていますが、その中でもひときわ意味のないことをただ繰り返して...

捕獲処理実装編 PHPポケモン 80
プログラミング
PHP,PHPポケモン,ポケモン
捕獲処理実装編 PHPポケモン 80

捕獲処理の作成 前回モンスターボールのクラスを作成したので、今回は捕獲判定までの一連の処理を仕上げていきます。サービス自体は他のアイテムと一緒にするためItemServiceを呼び出し、その中で使用されたアイテムを判断して分岐を作ります。   バトル中のアイテムサービス(/App/Services/Battle/ItemService.ph...

レベルアップ時のステータス表示編 PHPポケモン 48
プログラミング
PHP,PHPポケモン,ポケモン
レベルアップ時のステータス表示編 PHPポケモン 48

ステータスの表示 前回経験値バーのアニメーションとレベルアップ時の動的な変更を実装しましたが、レベルアップ時に表示されるステータスの実装は先送りにしていました。なので今回はそのステータス表示を作成しましょう。   ステータスはメッセージとして返却せずに、小モーダルを起動させるという方法で対応しま...

カテゴリ

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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力