views:

277

answers:

5

I'm considering making a text-based RPG-type program in PHP as both a holiday project and a chance to learn more about PHP and OOP. (Maybe not the best choice in language, I know, but I didn't want to have to learn another language from scratch at the same time as OOP.)

Anyway, I'm just beginning the design process and thinking about 'monsters'. Each monster type (ya know, orc, goblin, rat, etc) will have its own stats, skills and what not. At first I though I could just have a single monster class and set the properties when I instantiate the object. But then I figured that might be a little inefficient, so I'm considering having a class for each type of monster.

Is this the best way to approach the problem, considering that the methods in each class will likely be the same? Is there a better way of doing things that I don't yet know about?

Any help is appreciated.

+8  A: 

You could create an abstract class, say Monster, and then extend that class for each of the different types of monsters. So

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

edit Orc would extend Monster and then inherit the attribute $health and the function attack().

Aaron Hathaway
Of course the generic monster doesn't *need* to be `abstract`. That would allow you to use a 'generic' monster.
Tim Lytle
This is a good approach and I agree with what Tim said but it is merely preference if the OP wants a generic monster or not.
Chris
@Chris - Right, wasn't saying it *shouldn't* be `abstract`, just didn't want the OP to think an extended class needed to be abstract.
Tim Lytle
Monster class should be abstract as it should only define characteristics of that creature, IMO. Take look at my post.
RobertPitt
'Orc would then extend the $health and attack class' - this is likely to confuse somebody who's trying to learn OOP. By extending `Monster`, `Orc` inherits the `$health` property and the `attack` method. Note that it might be necessary to declare `$health` `protected` instead of `private`, so derived classes (like `Orc`) can read and write it.
grossvogel
multiple inheritance is not supported, so Orc cannot extend Weapons etc
RobertPitt
@grossvogel, thank you for catching my wording. I meant that they would extend Monster and therefore inherit $health and attack(). I'll add the edit!
Aaron Hathaway
Thank you for a simple yet helpful answer. I should be able to use this effectively.
Saladin Akara
This is hardly a maintainable approach. Imagine you already have Orc, OrcShaman (can castSpell(), but has less health) and OrcChieftain (can kick() and has a bonus to health) and then decide you also want OrcShamanChieftain. You will duplicate kick() and castSpell() and the stat bonus modifier then. Then you introduce an OrcWarlord (can use kick(), warcry() and has even more health and damage bonus). And you want OrcShamanWarlord.. see where that ends? Duplicate code everywhere. It is better to utilize Decorators and Strategy Patterns and to use composite Monsters.
Gordon
@Gordon, i qoute my point: "A Entity / Monster should be built up of several class defining its characteristics" - meaning what you said: "It is better to utilize Decorators and Strategy Patterns and to use composite Monsters".
RobertPitt
+4  A: 

Classes are about behaviour, so if different types of monsters in your game behave differently, then model them as different objects. If all monsters basically share the same behaviour (e.g. all having a shared set of properties, all can attack or defend and move), then you can save yourself lots of work if you model them as a single monster class. You can always extend the monster class into specialized classes for monsters that can, e.g., talk.

xmjx
+1  A: 

OOP lets you extend classes, reusing your the code. Here's a simple example. Check out the PHP docs for Inheritance.

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';
}

And the output would be:

$rat = new Rat();
echo $rat->scare(); //output: Bo! (runs away)
echo $rat->size;    //output: small
echo $rat->color;   //output: grey
Tim Lytle
Your monster reminds me of Ali G
fletcher
@Tim Lytle - thanks for the link. Much appreciated.
Saladin Akara
how can 'class Mouse extends Rat' when a mouse is the lesser being to a rat surly.
RobertPitt
This suffers from the problem as Aaron's solution. See my comment below his answer. You really should exchange the scare behavior for a Strategy pattern to keep the Monster extensible and to prevent code duplication in the concrete Vermin implementations.
Gordon
@Gordon - 'twas just a simple example of how basic class extension works. The OP asked about having multiple classes as someone just starting OOP. Seemed like an appropriate amount of information at this point.
Tim Lytle
+7  A: 

What you should do is get some real organization into the game.

I've never built a PHP Game before but i have a pretty good idea how the structure should be.

A Entity / Monster should be built up of several class defining its characteristics

Here's an small example of the top of my head:

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
{
}

Basic layout for the abstract classes are like so:

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.
}

Then once you have your base being types you can do things like

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.
    }
}

By doing this it would not be hard to generate characteristics for entities.

You can also set up parent destructors to save the user-names data in a database for next time, and use the __construct to update the classes data. Hope this gives you a good idea :)


There's more :)

If you make classes for SpecialMoves lets say you can always do

$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.
}

Within SpecialMove_Roundhouse it would have parameters such as damage, lenght of time taken to complete, how much energy it uses, how many times you can use it.

and the very first class that in the scope should always me a calculator for things such as heartrate, blood level, energy, inventory etc so you always have the essentials!


Implements Example

Implements make sure that the higher class contains certain functions and variables

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.
      }
   }
}

Example here taken from 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"
*/

?>
RobertPitt
+1 - my answer seems so simple now, compared to a full blown monster interface library.
Tim Lytle
I think the fight is unfair ha, I wooping him :)
RobertPitt
+1 - this will act as a great reference, though seems a little more complicated than Aaron's answer. I'll definitely give it a try though. Thanks. One question - What does the 'implements' bit do?
Saladin Akara
Added small example of how Implements would work here.
RobertPitt
and one more resource while im here http://sourceforge.net/projects/phprpg/
RobertPitt
@RobertPitt - Thanks a tonne. Awesome reference!
Saladin Akara
+1  A: 

You should read about S.O.L.I.D.
These are the 5 basic principles of OOD (Object Oriented Design):

S ingle Responsibility Principle
O pen Closed Principle
L iskov Substitution Principle
I nterface Segregation Principle
D ependency Inversion Principle

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

By the way, if you want to use inheritance just to extend some properties and methods, you can do this by using Composition instead of Inheritance. Inheritance should be used to add polymorphic behaviour to you class structure.

Guilherme Oenning