【勉強日記】クラスを使ってRPGを作ろう②【第18回】

プログラミング

はじめに

今回の記事では、クラスやオブジェクト指向を使って簡単なRPGをつくる演習を引き続き行います。ポイントを解説しながら、数回にわたって記事を掲載していきます。つまずきやすい部分(実際に筆者がつまずいた部分)を中心に、初心者にもわかりやすく順を追って説明していきます。

もくじ

  • はじめに
  • もくじ
  • 今回やること・・・各キャラクタークラスをつくる!
    • 各キャラクタークラスを作成 → main関数を作り、そこで動かしていく
  • プレイヤークラスをつくろう
  • モンスタークラスをつくろう
  • 継承で魔法使いクラス、騎士クラスをつくろう
  • 作成したプログラム
  • まとめ

各キャラクタークラスを作っていく

前回のブログでは、これから作成していくRPGプログラムの基本仕様を決定し、プログラム全体の流れをおおまかに作成しました。このRPGプログラムでは、オブジェクト指向の基本である「クラス」「インスタンス」や、「継承」「ポリモーフィズム」の考え方をもとにコードを書いていきます。

☆各キャラクタークラスを作成後 → main関数でインスタンスを作成し、キャラクターを動かしていく

RPGプログラムでは、各キャラクターのクラスを作成しておき、mainメソッドでインスタンスを用いてキャラクターを動かすプログラムを書いていきます。今回はキャラクターのクラスをひとつずつ作成していきます。作るクラスは以下です。

  • プレイヤークラス
  • エネミークラス
  • 魔法使いクラス(プレイヤークラスを継承してつくる)
  • 騎士クラス(プレイヤークラスを継承してつくる)

プレイヤークラスをつくろう

早速プレイヤークラスを作成していきましょう!

プレイヤーが持っているステータスは名前とHP(体力)、取ることができる行動は攻撃と防御です。よって、ステータスはインスタンス変数、行動はメソッドとして記述します。

プレイヤー

名前 自由に入力できる

HP(体力) 基本は HP=60

攻撃  モンスターに10ダメージを与える

防御 防御するとそのターン、モンスターの攻撃を受けなくなる

まずはインスタンス変数から定義していきます。名前やHPは初期値としてコンストラクタで設定していきます。プレイヤーの名前はRPG開始時に好きな名前に設定できるので、外部からの値を入力できるようにコンストラクタの引数に設定します。体力はどんなプレイヤーもHP=60で固定のため、そのまま記述します。

class Player():  # プレイヤークラス

    def __init__(self, name): # コンストラクタ(初期値の設定)
        self.name = name # 名前は自由に設定できるため、引数を追加する
        self.HP = 60

つぎに攻撃と防御メソッドを書いていきます。

プレイヤーが攻撃した場合、モンスターにダメージを与えます(基本のダメージは10とします)。

攻撃メソッドでは、呼び出し先でダメージ計算ができるように、与えたダメージを返り値として返すようにします。ここでダメージの変数が登場しますが、「プレイヤーが与えるダメージ」として、これもインスタンス変数に設定しておきます。

プレイヤーが防御した場合、相手から受けるダメージをゼロにすることができます。

プレイヤーが防御をしている・していないによってモンスターの攻撃を受けるか受けないか分岐させるため、2択の状態判定ができるように「is_defenced」というインスタンス変数を追加してみます。防御メソッドでは防御状態になるので、trueに変更しておきます。

class Player():  # プレイヤークラス

    def __init__(self, name):
        self.name = name
        self.HP = 60
        self.isdefenced = False # 防御状態かどうかを確認するために追加


    def attack(self):
        self.isdefenced = False # 攻撃時は防御していないのでFalse
        self.damage = 10
      
        return self.damage

    def defence(self):
        self.isdefenced = True  # 防御時のみTrue
    self.HP = player_HP

        return self.HP #防御後のHPを返す  

エネミー(敵)クラスをつくる

