プログラミング

ダメージ固定技編(ちきゅうなげ・カウンター) PHPポケモン 41

PHP PHPポケモン ポケモン
ダメージ固定技編(ちきゅうなげ・カウンター) PHPポケモン 41

ダメージ固定技とは

PHPポケモンでも作成したダメージ計算機能ですが、ポケモンの技の中にはそれを必要としない技がいくつかあります。それが「固定ダメージ技」です。

ポケモンwiki(ダメージ固定技)

ステータスに依存せず、わざ自体にダメージ量が決まっているわざ。

一口にダメージ固定技と言っても効果が大きく分かれており、対戦面などでも基本的に別物として考えられる。

  • 純粋にわざに定められたダメージを与えるもの
    ソニックブーム、ちきゅうなげなど
  • 相手の現在HPから一定割合を削るもの
    いかりのまえばなど
  • 相手から受けたダメージによって変化するもの(カウンター技)
    カウンターなど
  • 当たる確率は低いが相手のHPを全て削るもの
    一撃必殺技

 

 以前作成した「一撃必殺技」もダメージ固定技に分類されています。

今回作成するのは、自分のレベル分のダメージを与えることができる「ちきゅうなげ」と、自分のレベルに応じた乱数ダメージを与える「サイコウェーブ」このターン受けたダメージ量によって与えるダメージが変化する「カウンター」の3つを作成しましょう。

 

ちきゅうなげ

まずは「ちきゅうなげ」という技を作成します。固定ダメージ技には威力が不要のため、威力値にはnullをセットしておき、代わりに与えるダメージを返却するためのメソッド(getFixedDamage)と、ダメージ固定技を判別するためのプロパティ(fixed_damage_flg)を新しく用意しましょう。

 

