質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

4回答

1058閲覧

RPG戦闘でクラスの継承を使ったまま、魔法攻撃の種類も追加したい

taitai05

総合スコア13

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2018/12/26 10:46

編集2018/12/26 12:13

初心者です。初歩的な質問ですいません。
テキストでRPGの戦闘を題材にクラスの継承を勉強中です。
出題された問題は、勇者・戦士と魔法使いでは表示される文言を変えて出すために継承を利用していました。
print(self.name+"は"+enemy+"を攻撃した") #戦士、勇者の時
print(self.name+"は"+enemy+"に魔法を唱えた") #魔法使いの時

問題はクリアしたのですが、できればついでにもう少しリアルにしたいので、
魔法使いの攻撃については、wizardクラスのattackメソッドにある「魔法」に種類を追加したいです。
「炎」
「風」
「水」
などで、「魔法使いはスライムに炎を唱えた」などと表示されるようにしたいです。

python

1class Player(): 2 def __init__(self,name,spell): 3 self.name=name 4 self.spell=spell 5 6 def attack(self,enemy): 7 print(self.name+"は"+enemy+"を攻撃した") 8 9class Wizard(Player): 10 11 def attack(self,enemy,spell): 12 print(self.name+"は"+enemy+"に"+self.spell+"を唱えた") 13 14 15hero=Player("勇者") 16warrior=Player("戦士") 17wizard=Wizard("魔法使い") 18party=[hero,warrior,wizard] 19 20for player in party: 21 if player==wizard: 22 maho=["fire","water","riot"] 23 fire=(Wizard("炎")) 24 water=(Wizard("水")) 25 riot=(Wizard("雷")) 26 for spell in maho: 27 spell.attack() 28 else: 29 player.attack("スライム")

spellの使い方でエラーが出てしまいます。
Traceback (most recent call last):
File "Main.py", line 15, in <module>
hero=Player("勇者")
TypeError: init() missing 1 required positional argument: 'spell'

どのようにspellを使えばいいのでしょうか?
上の文章だと、spellがオブジェクトになってしまっているのだと思います。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yukkuri

2018/12/26 11:19

コードはコードブロック<code>で囲んでください。 > おそらくループの順番などの問題でうまく動きません。 ということですが、どこでそう思ったか追記したほうがいいと思います。
hayataka2049

2018/12/26 11:31

https://teratail.com/tour を参考にコード部分をシンタックスハイライト・マークダウンで囲ってください。質問は再編集できます。また、「上手く動きません」の実行結果と「こう動いてほしい」という結果を併せて記載してください。
taitai05

2018/12/26 11:50

ご指摘ありがとうございます。 修正します。
taitai05

2018/12/28 11:26 編集

魔法は一種類だけが唱えられる ってのが希望です。 randomを使用して実現できました。 <code> ``` import random class Player: def __init__(self, name): self.name = name def attack(self, enemy): print(self.name+"は"+enemy+"を攻撃した") class Wizard(Player): def attack(self, enemy, spell): print(self.name+"は"+enemy+"に"+spell+"を唱えた") hero = Player("勇者") warrior = Player("戦士") wizard = Wizard("魔法使い") party = [hero, warrior, wizard] for player in party: if player == wizard: maho = ["炎", "水", "雷"] num=len(maho) num1=random.randrange(num) spell=maho[num1] wizard.attack("スライム",spell) else: player.attack("スライム") ``` </code>
yukkuri

2018/12/29 04:33 編集

すいません、私の伝え方が悪かったですね。 hayataka2049さんの言っておられますとおり、質問を編集してください。 誤解を与えてしまい、すみません。
guest

回答4

0

いろいろ、基礎がわからないまま突っ走ってらっしゃる感じがしますので、面白さを感じてもらうためにちょっと回り道した説明をさせてもらいます。

python

1class Player(): 2 def __init__(self,name,spell): 3 self.name=name 4 self.spell=spell

Playerクラスは、勇者、戦士、魔法使いに共通するクラスです。そして__init__はクラスのインスタンスが作られるとき必ず動作するメソッド(コンストラクタと言います)です。
つまりここにspellを足してしまうと、勇者と戦士も魔法を使えることにしたいという意味になってしまいます。

これは意図とは違うはずですので、__init__は変更する前の状態に戻しましょう。代わりに、spellはWizardの__init__に入れてみます。
attackメソッドもspell引数を取っていますが、attackのself.spellは__init__のとき書き込んであるので引数なくても動きます。とりあえず取っちゃいましょう。