つぎにエネミークラスを作成していきます。モンスターは3種類で、それぞれ体力が20,30,60あるものがランダムで出現します。また、攻撃するときのダメージは0,10,20,30のいずれかになります。モンスターに防御行動はありません。

モンスター

HP 20,30,60のどれか それぞれスライム・デビル・ドラゴン とする

・攻撃 10%の確率で 30ダメージ

   20%の確率で 20ダメージ

   50%の確率で 10ダメージ

   20%の確率で 0ダメージ (攻撃が外れる)

モンスターはそれぞれスライム=20,デビル=30,ドラゴン=60のHPをもっており、インスタンス作成時(モンスター出現時)にランダムで決まるものとします。よって、これらの情報が入った辞書を作成し、ランダムで抽出するようにします。

class Enemy():  # エネミークラス

    def __init__(self):
    # モンスター3種類の情報を辞書型で持っておく
        enemy_type = {'スライム': 20, 'デビル': 30, 'ドラゴン': 60}
    # ランダムで3種類いずれかのモンスターが選ばれる
        self.monster = random.choice(list(enemy_type.items()))

ランダムに選ぶメソッドを使用したいので、ランダムモジュールをコードの一番上に書いておきます。

import random  # 一番上に書く

つぎに、これらのモンスターは攻撃では毎ターン0,10,20,30のいずれかのダメージをプレイヤーに与えますが、それぞれ確率が決まっています。そこでモンスターに「運」要素をもたせ、それが~%だった場合に××ダメージを与える、というように条件分岐をさせるようにします。

class Enemy():  # エネミークラス

    def __init__(self):
        enemy_type = {'スライム': 20, 'デビル': 30, 'ドラゴン': 60}
        self.monster = random.choice(list(enemy_type.items()))

    def attack(self):
        enemy_attack_damage = [0, 10, 20, 30] # ランダムで選ばれるダメージのリスト
        self.probability = random.random() # 0から1までの小数点のうち1つが選ばれる
        monster_name = self.monster[0]

        if self.probability > 0.9: # 10%の確率でプレイヤーに30ダメージ
            print(f'{monster_name}の攻撃が急所に当たった!')
            self.damage = enemy_attack_damage[3]

            return self.damage

        elif 0.9 >= self.probability > 0.7: # 20%の確率でプレイヤーに20ダメージ
            self.damage = enemy_attack_damage[2]

            return self.damage

        elif 0.7 >= self.probability > 0.2: # 50%の確率でプレイヤーに10ダメージ
            self.damage = enemy_attack_damage[1]

            return self.damage

        else: # 20%の確率でプレイヤーに0ダメージ
            self.damage = enemy_attack_damage[0] 
            print(f'{monster_name}の攻撃は外れた!')

            return self.damage

継承で魔法使いクラス、騎士クラスをつくる

最後に、プレイヤークラスを継承して、魔法使いクラスや騎士クラスを作成していきます。そのさいにはメソッドを上書きして別の処理に変更していきます。

☆オーバーライドしたいもの

魔法使いクラス・・・攻撃が魔法攻撃になり、80%の確率で攻撃が当たる 防御するとHPが5回復する

騎士クラス・・・ 攻撃が斬撃攻撃になり、20%の確率で攻撃ダメージが増える(20ダメージ)が、20%の確率で攻撃が外れる

継承してクラスを作成するため、変更する部分や追加する部分だけを記述していきます。

まずは魔法使いクラスから作成していきます。魔法使いの攻撃は20%の確率で外れるという運要素があります。魔法使いは90%の確率で攻撃が外れるので、if文で追加していきます。

class Caster(Player):  # 魔法使いクラス

    def attack(self): # 攻撃を魔法攻撃にオーバーライド
        self.damage = 10
        print(f'{self.name}は魔法をくりだした!')  
        self.is_defenced = False
        self.probability = random.random()

        if self.probability >= 0.8:  # 20%の確率で攻撃が外れる
            print('攻撃が外れてしまった!')
            self.damage = 0
            return self.damage

        else:

            return self.damage

    def defence(self, player_HP): # 防御を回復仕様にオーバーライド
        self.is_defenced = True
        self.HP = player_HP + 5

        return self.HP

