【问题标题】:Should I use multiple classes for game?我应该为游戏使用多个类吗?
【发布时间】:2011-04-02 16:08:18
【问题描述】:

我正在考虑用 PHP 制作一个基于文本的 RPG 类型的程序,作为一个假期项目和一个了解更多关于 PHP 和 OOP 的机会。 (我知道,这可能不是语言的最佳选择,但我不想在 OOP 的同时从头开始学习另一种语言。)

无论如何,我才刚刚开始设计过程并考虑“怪物”。每种怪物类型(你知道,兽人、地精、老鼠等)都会有自己的统计数据、技能等等。起初我虽然可以只有一个怪物类并在实例化对象时设置属性。但后来我发现这可能效率有点低,所以我正在考虑为每个 type 怪物设置一个类。

考虑到每个类中的方法可能相同,这是解决问题的最佳方法吗?有没有更好的方法来做我还不知道的事情?

感谢任何帮助。

【问题讨论】:

  • 有什么理由不能枚举怪物的类型。保持原来的怪物类,但只包含一个额外的属性“类型”?
  • 如果不了解至少一些游戏机制,这有点难以分辨。
  • 我觉得设计这样的游戏是您学习/更好地理解 OOP 的好方法。干杯。
  • (相关) RPG Design Patterns
  • @Gordon 这是一个很棒的链接。谢谢

标签: php oop class


【解决方案1】:

你应该做的是让一些真正的组织进入游戏。

我以前从未构建过 PHP 游戏,但我非常了解结构应该如何。

一个实体/怪物应该由几个定义其特征的类组成

这是我头顶的一个小例子:

abstract class NonHuman implements Strengh,Weapons,Vehicles
{
     var $strength;
}
abstract class Vermin implements Strengh,Chemicals
{
    var $strength = 20;
    var $poisonous = true;
}
abstract class Humanoid implements Strengh,Weapons,Vehicles,Arms,Legs
{
}

抽象类的基本布局如下:

abstract class <BeingType> implements < Characteristics , Weapons , Etc>
{
    // Depending on < Characteristics , Weapons , Etc> you should
    // Build the methods here so that theres less work in the long run.
}

然后,一旦你有了你的基本类型,你就可以做类似的事情

class Rat extends Vermin
{
    public function __construct($name,$strength = 50)
    {
         $this->strength = $strength;
    }

    //Any new methods here would be specific to this Being / Rat.
}

$Robert = new Rat('Robert',80);
$Andrew = new Rat('Andrew',22);

if($Robert->strength > 50)
{
    $Robert->Kick($Andrew,'left',20); //20 mph lol
    if($Andrew->IsAlive())
    {
        if($Robert->TakeWeapon($Andrew,20)) //Uses 20% force
        {
             $Robert->FireWeaponAt($Andrew,-1); //Use all bullets on andrew!
        }
    }
    if(!$Andrew->IsAlive())
    {
        $Robert->UpdateScoreFromPLayer($Andrew,100); //Max of 100 points if andrew has them.
    }
}

通过这样做,为实体生成特征并不难。

您还可以设置父析构函数将用户名数据保存在数据库中以备下次使用,并使用 __construct 更新类数据。 希望这能给你一个好主意:)


还有更多:)

如果您为 SpecialMoves 上课,可以说您总是可以这样做

$Robert->AddSpecialMove('Roundhouse',new SpecialMove_Roundhouse(12));
$Robert->UserSpecialMove('Roundhouse',2);/ x2
if($Robert->_SpecialMoves->Roundhouse->Left() < 12)
{
    $Robert->UserSpecialMove('Roundhouse',-1);/ Use all kicks.
}

SpecialMove_Roundhouse 内,它会包含一些参数,例如损坏、完成所需的时间、它使用多少能量、你可以使用多少次。

在范围内的第一类应该总是我一个计算器,用于计算心率、血液水平、能量、库存等,所以你总是有必需品!


实现示例

实现确保更高的类包含某些函数和变量

interface Weapons
{
    public function Fire($target,$bullets);
}

class Colt45 implements Weapons
{
   var $damage = 2;
   var $max_bullets = 80;
   var $clip = 80;

   //THIS CLASS MUST HAVE FIRE
   public function fire($target,$bullets)
   {
      $ammo = $bullets > $clip ? $clip : $ammo;
      for($shot=0;$shot<=$ammo;$shot++)
      {
          $target->ReduceHealth($damage);
          if(!$target->IsAlive())
          {
              break;
          }
          $clip--; //Reduce ammo in clip.
      }
   }
}

此处的示例取自 php.net | http://www.php.net/manual/en/language.oop5.interfaces.php#96368

<?php

interface Auxiliary_Platform
{
    public function Weapon();
    public function Health();
    public function Shields();
}