python

1class Wizard(Player): 2 def __init__(self,name,spell): 3 self.name=name 4 self.spell=spell 5 def attack(self,enemy): 6 print(self.name+"は"+enemy+"に"+self.spell+"の呪文を唱えた")

さてWizardクラスのinitがどこで使われるかピンときてらっしゃらないようですが、ここをこう書き換えると使われるようになります。

python

1hero = Player("勇者") 2warrior = Player("戦士") 3wizard = Wizard("炎の魔法使い","炎") 4party = [hero, warrior, wizard]

ここはクラスのインスタンスを作っています。__init__が呼ばれてクラスの中身が作られます。
これで炎の魔法使いが誕生しました。

最後に、ここをシンプルにこうします。

python

1for player in party: 2 player.attack("スライム")

これを実行すると「炎の魔法使いはスライムに炎の呪文を唱えた」と表示されるようになると思います。
__init__と継承のイメージが少しは湧いたでしょうか?

ここまでやってみて納得できたようでしたら、以下のことに取り組めば、希望されるような動きをすると思います。
・Wizardの__init__でspellに渡すのを文字列ではなくて、配列にする。maho = ["炎", "水", "雷"] のmahoを渡せばいいでしょう。
・Wizardのattackの処理を変更して、spell配列の中からランダムにひとつを選び表示するようにする
・Wizardをインスタンスにするとき名前を「大魔法使い」とかに変えておくのも忘れない:)

ちなみに、この改修をすると継承の意味がなくなってしまいます。とはいえ、ちゃんとゲームにするならPlayerにdefenceとかrunとかitemとかメソッドを足す可能性は高いので、まったくの無意味でもないです。このあたりは興味があればまたご回答します。

投稿2018/12/27 12:44

mihit

総合スコア61

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

taitai05

2018/12/28 12:57

返信が大変遅くなっております。誠に申し訳ありません。 皆さまのおかげで、自分のやりたいことは達成できました。 ありがとうございました。 ですが、 >基礎がわからないまま突っ走ってらっしゃる感じがしますので、 まさにこの通りです。 ご教授頂いた内容が、自分がなんとなくうやむやなまま置いてきた部分ですので、 再復習しています。 かなり時間がかかっておりますが、まだ時間がかかりそうです。 コンストラクタの存在価値から本当は分かっていませんので。(とりあえず初期化するらしいが、とにかくインスタンス変数を利用する際に必要なものだから書いておくことというぐらいの理解です。) もう少し理解が深まったら、正式な返信をさせていただきます。 ご丁寧なご回答ありがとうございます。
mihit

2018/12/28 14:54 編集

みなさんの回答に沿ってコードが書けるところまで来ているのでしたら、もっと書いてみるほうが理解の近道になると思います。アイデア作って、仮説立てて、それに沿ってもっとコード改変して、動いても動かなくても誰かに見せましょう。 オススメの方向性としては、この例にさらに機能を増やして、内容が複雑になってくると継承やクラスやインスタンスがどういう影響を見せてくるか、追いかけてみることです。 それにちょうどいい問題を思いついたので、気が向いたらやってみてください。 ・勇者が攻撃すると、今までの「攻撃した」の代わりに「ギガブレイクでスライムを攻撃」と表示するようにしてみましょう。ただし、戦士の攻撃メッセージを変えてはいけません。また、for: player in party 以下を修正してはいけません。
guest

0

Error内容について

Traceback (most recent call last): File "Main.py", line 15, in <module> hero=Player("勇者") TypeError: init() missing 1 required positional argument: 'spell'

こちら、何とかかれているかというと、
15行目で書かれているhero=Player("勇者")の部分について、
init関数で要求されている位置変数「spell」がないよ、といわれています。

Playerクラスを見てみると、

python

1class Player(): 2 def __init__(self,name,spell):

と、インスタンス化する際に、namespellとの2つの引数を必要としているのですが、
hero=Player("勇者") では「勇者」という1つの位置引数しか渡していません。
なので、spell部分は?といわれています。

どうすればよいか

上記のエラー箇所を回避するためだけであれば、
インスタンス化する際に、hero=Player("勇者","適当な呪文")の様に指定すれば
次(のエラー)に進めると思います。
ただ、やりたいことはそうではなく、

  • 魔法使いだけ呪文が使えるようにしたい
  • 使える呪文をころころ変えたい