魔法使いは防御するとHPが5回復するので、防御メソッドもオーバーライドしていきます。

以上で魔法使いクラスの記述は完了です。

騎士クラスは20%の確率で攻撃が20になり、20%の確率で攻撃が外れます。これもエネミークラスと同様にコードを書いていきます。

class Knight(Player):  # 騎士クラス

    def attack(self):
        self.damage = 10
        print(f'{self.name}は斬撃をくりだした!')  # 攻撃を斬撃攻撃にオーバーライド
        self.is_defenced = False
        self.probability = random.random()

        if self.probability >= 0.8:  # 20%の確率で急所、20%の確率で攻撃が外れる
            print('急所に当たった!')
            self.damage = self.damage + 10

            return self.damage

        elif 0.8 > self.probability > 0.2:

            return self.damage

        else:
            print('攻撃が外れてしまった!')
            self.damage = 0

            return self.damage

騎士クラスは防御メソッドに変更はないため記述しません。

クラスの作成は以上になります。

作成したクラスのコードまとめ

import random


class Player():  # プレイヤークラス

    def __init__(self, name):
        self.name = name
        self.HP = 60
        self.is_defenced = False

    def attack(self):
        self.is_defenced = False
        self.damage = 10

        return self.damage

    def defence(self, player_HP):
        self.is_defenced = True
        self.HP = player_HP

        return self.HP


class Caster(Player):  # 魔法使いクラス

    def attack(self):
        self.damage = 10
        print(f'{self.name}は魔法をくりだした!')  # 攻撃を魔法攻撃にオーバーライド
        self.is_defenced = False
        self.probability = random.random()

        if self.probability >= 0.8:  # 20%の確率で攻撃が外れる
            print('攻撃が外れてしまった!')
            self.damage = 0
            return self.damage

        else:

            return self.damage

    def defence(self, player_HP):
        self.is_defenced = True
        self.HP = player_HP + 5

        return self.HP


class Knight(Player):  # 騎士クラス

    def attack(self):
        self.damage = 10
        print(f'{self.name}は斬撃をくりだした!')  # 攻撃を斬撃攻撃にオーバーライド
        self.is_defenced = False
        self.probability = random.random()

        if self.probability >= 0.8:  # 20%の確率で急所、20%の確率で攻撃が外れる
            print('急所に当たった!')
            self.damage = self.damage + 10

            return self.damage

        elif 0.8 > self.probability > 0.2:

            return self.damage

        else:
            print('攻撃が外れてしまった!')
            self.damage = 0

            return self.damage


class Enemy():  # エネミークラス

    def __init__(self):
        enemy_type = {'スライム': 20, 'デビル': 30, 'ドラゴン': 60}
        self.monster = random.choice(list(enemy_type.items()))

    def attack(self):
        enemy_attack_damage = [0, 10, 20, 30]
        self.probability = random.random()
        monster_name = self.monster[0]

        if self.probability > 0.9:
            print(f'{monster_name}の攻撃が急所に当たった!')
            self.damage = enemy_attack_damage[3]

            return self.damage

        elif 0.9 >= self.probability > 0.7:
            self.damage = enemy_attack_damage[2]

            return self.damage

        elif 0.7 >= self.probability > 0.2:
            self.damage = enemy_attack_damage[1]

            return self.damage

        else:
            self.damage = enemy_attack_damage[0]
            print(f'{monster_name}の攻撃は外れた!')

            return self.damage

まとめ

クラスを作成していくことで、実際に動かすさいのプログラムでメソッドをわかりやすく記述していくことができます。次回は、実際のメインプログラムのなかで作成したクラスのメソッドを呼び出してゲームを進めていく部分を記述していきます。

ここまで読んでくださってありがとうございました!

コメント

タイトルとURLをコピーしました