ポケモンのタイプについて
2020年剣盾シリーズでのポケモンでは、全18タイプが実装されています。初代では15タイプ(あく、はがね、フェアリーを除く)からスタートして、たった3つのタイプしか増えていない、と感じる方もいるかも知れませんが、この追加に関してもバトルの歴史を大きく動かしてきたのです。また、タイプ相性についても世代を重ねるごとに改変されています。
PHPポケモンでは、現在リリースされている18タイプを参考に作成していきます。ポケモンだけではなく、技のタイプについても同様です。
- ポケモンが覚える技:初代
- 種族値:最新
- 技効果:最新
- タイプ:最新
クラスの作成
技やポケモンに比べると情報量は少ないと言えますが、相性などを設定する関係上、タイプに関してもそれぞれクラスを作成し、親となる「タイプ(type)」というクラスを継承させる仕様で進めていきます。
子クラス
まずは子クラスです。手始めにでんき(Electric)を作成しましょう。
でんきタイプ(/Classes/Type/Electric.php)
<?php
require_once(__DIR__.'/../Type.php');
// でんきタイプ
class Electric extends Type
{
/**
* 正式名称
* @var string
*/
protected $name = 'でんき';
/**
* 攻撃技で使用したときの判定
*/
/**
* こうかばつぐん
* @var integer
*/
protected $excellent = ['Water', 'Flying'];
/**
* こうかいまひとつ
* @var integer
*/
protected $not_very = ['Electric', 'Grass', 'Dragon'];
/**
* こうかがない
* @var integer
*/
protected $doesnt_affect = ['Ground'];
}
※親クラスは想定で準備しています
技の相性判定をするためには、その関係性を持たせて置く必要があります。今回は「実装するタイプクラスで攻撃した場合」の相性をプロパティとして設定しています。
/**
* こうかばつぐん
* @var integer
*/
protected $excellent = ['Water', 'Flying'];
例えば、でんきタイプの技は、みず(Water)とひこう(Flying)に相性がよく「こうかばつぐん」です。ダメージの計算上は2倍のダメージ量を与えることができます。もし攻撃した側のexcellentプロパティに受け手側のタイプが含まれているかどうかをチェックすることで判定します。
もちろん相性が悪いタイプもあります。その第1段階をnot_varyのプロパティに設定します。
/**
* こうかいまひとつ
* @var integer
*/
protected $not_very = ['Electric', 'Grass', 'Dragon'];
でんきタイプの攻撃は、でんき(Electric)とくさ(Grass)とドラゴン(Dragon)の3タイプに相性が悪く、通常の0.5倍のダメージ量しか与えることができません。
相性が悪いタイプについては、2段階で構成されており、もう一つをdoesnt_affectのプロパティに設定します。
/**
* こうかがない
* @var integer
*/
protected $doesnt_affect = ['Ground'];
でんきタイプの攻撃は、じめん(Ground)には全く効果がありません。0倍のダメージ量です。もし相手のタイプが、みず・じめんの2つを備えていた場合、有利タイプがあったとしてもダメージは与えられません。
ポケモンは最大2つのタイプを備えているため、ダメージの判定は以下の6通りから構成されることになります。
4倍・2倍・1倍・0.5倍・0.25倍・0倍
設定例として御三家で使用する「みず・ほのお・くさ・どく」の4タイプ分のサンプルコードを載せておきます。残りの13タイプは相性表を参考に作成してみてください。
ポケモンwiki(第6世代以降を参照)
みずタイプ(/Classes/Type/Water.php)
<?php
require_once(__DIR__.'/../Type.php');
// みずタイプ
class Water extends Type
{
/**
* 正式名称
* @var string
*/
protected $name = 'みず';
/**
* 攻撃技で使用したときの判定
*/
/**
* こうかばつぐん
* @var integer
*/
protected $excellent = ['Fire', 'Ground', 'Rock'];
/**
* こうかいまひとつ
* @var integer
*/
protected $not_very = ['Water', 'Grass', 'Dragon'];
/**
* こうかがない
* @var integer
*/
protected $doesnt_affect = [];
}
ほのおタイプ(/Classes/Type/Fire.php)
<?php
require_once(__DIR__.'/../Type.php');
// ほのおタイプ
class Fire extends Type
{
/**
* 正式名称
* @var string
*/
protected $name = 'ほのお';
/**
* 攻撃技で使用したときの判定
*/
/**
* こうかばつぐん
* @var integer
*/
protected $excellent = ['Grass', 'Ice', 'Bug', 'Steel'];
/**
* こうかいまひとつ
* @var integer
*/
protected $not_very = ['Fire', 'Water', 'Rock', 'Dragon'];
/**
* こうかがない
* @var integer
*/
protected $doesnt_affect = [];
}
くさタイプ(/Classes/Type/Grass.php)
<?php
require_once(__DIR__.'/../Type.php');
// くさタイプ
class Grass extends Type
{
/**
* 正式名称
* @var string
*/
protected $name = 'くさ';
/**
* 攻撃技で使用したときの判定
*/
/**
* こうかばつぐん
* @var integer
*/
protected $excellent = ['Water', 'Ground', 'Rock'];
/**
* こうかいまひとつ
* @var integer
*/
protected $not_very = ['Fire', 'Grass', 'Poison', 'Flying', 'Bug', 'Dragon', 'Steel'];
/**
* こうかがない
* @var integer
*/
protected $doesnt_affect = [];
}
どくタイプ(/Classes/Type/Poison.php)
<?php
require_once(__DIR__.'/../Type.php');
// どくタイプ
class Poison extends Type
{
/**
* 正式名称
* @var string
*/
protected $name = 'どく';
/**
* 攻撃技で使用したときの判定
*/
/**
* こうかばつぐん
* @var integer
*/
protected $excellent = ['Grass', 'Fairy'];
/**
* こうかいまひとつ
* @var integer
*/
protected $not_very = ['Poison', 'Ground', 'Rock', 'Ghost'];
/**
* こうかがない
* @var integer
*/
protected $doesnt_affect = ['Steel'];
}
親クラス
次に親クラスになるType.phpを作成しましょう。
タイプ(/Classes/Type.php)
<?php
// タイプ
abstract class Type
{
/**
* インスタンス作成時に実行される処理
*
* @return void
*/
public function __construct()
{
//
}
/**
* タイプ名の取得
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* 攻撃で有利なタイプ
*
* @return array
*/
public function getAtkExcellentTypes()
{
return $this->excellent;
}
/**
* 攻撃で相性が悪いタイプの取得
*
* @return array
*/
public function getAtkNotVeryTypes()
{
return $this->not_very;
}
/**
* 攻撃で全く効果が無いタイプの取得
*
* @return array
*/
public function getAtkDoesntAffectTypes()
{
return $this->doesnt_affect;
}
}
タイプクラスもインスタンス化する必要がないため、abstractを宣言しています。
今回はgetNameのプロパティしか使用しませんが、残りの関係タイプも取得できるようにしておきましょう。
ポケモンへの実装
それではポケモンにタイプを割り当てしていきましょう。今回もピカチュウを例に実装します。
ピカチュウ(/Classes/Pokemon/Pikachu.php)
<?php
require_once(__DIR__.'/../Pokemon.php');
require_once(__DIR__.'/Raichu.php');
// ピカチュウ
class Pikachu extends Pokemon
{
/**
* 正式名称
* @var string(min:1 max:5)
*/
protected $name = 'ピカチュウ';
/**
* タイプ
* @var array
*/
protected $types = ['Electric'];
ピカチュウに対して、typesというプロパティを割り当てました。ピカチュウは1タイプだけですが、フシギダネなど2タイプを持つポケモンもいるため、配列で用意します。
次にタイプの取得用メソッド(getTypes)をGetトレイトに作成しましょう。
Get格納トレイト(/Traits/Pokemon/GetTrait.php)
/**
* タイプの取得
*
* @param string|array|object $return
* @var void
*/
public function getTypes($return='object')
{
/**
* タイプ名の取得用関数
* @param object
* @var string
*/
function getTypesName($obj){
return $obj->getName();
}
// array_mapで配列内のタイプクラスをインスタンス化
$types = array_map([$this, 'getInstance'], $this->types);
switch ($return) {
case 'string':
// array_mapでタイプ名の配列にしたものを、implodeで文字列に変換
$types = implode(',', array_map('getTypesName', $types));
break;
case 'array':
// array_mapでタイプ名の配列にして返却
$types = array_map('getTypesName', $types);
break;
}
return $types;
}
引数で返却する型を指定できるようにしておきます。タイプはそのまま取得すればクラス名が入った配列です。しかし、出力画面で使用するためには、格納されている各タイプがインスタンス化されているか、クラスで設定した名称が取得できなければなりません。状況に合わせて様々な型を受け取れるように、仮でstring、array、objectの3つを用意しました。
それではメソッド内の中腹部分から見てみましょう。
// array_mapで配列内のタイプクラスをインスタンス化
$types = array_map([$this, 'getInstance'], $this->types);
まずは、配列に格納されているクラスを全てインスタンス化しなければなりませんので、前回技のインスタンス化をした要領でarray_mapをかけています。コールバック関数を使い回すため、今回はgetInstanceというメソッドを新しいトレイトに作成しました。
インスタンス化用トレイト(/Traits/InstanceTrait.php)
<?php
trait InstanceTrait
{
/**
* インスタンス化関数
* @param string $class_name
* @return object
*/
protected function getInstance($class_name){
if(class_exists($class_name)){
return new $class_name();
}
}
}
前回の技取得のコールバック関数で使用したgetMoveInstanceの関数と内容は同じです。こちらのトレイトをポケモンクラスで読み込んでおきましょう。
Getトレイトの記述に戻ります。次に返り値を引数に合わせて返却するため、switchを使用します。
switch ($return) {
case 'string':
// array_mapでタイプ名の配列にしたものを、implodeで文字列に変換
$types = implode(',', array_map('getTypesName', $types));
break;
case 'array':
// array_mapでタイプ名の配列にして返却
$types = array_map('getTypesName', $types);
break;
}
return $types;
objectの場合は、そのままを返却するため省略しています。文字列(string)であれば、「くさ,どく」のような形式で返却したいので、インスタンス化したタイプから名前を抽出し、それをimplodeを使って文字列に直します。ここで名前を抽出するために、最初に記述したタイプ名の取得関数を使用します。
/**
* タイプ名の取得用関数
* @param object
* @var string
*/
function getTypesName($obj){
return $obj->getName();
}
受け取ったタイプのオブジェクト(インスタンス)から名前を取得して返却しています。array_mapの返り値は以下のようになっています。
# ピカチュウの場合
['でんき']
# フシギダネの場合
['くさ', 'どく']
文字列(string)の場合は作成できた配列をimplode、配列希望の場合はそのまま返却しています。
それではタイプをポケモンの詳細取得メソッド(getDetails)に追加しましょう。
Get格納トレイト(/Traits/Pokemon/GetTrait.php)
/**
* 詳細を取得する
* @return integer
*/
public function getDetails()
{
return [
'正式名称' => $this->getName(),
'ニックネーム' => $this->getNickName(),
'タイプ' => $this->getTypes('string'),
'現在のレベル' => $this->getLevel(),
'現在の経験値' => $this->getExp(),
'次のレベルまで' => $this->getReqLevelUpExp(),
];
}
出力結果は以下の通りです。
技への実装
前回実装した技にもタイプが割り振られています。なので、技のタイプ取得用メソッドにも同様の処理を記述します。
技クラス(/Classes/Move.php)
<?php
require_once(__DIR__.'/../Traits/InstanceTrait.php');
// 技
abstract class Move
{
use InstanceTrait;
/**
* インスタンス作成時に実行される処理
*
* @return void
*/
public function __construct()
{
//
}
--省略
/**
* タイプの取得
*
* @return object
*/
public function getType()
{
return $this->getInstance($this->type);
}
まずは先程作成した、インスタンス化用のトレイトを読み込んでおきます。ポケモンと違い、タイプが配列ではないので、そのままgetInstanceのメソッドにタイプのプロパティ($this->type)をかけてあげるだけでタイプのインスタンスを返すことができます。
今回は名前の取得は出力画面側で対応します。
出力用ファイル(/index.php)
<?php # 覚えている技 ?>
<table class="table table-bordered table-hover">
<thead class="thead-light">
<tr>
<th scope="col">覚えている技</th>
<th scope="col">タイプ</th>
<th scope="col">PP</th>
</tr>
</thead>
<tbody>
<?php foreach($controller->pokemon->getMove() as $move): ?>
<tr>
<th scope="row" class="w-50"><?=$move->getName()?></th>
<td><?=$move->getType()->getName()?></td>
<td><?=$move->getPp()?>/<?=$move->getPp()?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
タイプ用に名称横に1列追加しました。タイプ名の取得は、$move->getType()->getName()のように、メソッドをつなぎ合わせていきながら出力しています。
それでは出力結果を見てみましょう。
それぞれに割り当てたタイプが出力されていますね。これでタイプシステムの実装が完了です。
まとめ
いかがだったでしょうか。
今回のPHPポケモンは「タイプ実装編」についてご紹介しました。
技、タイプが揃えば、バトルシステムに必要な要素が整いました。よりポケモンらしくなったとも言えるでしょう。
ゲームやポケモン好きな人、プログラミング学習中の方は、ぜひ参考にしてくださいね。