ちきゅうなげ(/Classes/Move/SeismicToss.php
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Move.php');
 
// ちきゅうなげ
class SeismicToss extends Move
{
 
    /**
    * 正式名称
    * @var string
    */
    protected $name = 'ちきゅうなげ';
 
    /**
    * 説明文
    * @var string
    */
    protected $description = '自分のレベル分の固定ダメージを与える';
 
    /**
    * タイプ
    * @var string
    */
    protected $type = 'Fighting';
 
    /**
    * 分類
    * @var string(physical:物理|special:特殊|status:変化)
    */
    protected $species = 'physical';
 
    /**
    * 威力
    * @var integer
    */
    protected $power = null;
 
    /**
    * 命中率
    * @var integer
    */
    protected $accuracy = 100;
 
    /**
    * 使用回数
    * @var integer
    */
    protected $pp = 20;
 
    /**
    * 優先度
    * @var integer
    */
    protected $priority = 0;
 
    /**
    * ダメージ固定技フラグ
    * @var boolean
    */
    protected $fixed_damage_flg = true;
 
    /**
    * 固定ダメージ量の取得
    *
    * @param Pokemon $atk 攻撃ポケモン
    * @param Pokemon $def 防御ポケモン
    * @return integer
    */
    public function getFixedDamage($atk, $def)
    {
        // 攻撃ポケモンのレベル分ダメージを与える
        return $atk->getLevel();
    }
 
}

 

次に、親クラスにダメージ固定技フラグの取得用メソッド(getFixedDamageFlg)と、ダメージ固定技フラグのプロパティ(fixed_damage_flg)に初期値のfalseをセットしておきましょう。

 

技クラス(/Classes/Move.php
<?php
$root_path = __DIR__.'/..';
require_once($root_path.'/App/Traits/InstanceTrait.php');
require_once($root_path.'/App/Traits/ResponseTrait.php');
 
// 技
abstract class Move
{
 
--省略
 
    /**
    * 固定ダメージ技フラグの取得
    *
    * @return boolean
    */
    public function getFixedDamageFlg()
    {
        return $this->fixed_damage_flg;
    }

 

これで固定ダメージ技の準備が整いました。

次にダメージ計算処理の修正です。攻撃トレイトのattackSuccessメソッド内に分岐を追加、修正しましょう。

 

攻撃トレイト(/App/Traits/Service/Battle/ServiceBattleTrait.php
/**
* 攻撃判定成功時の処理
*
* @param object $atk_pokemon
* @param object $def_pokemon
* @param object $move
* @return void
*/
private function attackSuccess($atk_pokemon, $def_pokemon, $move)
{
    if($move->getFixedDamageFlg()){
        /**
        * 固定ダメージ技
        */
        $damage = $move->getFixedDamage($atk_pokemon, $def_pokemon);
    }else{
        /**
        * 通常技
        */
        // ローカル変数として補正値を用意
        $m = 1;
        // 必要ステータスの取得
        $stats = $this->getStats($move->getSpecies(), $atk_pokemon, $def_pokemon);
        // ダメージ計算(物理,特殊技)
        if($move->getSpecies() !== 'status'){
            // 急所判定
            $critical = $this->checkCritical($move->getCritical());
            if($critical){
                // 補正値を乗算
                $m *= $critical;
                $this->setMessage('急所に当たった!');
            }
            // 乱数補正値の計算
            $m *= $this->calRandNum();
            // タイプ一致補正の計算
            $this->calMatchType($move->getType(), $atk_pokemon->getTypes());
            // ダメージ計算
            $damage = $this->calDamage(
                $atk_pokemon->getLevel(),   # 攻撃ポケモンのレベル
                $stats['a'],                # 攻撃ポケモンの攻撃値
                $stats['d'],                # 防御ポケモンの防御値
                $move->getPower(),          # 技の威力
                $this->m * $m,              # 補正値(プロパティ*ローカル)
            );
            // やけど補正
            if(($move->getSpecies() === 'physical') && ($atk_pokemon->getSa() === 'SaBurn')){
                // 物理且つやけど状態ならダメージを半減
                $damage *= 0.5;
            }
            // タイプ相性のメッセージを返却
            $this->setMessage($this->type_comp_msg);
        }
    }
    // ダメージ計算
    $def_pokemon->calRemainingHp('sub', $damage ?? 0);
    // 追加効果(相手にHPが残っていれば)
    if($def_pokemon->getRemainingHp()){
        // 追加効果
        $move->effects($atk_pokemon, $def_pokemon);
        // 追加効果のメッセージをセット
        $this->setMessage($move->getMessages());
        $move->resetMessage();
        return;
    }
}

 

固定ダメージ技のダメージ量は、各技クラスに作成したgetFixedDamageが算出してくれるため、ダメージ計算を行う必要がありません。その関係上、相手のステータス取得や補正値計算も不要です。タイプ相性の「こうかがない」に関しては従わなければなりませんが「こうかばつぐん」と「こうかいまひとつ」に関しても判定メッセージは不要です。もちろん急所に当たることもありません。

なので、攻撃成功であればまず固定ダメージかどうかの分岐を行いダメージ量を計算、その後ポケモンにダメージを与えるという順番で処理を行なっています。

 

「ちきゅうなげ」のgetFixedDamageメソッドでは攻撃ポケモンのレベルを返り値として返却しているので、この値がダメージ量となってそのまま計算されることになります。

以下出力結果です。

 

 

 

一撃必殺を除き、固定ダメージ技は大ダメージと言えるものではありません。そのため、通常のポケモンではそこまで大きなメリットを感じないかも知れませんが、攻撃力が乏しいポケモン(耐久型ポケモン)などが使えるとなればかなり心強い技の1つです。また、相手の耐久値が高い場合や、ランク補正などで通常技ではとても十分なダメージを与えられない状況においては重宝される1つです。

 

サイコウェーブ

次に「サイコウェーブ」を作成します。こちらも「ちきゅうなげ」の要領で作成します。

 

サイコウェーブ(/Classes/Move/Psywave.php
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Move.php');
 
// サイコウェーブ
class Psywave extends Move
{
 
    /**
    * 正式名称
    * @var string
    */
    protected $name = 'サイコウェーブ';
 
    /**
    * 説明文
    * @var string
    */
    protected $description = '相手にランダムに決まった値を固定ダメージとして与える。';
 
    /**
    * タイプ
    * @var string
    */
    protected $type = 'Psychic';
 
    /**
    * 分類
    * @var string(physical:物理|special:特殊|status:変化)
    */
    protected $species = 'special';
 
    /**
    * 威力
    * @var integer
    */
    protected $power = null;
 
    /**
    * 命中率
    * @var integer
    */
    protected $accuracy = 100;
 
    /**
    * 使用回数
    * @var integer
    */
    protected $pp = 15;
 
    /**
    * 優先度
    * @var integer
    */
    protected $priority = 0;
 
    /**
    * 固定ダメージフラグ
    * @var boolean
    */
    protected $fixed_damage_flg = true;
 
    /**
    * 固定ダメージ量の取得
    *
    * @param Pokemon $atk 攻撃ポケモン
    * @param Pokemon $def 防御ポケモン
    * @return integer
    */
    public function getFixedDamage($atk, $def)
    {
        // 攻撃ポケモンのレベル*(0.5〜1.5)倍のダメージを与える
        return (int)($atk->getLevel() * (random_int(5, 15) / 10));
    }
 
}

 

サイコウェーブのダメージ量は以下の通りです。

 (攻撃側のレベル×0.5~攻撃側のレベル×1.5)のダメージ

 

固定ダメージとはいっても乱数が含まれます。なので、getFixedDamage内で乱数を作成してダメージ量を確定させます。

小数点以下が含まれる乱数はrandom_intでは作成出来ないため、5〜15の間で乱数を作成し、10で割ってからレベルを掛け、整数(切り捨て)してダメージ量を返却しています。

 

 

 

初代技であれば、「ソニックブーム」や「りゅうのいかり」、「ナイトヘッド」「いかりのまえば」が同じ仕組みを使って作成することができます。ダメージ量が決まっている技はそのまま数値を返却、それ以外も攻撃ポケモンまたは防御ポケモンのインスタンスを引数で受け取っていれば算出が可能なものです。

 

カウンター

ダメージ固定技の中でも「ちきゅうなげ」などとは同じ仕組みだけでは実装できないものがあります。それが「カウンター」という技です。

(使用したターンの最後に受けた物理技のダメージ×2)のダメージ

  

受けたダメージの格納

カウンターは優先度マイナス5という技のため、もし相手も同じ優先度の技を使用していない限りは後手になります。更に、ダメージ量の計算が「使用ターンに自身が受けた物理ダメージの2倍」となるので、技によって受けた最後のダメージ量を格納しておかなければなりません

なので、ポケモンに対して新しくプロパティを用意することで対応しましょう。

 

ポケモンクラス(/Classes/Pokemon.php
// ポケモン
abstract class Pokemon
{
 
--省略
 
    /**
    * このターンに受けた攻撃によるダメージ
    * @var array
    */
    protected $turn_damage = [
        'physical' => 0,
        'special' => 0,
    ];

 

ダメージは物理・特殊の2種類あるため、連想配列で用意します。プロパティの作成に合わせて取得(getTurnDamage)、格納(setTurnDamage)、リセット(resetTurnDamage)を用意します。

 

ポケモン用Get格納トレイト(/App/Traits/Class/Pokemon/ClassPokemonGetTrait.php
/**
* このターンに受けたダメージの取得
* @param string (physical|special)
* @return mixed
*/
public function getTurnDamage($param='')
{
    if(empty($param)){
        // 配列で全返却
        return $this->turn_damage;
    }else{
        // 指定ダメージを取得
        return $this->turn_damage[$param] ?? 0;
    }
}

 

ポケモン用Set格納トレイト(/App/Traits/Class/Pokemon/ClassPokemonSetTrait.php
/**
* このターン受けたダメージ量の格納
* @param string $param
* @param integer $damage
* @return void
*/
public function setTurnDamage($param, int $damage)
{
    $this->turn_damage[$param] = $damage;
}

 

ポケモン用Reset格納トレイト(/App/Traits/Class/Pokemon/ClassPokemonResetTrait.php
<?php
trait ClassPokemonResetTrait
{
    /**
    * このターン受けたダメージ量をリセットする
    * @return void
    */
    public function resetTurnDamage()
    {
        $this->turn_damage = [
            'physical' => 0,
            'special' => 0,
        ];
    }
 
}

※リセットトレイトは新しく作成しています

 

これで準備が整いました。引き継ぎ対象としていないため、前回のダメージがそのまま残り続けることはありませんが、ループで行う自動ターン処理があるため、念には念を入れてサービス開始時にまずリセットを実行しておきましょう。

 

たたかうサービス(/App/Services/Battle/FightService.php
/**
* @return void
*/
public function execute()
{
    // ターンダメージのリセット
    $this->pokemon
    ->resetTurnDamage();
    $this->enemy
    ->resetTurnDamage();

 

次にダメージの格納処理です。こちらはダメージ計算終了後にメソッドを呼び出して、算出したダメージをダメージ計算前に技種別に合わせて格納します。

 

攻撃トレイト(/App/Traits/Service/Battle/ServiceBattleAttackTrait.php
/**
* 攻撃判定成功時の処理
*
* @param object $atk_pokemon
* @param object $def_pokemon
* @param object $move
* @return void
*/
private function attackSuccess($atk_pokemon, $def_pokemon, $move)
{
    if($move->getFixedDamageFlg()){
        /**
        * 固定ダメージ技
        */
        $damage = $move->getFixedDamage($atk_pokemon, $def_pokemon);
    }else{
        /**
        * 通常技
        */
        // ローカル変数として補正値を用意
        $m = 1;
        // 必要ステータスの取得
        $stats = $this->getStats($move->getSpecies(), $atk_pokemon, $def_pokemon);
        // ダメージ計算(物理,特殊技)
        if($move->getSpecies() !== 'status'){
            // 急所判定
            $critical = $this->checkCritical($move->getCritical());
            if($critical){
                // 補正値を乗算
                $m *= $critical;
                $this->setMessage('急所に当たった!');
            }
            // 乱数補正値の計算
            $m *= $this->calRandNum();
            // タイプ一致補正の計算
            $this->calMatchType($move->getType(), $atk_pokemon->getTypes());
            // ダメージ計算
            $damage = $this->calDamage(
                $atk_pokemon->getLevel(),   # 攻撃ポケモンのレベル
                $stats['a'],                # 攻撃ポケモンの攻撃値
                $stats['d'],                # 防御ポケモンの防御値
                $move->getPower(),          # 技の威力
                $this->m * $m,              # 補正値(プロパティ*ローカル)
            );
            // やけど補正
            if(($move->getSpecies() === 'physical') && ($atk_pokemon->getSa() === 'SaBurn')){
                // 物理且つやけど状態ならダメージを半減
                $damage *= 0.5;
            }
            // タイプ相性のメッセージを返却
            $this->setMessage($this->type_comp_msg);
        }
    }
    // このターン受けるダメージをポケモンに格納
    $def_pokemon->setTurnDamage($move->getSpecies(), $damage ?? 0);
    // ダメージ計算
    $def_pokemon->calRemainingHp('sub', $damage ?? 0);

 

これでダメージ量の補完処理が作成できました。

 

では、カウンターの技クラスを作成しましょう。

 

カウンター(/Classes/Move/Counter.php
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Move.php');
 
// カウンター
class Counter extends Move
{
 
    /**
    * 正式名称
    * @var string
    */
    protected $name = 'カウンター';
 
    /**
    * 説明文
    * @var string
    */
    protected $description = '相手から受けた物理攻撃のダメージを2倍にして与える。';
 
    /**
    * タイプ
    * @var string
    */
    protected $type = 'Fighting';
 
    /**
    * 分類
    * @var string(physical:物理|special:特殊|status:変化)
    */
    protected $species = 'physical';
 
    /**
    * 威力
    * @var integer
    */
    protected $power = null;
 
    /**
    * 命中率
    * @var integer
    */
    protected $accuracy = 100;
 
    /**
    * 使用回数
    * @var integer
    */
    protected $pp = 20;
 
    /**
    * 優先度
    * @var integer
    */
    protected $priority = -5;
 
    /**
    * 固定ダメージフラグ
    * @var boolean
    */
    protected $fixed_damage_flg = true;
 
    /**
    * 固定ダメージ量の取得
    *
    * @param Pokemon $atk 攻撃ポケモン
    * @param Pokemon $def 防御ポケモン
    * @return integer
    */
    public function getFixedDamage($atk, $def)
    {
        // 攻撃ポケモンのレベル分ダメージを与える
        return $atk->getTurnDamage('physical') * 2;
    }
 
}

 

これでカウンターで与える固定ダメージ量が計算できるようになりました。

ただ、カウンターは相手からこのターン物理ダメージを受けていなければ「攻撃失敗」という処理が必要です。なので、こちらは命中判定処理に追記しましょう。

 

攻撃トレイト(/App/Traits/Service/Battle/ServiceBattleAttackTrait.php
/**
* 命中判定
*
* @param object $atk
* @param object $def
* @param object $move
* @return boolean
*/
private function checkHit($atk, $def, $move)
{
    // 一撃必殺技のチェック
    if($move->getOneHitKnockoutFlg()){
        if($atk->getLevel() < $def->getLevel()){
            // 相手の方がレベルが高ければ無効
            $this->setMessage($move->getOneHitKnockoutFailedMessage($def->getPrefixName()));
            return false;
        }
        // レベル差計算を含めた命中率を取得
        $accuracy = $move->getOneHitKnockoutAccuracy($atk, $def);
    }else{
        // 命中率取得
        $accuracy = $move->getAccuracy();
    }
    // nullの場合は命中率関係無し
    if(is_null($accuracy)){
        return true;
    }
    // カウンターの失敗判定
    if((get_class($move) === 'Counter') && empty($atk->getTurnDamage('physical'))){
        // 自身にこのターン物理ダメージが蓄積していなければ失敗
        $this->setMessage($move->getFailedMessage($atk->getPrefixName()));
        return false;
    }
    /**
    * 0〜100からランダムで数値を取得して、それより小さければ命中
    * 例:命中80%→mt_randで60が生成されたら成功、90なら失敗
    */
    if($accuracy >= mt_rand(0, 100)){
        // 攻撃成功
        return true;
    }
    // 攻撃失敗
    $this->setMessage($move->getFailedMessage($atk->getPrefixName()));
    return false;
}

 

カウンターは特別な技のため、技クラスで個別の判定を加えました。初代以外ではミラーコートやメタルバーストなどがありますが、もしすべてを実装したとしても数が知れているので、一旦は個別処理で対応します。

 

では、実際にカウンターを使用してみましょう。

 

カウンター成功

 

 

 

カウンター失敗

 

 

 

攻撃が外れた際には失敗となりました。これで正常に判定がされているということがわかりますね。

  

まとめ

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

今回のPHPポケモンでは「ダメージ固定技」について「ちきゅうなげ」「サイコウェーブ」「カウンター」の作成方法をご紹介しました。

プログラミング学習に取り組んでいる方や、ゲームづくりに興味がある人は、ぜひ参考にしてみてくださいね。

 

注目の記事

千利休から学ぶビジネスモデルの作り方3ステップ!守破離とは
ビジネスモデル
千利休から学ぶビジネスモデルの作り方3ステップ!守破離とは

  千利休の利休道歌に以下のような記述があります。 規矩作法 守り尽くして破るとも離るるとても本を忘るな    これは武道や芸道など学びの基礎として考えられ、創造過程のベースとして用いられてきました。これはビジネスモデルを作り上げるという観点から見ても非常に重要かつ、失敗する多くの人が疎...

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

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

YouTuber・ブロガー必見!知る人ぞ知るサムネイルの重要性とは
デザイン
Facebook,Instagram,Twitter,YouTuber,サムネイル,ブロガー
YouTuber・ブロガー必見!知る人ぞ知るサムネイルの重要性とは

  サムネイルって本当に重要? ブログの場合はフリー画像でもいいんじゃない?   そう考えている人はいませんか? 残念ですが、それは大きな間違いです。サムネイルを作り込むことは非常に重要であり、集客ポイントを拡大させるのはもちろん、ブランディングにもつながるのです。   今回は「知る人ぞ...

3日坊主にならないために
雑記
ブログ,プログラミング
3日坊主にならないために

  プログラミングに挑戦してみたが、途中で挫折した人 ブログを書き始めたが、まったく続かない人   継続することの大切さは分かっても、なかなか難しいものです。 しかしそれは、取り組み方を見直すだけで、実は簡単に解決できてしまうものなのです。 今回は、そんな「3日坊主」と呼ばれる人が、そ...

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

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

成功に近い3つの思考「楽して稼ぐ」「知識オタク」「資産形成は無駄」
雑記
成功に近い3つの思考「楽して稼ぐ」「知識オタク」「資産形成は無駄」

  「楽して稼ぎたい」   人間誰しも、そう考えているはずです。これは正しく、成功するためには必要な思考です。 それを「楽に稼ぐ方法なんてない」と無理やり押さえつけてしまう人は、完全に本質が見えておらず、その大半に「楽して稼げない自分を認められてない」というマイナス因子が含まれてい...

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

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

モンスターボール作成編 PHPポケモン 79
プログラミング
PHP,PHPポケモン,ポケモン
モンスターボール作成編 PHPポケモン 79

モンスターボールとは ポケモンのゲームにとっての楽しみの1つである「バトル」はある程度実装できてきましたが、もう一つ無くてはならない楽しみがあります。それが「ポケモンを集める」というコレクター要素です。 そして、ポケモンを捕まえるために欠かせない道具の1つが「モンスターボール」です。  モンス...

カテゴリ

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