なのかな、と想像しましたので、
spell は Player のコンストラクタで持たせるのではなく、
Wizard の attack methodで持たせたほうがよいと思います。

python

1class Player: 2 def __init__(self, name): 3 self.name = name 4 5 def attack(self, enemy): 6 print(self.name+"は"+enemy+"を攻撃した") 7 8 9class Wizard(Player): 10 def attack(self, enemy, spell): 11 print(self.name+"は"+enemy+"に"+spell+"を唱えた") 12 13 14hero = Player("勇者") 15warrior = Player("戦士") 16wizard = Wizard("魔法使い") 17party = [hero, warrior, wizard] 18 19for player in party: 20 if player == wizard: 21 maho = ["炎", "水", "雷"] 22 for spell in maho: 23 wizard.attack("スライム", spell) 24 else: 25 player.attack("スライム")

これでとりあえず実行はできると思います。
ただ、上記だと、Wizardでattack methodをオーバライドしていることになるのですが、
引数が親クラスのPlayerと合っていません。
(これはあんまりよろしくないみたいです。)

なので、別methodとして定義してあげたほうがベターかもしれません。

python

1... 2class Wizard(Player): 3 def attack_spell(self, enemy, spell): 4 print(self.name+"は"+enemy+"に"+spell+"を唱えた") 5... 6 7 wizard.attack_spell("スライム", spell)

投稿2018/12/26 17:01

iTach

総合スコア74

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

taitai05

2018/12/28 11:23

返信遅くなり、申し訳ありません。 引数を全く理解してませんでした。 位置、キーワード、デフォルトとかいろいろあるのが理解できました。ありがとうございます。 親クラスと子クラスが同じ方がいいというのは理由は分かりませんが、 都合が悪いことが出るケースが少なからずある。 rstripを付けるのと同じような感覚でしょうか。悪さすることが多いからrstripで改行を削除すると習いました。 詳細はわかりませんが、そのように努めます。 丁寧なご回答ありがとうございました。
guest

0

コードがちゃんと読めないので憶測も多少混じりますが、

python

1class Wizard(Player): 2 def attack(self,enemy): 3 print(self.name+"は"+enemy+"に魔法を唱えた")

は仮に継承をしなかったとしたら、こういうコードになります。

python

1class Wizard: 2 def __init__(self, name): 3 self.name=name 4 5 def attack(self, enemy): 6 print(self.name+"は"+enemy+"に魔法を唱えた")

なので、Wizard("炎")などとすると炎は<敵>に魔法を唱えたってなっちゃう、ってことかな?

これの修正はゲームデザインによります。炎属性の魔法しか使えない魔法使い、みたいな設定にするなら、__init__をオーバーライドして属性を引き受けることになります。

python

1class Wizard(Player): 2 def __init__(self, name, magic_type): 3 self.name = name 4 self.magic_type = magic_type 5 def attack(self,enemy): 6 print(self.name + "は" + enemy + "に" + self.magic_type + "を唱えた")

みたいな感じですかね。インスタンス化するときはWizard("魔法使い", "炎")みたいにしてください。

あるいは、attackに追加するというのもあるでしょう。そうするといろいろな種類の魔法が使える魔法使いになれます。

python

1class Wizard(Player): 2 def attack(self, enemy, magic_type): 3 print(self.name + "は" + enemy + "に" + magic_type + "を唱えた")

ただし、attackの呼び方が変わります。なので質問文のようなループでは対応できないですね。

あるいは、「覚えてる魔法」を保持するsetでもインスタンスに持たせて、その中から相性を見てベストなのを選択するとか。やり方はいろいろあるかと。

投稿2018/12/26 11:42

編集2018/12/26 11:44
hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

taitai05

2018/12/27 04:21

いろんな種類の魔法を使える魔法使いが希望です。 説明不足で申し訳ありませんでした。 ご回答ありがとうございました!
guest

0

こういうことをやるのにはクラスの継承を使うこと自体が間違っているのでしょうか。

ということなので、オブジェクト指向的に答えます。(Pythonはほぼ無知なのでできない等ありましたら教えてもらえると嬉しいです。)

個人的には、三角です。

class Magic():
として、
効果を定義した時、その後、属性をつけた時、何を変えるでしょうか。

...どんなゲームを作るのかわかりませんが、相性程度なら自分の属性を渡して、継承しないのもありだと思います。
ただ、魔法ごとにMagicを継承したオブジェクトを作る、ということもできます。

投稿2018/12/26 11:31

yukkuri

総合スコア624

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問