class T805 implements Auxiliary_Platform
{
    public function Weapon()
    {
        var_dump(__CLASS__);
    }
    public function Health()
    {
        var_dump(__CLASS__ . "::" . __FUNCTION__);
    }
    public function Shields()
    {
        var_dump(__CLASS__ . "->" . __FUNCTION__);
    }
}

class T806 extends T805 implements Auxiliary_Platform
{
    public function Weapon()
    {
        var_dump(__CLASS__);
    }
    public function Shields()
    {
        var_dump(__CLASS__ . "->" . __FUNCTION__);
    }
}

$T805 = new T805();
$T805->Weapon();
$T805->Health();
$T805->Shields();

echo "<hr />";

$T806 = new T806();
$T806->Weapon();
$T806->Health();
$T806->Shields();

/* Output:
    string(4) "T805"
    string(12) "T805::Health"
    string(13) "T805->Shields"
    <hr />string(4) "T806"
    string(12) "T805::Health"
    string(13) "T806->Shields"
*/

?>

【讨论】:

  • +1 - 我的答案现在看起来很简单,与成熟的怪物界面库相比。
  • 我认为这场战斗不公平哈,我向他求婚:)
  • +1 - 这将作为一个很好的参考,虽然看起来比 Aaron 的答案要复杂一些。不过我肯定会试一试。谢谢。一个问题 - “实现”位有什么作用?
  • @RobertPitt - 非常感谢。很棒的参考!
【解决方案2】:

你应该阅读S.O.L.I.D
以下是 OOD(面向对象设计)的 5 条基本原则:

S单一责任原则
O封闭原则
Liskov替代原则
I 接口隔离原则
D 依赖倒置原则

http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

顺便说一句,如果您只想使用继承来扩展某些属性和方法,您可以使用Composition instead of Inheritance 来实现。应该使用继承来为您的类结构添加多态行为。

【讨论】:

    【解决方案3】:

    您可以创建一个抽象类,例如 Monster,然后为每种不同类型的怪物扩展该类。所以

    <?php
    abstract class Monster {
      private $health;
      public function attack() {
        //do stuff
      }
    }
    class Orc extends Monster {
      private $damage_bonus;
    }
    ?>
    

    编辑 Orc 会扩展 Monster,然后继承属性 $health 和函数 attack()。

    【讨论】:

    • 当然,通用怪物不需要abstract。这将允许您使用“通用”怪物。
    • 这是一个很好的方法,我同意蒂姆所说的,但如果 OP 想要一个通用的怪物,这只是偏好。
    • @Chris - 对,不是说不应该abstract,只是不想让OP认为扩展类需要是抽象的。
    • Monster 类应该是抽象的,因为它应该只定义该生物 IMO 的特征。看看我的帖子。
    • 这几乎不是一种可维护的方法。想象一下,你已经有兽人、兽人萨满(可以施法(),但生命值较低)和兽人酋长(可以踢()并有生命值加成),然后决定你也想要兽人萨满酋长。然后,您将复制 kick() 和 castSpell() 以及统计奖励修改器。然后你介绍一个 OrcWarlord(可以使用 kick()、warcry() 并且有更多的生命值和伤害加成)。而你想要 OrcShamanWarlord .. 看到哪里结束了吗?到处都是重复的代码。最好使用装饰器和策略模式并使用复合怪物。
    【解决方案4】:

    OOP 允许您扩展类,重用您的代码。这是一个简单的例子。查看Inheritance 的 PHP 文档。

    class Monster
    {
      public $size = 'average';
      public $color = 'grey';
    
      public function scare()
      {
        return "Bo!";
      }
    }
    
    class Rat extends Monster
    {
      public $size = 'small';
    
      public function scare()
      {
        return parent::scare() . " (runs away)";
      }
    }
    
    class Mouse extends Rat 
    {
      public $color = 'white';
    }
    

    输出将是:

    $rat = new Rat();
    echo $rat->scare(); //output: Bo! (runs away)
    echo $rat->size;    //output: small
    echo $rat->color;   //output: grey
    

    【讨论】:

    • 这受 Aaron 解决方案的问题困扰。请参阅我在他的答案下方的评论。您确实应该将恐慌行为换成策略模式,以保持 Monster 的可扩展性并防止具体 Vermin 实现中的代码重复。
    • @Gordon - '这只是基本类扩展如何工作的一个简单示例。 OP 询问作为刚开始 OOP 的人是否有多个课程。在这一点上似乎是适当数量的信息。
    【解决方案5】:

    类是关于行为的,所以如果你的游戏中不同类型的怪物行为不同,那么将它们建模为不同的对象。如果所有怪物基本上都具有相同的行为(例如,所有怪物都具有一组共享的属性,都可以攻击或防御和移动),那么如果将它们建模为单个怪物类,则可以节省大量工作。您始终可以将怪物类扩展为可以说话的怪物的专用类。

    【讨论】: