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

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

新規登録して質問してみよう
ただいま回答率
85.38%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

4343閲覧

1つのAnimatorでルートモーションを切り分ける方法

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2021/07/14 15:41

編集2021/07/15 06:13

前提・実現したいこと

1つのAnimatorにおいて、アニメーションごとに、
アニメーションでの座標移動とスクリプトでの座標移動というようにルートモーションを切り分けたいのですが、
スクリプトでの座標移動の設定を試みているアニメーションで、
意図した動きにならない状態です。ご教示お願いします。

試したこと

こちらのサイトを参考にしました。

アイドル、歩行、ジャンプという簡単なステートマシンを組んでみました。

イメージ説明

スクリプトはアニメーションの切り替えのみ処理しています。
歩行アニメーションの遷移条件が、forward>0で、ジャンプアニメーションの遷移条件がトリガー型でtrueのシンプルなものです。

C#

1 void Update() 2 { 3 if (Keyboard.current.upArrowKey.isPressed) { 4 animator.SetFloat(forward, 1); 5 }else if(Keyboard.current.upArrowKey.wasReleasedThisFrame){ 6 animator.SetFloat(forward, 0); 7 } 8 if(Keyboard.current.spaceKey.wasPressedThisFrame) { 9 animator.SetTrigger(jump); 10 } 11 }

まず、Apply Root Motionをオンにして、全てのアニメーションのRoot Motion NodeをRootのままに
しておいたところ、歩行もジャンプもアニメーションのみで座標移動されることが確認できました。
ジャンプアニメーションは、アニメーションのみの座標移動にしたいので、このままにしておきます。

※また、この設定での(アニメーションのみの座標移動での)歩行アニメーションは、 アニメーションをループ再生している間、全く問題なく、ずっと歩行して前進し続けていることを確認しています。

続いて、歩行アニメーションの方をスクリプト移動させたいので、
Root Motion NodeをRoot Transformに設定します。
スクリプトは前回のものと同じままで、アニメーションの切り替えのみ行います。
想定としては、歩行アニメーションを再生した際、その場で足踏みするように、歩行の足の動きだけして、
前進はしない(座標移動しない)ことを考えていたのですが、
実際に試したところ、以下のように、前進してしまい、ループされる度に初期位置にリセットされるという、
繰り返し運動になってしまいます。
しかも、前回のアニメーションのみの座標移動では背中に担いでいる武器もキャラクターと共に移動したのですが、
今回のケースではその場に残ってしまいます。
・開始時。
イメージ説明

・再生中。
イメージ説明
歩行アニメーションの再生中、次のループに入ったタイミングで、開始時の位置に瞬時に戻ってしまいます。

また、アニメーションのアセットの構造もよくわからなく、
複数のアニメーションが以下のようにひとまとまりになっていて、その中の1つの歩行アニメーションを
Animatorにドラッグすると、ステートとして配置できるので、それを使っています。
このアニメーションがひとまとまりになっている構造はどういう仕組みとなっているのでしょうか?
さらに、このひとまとまりの中の個々のアニメーションのインスペクタでは、Motionの項目がなく、
そもそもそのインスペクタでアニメーションの設定ができません。
これより、そのひとまとまりのルートの部分のインスペクタにおいて、Motionの項目のRoot Motion NodeをRoot Transformに設定しています。
この構造だと、個々のアニメーションで個別にRoot Motion Nodeを設定できないということでしょうか?

イメージ説明

個別のインスペクタでは、以下のようにMotionの項目がなく、設定もできません。

イメージ説明

ルート部分でしか設定できませんでした。

イメージ説明

補足情報(FW/ツールのバージョンなど)

・歩行モーションで使用しているアセット。
Walk Animset Basic(Kubold)

追記

Apply Root Motionをオフにしたら、歩行アニメーションはその場で足踏みをするように、
座標移動せずに歩行の動きをするという、意図通りのアニメーションとなりました。
しかし、これでは全体のアニメーションに適用されてしまうため、ジャンプアニメーションも
座標移動しなくなってしまいます。
ジャンプアニメーションは座標移動をさせたいと思っています。
ただ、歩行アニメーションにおいて、意図通りのアニメーションが再生されるということだけわかりました。

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

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

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

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

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

guest

回答1

0

ベストアンサー

順番が前後しますが「アニメーションがひとまとまりになっている構造」については、ModelImporterがモデルファイルをインポートして、モデルに関するさまざまなオブジェクト...MeshAvatarAnimationClipといったサブアセットの集合体として整理してくれた結果と言えるかと思います。これによって、ご質問者さんがなさったように「サブアセットのAnimationClipをAnimatorウィンドウにドラッグ&ドロップして配置」みたいな操作もできるようになっているのでしょう。
サブアセットを選択しても独立して設定を編集できないのは、おそらくサブアセットはModelImporterの管理下にあって、サブアセットを単独で編集できるようにしてしまうとメインアセットのModelImporterの設定との整合性を保つのが面倒になるからじゃないか...と思いますが、詳しいところは分からないです。

「特定のアニメーションだけRoot Motion Nodeの設定を変える」ということについてですが、もしかするとメインアセットの設定だけでは無理かもしれません。ですが代替案として、ちょうど先日見かけました「移動するアニメーションの再生が終わると、元の位置に瞬間移動してしまいます。」とのご質問でも使用しました「アセット内にあるアニメーションクリップの複製」でいけるんじゃないでしょうか。

  • まず、メインアセットのRoot Motion Nodeは「<Root Transform>」にしておく。
  • プロジェクトビュー上でメインアセットの中身を展開表示し、<Root Transform>状態にしておきたいアニメーションクリップを複製する。複製物はメインアセットの外に出た単独のアセットになる。
  • オリジナルのアニメーションクリップと区別しやすくするため、必要に応じて複製物の名前を変更する。
  • メインアセットのRoot Motion Nodeを「<None>」に戻す。

といった手順でいかがでしょう。
ですが、そのようにしたアニメーションクリップを使っても、もしかすると「1ループ歩いては元の位置に瞬間移動し、また1ループ歩いて...」という挙動は変わらないかもしれません。
代替案として下記のような手順を考えてみました。うまくいけばそれでかまわないのですが、ダメだった場合にはちょっと試してみてください。

  • まずアニメーションの設定は当初のApply Root Motionオンの状態に戻す。つまりRoot Motion Nodeは「<None>」に戻して、歩行アニメーションを再生すれば前進し、ジャンプアニメーションを再生すれば上方へ跳び上がるようにしておく。
  • キャラクター用スクリプトに下記のような記述を追加する。なお、このOnAnimatorMoveの追加によってAnimatorのApply Root Motion欄は「Handled by Script」に変わり、オン・オフを操作できなくなる。

lang

1 // 効率化のため、あらかじめステート名「WalkFwdLoop」のハッシュ値を求めておく 2 static readonly int WalkFwdLoopState = Animator.StringToHash("WalkFwdLoop"); 3 4 void OnAnimatorMove() 5 { 6 // ベースレイヤーについて、現在のステートも遷移先のステートも 7 // WalkFwdLoopではない場合はルートモーション適用を行う 8 // 逆にWalkFwdLoopの時は何もしない...つまり「Apply Root Motion」が 9 // オフの時と同等の状態になるはず 10 if ((animator.GetCurrentAnimatorStateInfo(0).shortNameHash != WalkFwdLoopState) && (animator.GetNextAnimatorStateInfo(0).shortNameHash != WalkFwdLoopState)) 11 { 12 animator.ApplyBuiltinRootMotion(); 13 } 14 }

StateMachineBehaviourによるApply Root Motion切り替え

まず、当初の回答で追加したキャラクター用のスクリプトのOnAnimatorMoveは削除してしまい、再び「歩行アニメーションを再生すれば前進し、ジャンプアニメーションを再生すれば上方へ跳び上がる」という状態にしておきます。
そして、下記のようなスクリプトを作り...

lang

1using UnityEngine; 2 3public class PartialRootMotionDisabler : StateMachineBehaviour 4{ 5 public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) 6 { 7 animator.applyRootMotion = false; 8 } 9 10 public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) 11 { 12 animator.applyRootMotion = true; 13 } 14}

それをApply Root Motionオフ状態にしたいステートにアタッチします。これならいかがでしょうか。

図

投稿2021/07/15 13:16

編集2021/07/18 01:10
Bongo

総合スコア10809

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

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

退会済みユーザー

退会済みユーザー

2021/07/17 13:27

ご回答ありがとうございます。 すみません、調べたり、考えたりしてみたのですが、質問が5点ほどあります。 まず、ご回答案の2点のうち、前者の方法ではうまくいきませんでしたが、 後者のスクリプトで調整する方法で意図通りの動作となりました。 また、アニメーションがひとまとまりになっている構造についても理解できました。 ありがとうございます。 ・質問1。  Apply Root Motionオンの場合は、Root Motion Nodeを<None>に戻すことを勧められていますが、  Rootでは不具合が生じますか?  今回使用しているアニメーションアセットや別のアニメーションアセットを調べてみた所、  デフォルトでは、Root Motion NodeがRootに設定されていたため、気になりました。  リファレンス(https://docs.unity3d.com/ja/2018.4/Manual/AnimationRootMotionNodeOnImportedClips.html)を  見てみたのですが、Root Motion Nodeの<None>とRootの違いがわからず、可能であれば教えていただけませんか? ・質問2。  OnAnimatorMoveというものがわからなかったので、調べてみたのですが、  https://docs.unity3d.com/jp/current/ScriptReference/MonoBehaviour.OnAnimatorMove.html  「ルートモーションを修正するアニメーション動作を処理するコールバック」と書かれていたのですが、  今一つ、内容がわからず、  「animatorコンポーネントが有効な場合に毎フレーム呼び出されるコールバック」と思ったのですが、  その理解で合っていますか? ・質問3。  ApplyBuiltinRootMotionですが、これを実行すると、AnimatorのApply Root Motionを  オンにする処理となると思うのですが、  ご提示いただいたコードでは、歩行アニメーション中にオフにしている処理がなかったのですが、  オフにする処理は必要ないということでしょうか?  以下のようにelseでのオフにする処理が必要ではないかと思っていたのですが、  ご提示いただいたコードで正常に動作しているようでした。  そもそもApply Root Motionをオフにするメソッドが見つからなかったです。  この辺りの処理が、OnAnimatorMoveも含め、私は理解できていないかもしれません。 if ((animator.GetCurrentAnimatorStateInfo(0).shortNameHash != WalkFwdLoopState) ~省略) { animator.ApplyBuiltinRootMotion(); }else{ // Apply Root Motionをオフにする処理。 }  リファレンスは以下を確認しました。  https://docs.unity3d.com/ja/current/ScriptReference/Animator.ApplyBuiltinRootMotion.html ・質問4。  GetNextAnimatorStateInfoというものを知らなかったので調べてみたのですが、  次のステートの情報を取得するものということはリファレンスを見て分かったのですが、  単一のステートに、複数の遷移先のステートが繋がれていた場合、どのステートの情報を  取得してくるのだろうと思い、Updateメソッドでログを出力して確認したのですが、  どうやらこのGetNextAnimatorStateInfoメソッドというのは、任意のステートが再生中の場合は、  次のステートの情報は取得してこなく、トランジションを再生中の場合(次のステートが確定している場合)に、  次のステートの情報を取得してくるものということで合っていますか? ・質問5。  今回は、歩行中のアニメーションのみApply Root Motionをオフにしたかったので、  コードでのif文の条件が2つの指定で済みましたが、  Animatorを作り込んで、Apply Root Motionをオフにしたいステートが増えた場合、  if文の条件を単純に増やしていくという構造になりますか?  例えば、以下のようにif文が長くなっていってしまうということでしょうか?  if ( (animator.GetCurrentAnimatorStateInfo(0).shortNameHash != WalkFwdLoopState) && (animator.GetNextAnimatorStateInfo(0).shortNameHash != WalkFwdLoopState) (animator.GetCurrentAnimatorStateInfo(0).shortNameHash != RunFwdLoopState) && (animator.GetNextAnimatorStateInfo(0).shortNameHash != RunFwdLoopState) (animator.GetCurrentAnimatorStateInfo(0).shortNameHash != DamageBackLoopState) && (animator.GetNextAnimatorStateInfo(0).shortNameHash != DamageBackLoopState))  もちろん、これでも処理が実装できて問題ないのですが、何かコードを短くする方法が存在すれば、  教えていただきたいです。 理解が及ばず、たくさんの質問をしてしまいすみません。
Bongo

2021/07/18 01:11

質問1について ご提示のマニュアル中に「インポートしたアニメーションの階層内の任意のノード (Transform) を選択し、ルートモーションソースとして使用することができます。ルートモーションソースのアニメーション化された位置と回転は、ゲームオブジェクトのアニメーション再生時にその位置と回転を制御します。」との文章があります。「ルートモーションソース」という表現から考えますに、AnimatorのApply Root Motionがオンの時には、アニメーション再生時にRoot Motion Nodeで選択したボーンの動きがキャラクターの位置・回転に反映されるのだろうと思います。 選択肢の中の「<Root Transform>」というのは、アニメーションのモデル内のボーン階層のうち最上位のものを指すのでしょう。キャラクターのスケルトンの作りはモデルによってさまざまだと思いますが、スケルトンの最上位ボーンの名前はたとえば「Hips」なんて名前だったりするかと思います。ですが<Root Transform>はそれよりさらに1階層上の、いわばモデル自体のローカル空間を定義する目に見えないボーンみたいなものでしょうかね。 「<None>」の場合は、そもそもルートモーションソースとしてモデル内のどのボーンも(<Root Transform>も含めて)使用しないことを表すのだろうと思います。<None>に設定した場合、インスペクターのちょっと上の方に表示されているアニメーションクリップのインポート設定の項目が増えて「Root Transform Rotation」、「Root Transform Position (Y)」、「Root Transform Position (XZ)」といったものが出現するかと思います。どのボーンもルートモーションソースとして使わない代わりに、この追加表示された設定内容に従ってルートの動きを計算するのでしょう。つまり、<None>の時にはhttps://docs.unity3d.com/ja/current/Manual/RootMotion.html で解説されているプロセスによりルートモーションが決まるのだろうと思います。 <Root Transform>にすると不具合が生じるか...という点については、これはモデルによるんじゃないでしょうかね? あいにく私はWalk Animset Basicは未購入でして、代用品としてBasic Motions FREE(https://assetstore.unity.com/packages/3d/animations/basic-motions-free-154271?locale=ja-JP )を使って挙動の確認をしたのですが、これの歩行モーションの場合は<Root Transform>に設定するとインスペクターの少し上の枠内に表示されている平均速度・平均Y軸周り回転速度が... Average Velocity: (0.000, 0.000, 0.000) Average Angular Y Speed: 0.0 deg/s と表示されました。つまりこのモデルは自分自身のルートそのもの(<Root Transform>)を移動させるようなアニメーションにはなっておらず、この状態ではたとえApply Root Motionがオンであっても、キャラクターオブジェクトに適用されるモーションが移動なし・回転なしということなので、キャラクターは前進しないわけです(ただしキャラクターオブジェクト自体の座標は変化しないものの、子階層のボーンは歩行アニメーションによって前進するので、全体としては見た目は前進しているかのように見えます...この状態では「1ループごとに初期位置へ瞬間移動」現象が起こってしまうでしょう)。 手持ちのモデルを探しただけではちょっと実例を見つけられなかったのですが、おそらく<Root Transform>を移動させるように作られたアニメーションであれば、Root Motion Nodeを<Root Transform>に設定した場合でもキャラクターオブジェクト自体が移動するだろうと思います。ご質問者さんがご使用のモデルの場合、<Root Transform>に設定しても変な挙動(特に初期位置瞬間移動現象)が起こらないのであれば、<Root Transform>でもいっこうにかまわないと思います。 質問2について はい、おっしゃるような認識で問題ないかと思います。あえて申し上げるとすれば、「毎フレーム」とは限らないかもしれませんね。イベント関数の実行順序(https://docs.unity3d.com/ja/current/Manual/ExecutionOrder.html )の図をご覧いただきますと、Game logicのセクションにOnAnimatorMoveがありますが、これは確かに1フレームに1回となるだろうと思います。ですがPhysicsセクションにももう一つOnAnimatorMoveがあり、これは物理シミュレーション更新と同じ周期のはずですので、1フレーム中の実行回数が変動しうるでしょう(まあ、物理シミュレーションフレームという観点では同じく「1フレームに1回」であると言えるでしょうね)。AnimatorのUpdate Modeを「Animate Physics」に設定した場合は後者のタイミングで実行されるかと思います。 質問3について いえ、ApplyBuiltinRootMotionを実行すると、その時だけ単発でキャラクターオブジェクトにルートモーションが適用されるはずです。ですのでオンとかオフといった概念はないでしょうね。 言ってみればApply Root Motionがオンの状態というのは、OnAnimatorMoveで毎回無条件でApplyBuiltinRootMotionを実行しているようなものでしょう。今回例示しましたスクリプトの場合ですと、条件付きでApplyBuiltinRootMotionを実行していることになるわけです。 質問4について そうですね。実際の実装はネイティブコードになっているようで分からなかったのですが、どうやらそのような挙動をするようでしたので、それを前提に例示のコードを書きました。 質問5について 確かにそれは面倒ですね。条件部分は同じパターンの繰り返しですのでメソッド化すれば楽になるかと思うのですが、StateMachineBehaviourを使ってApply Root Motionを切り替えてもいいんじゃないかと思いなおしました。回答に追記しましたが、ああした方がシンプルでよかったかもしれませんね。
退会済みユーザー

退会済みユーザー

2021/07/18 09:53 編集

ご回答ありがとうございます。 ・質問1について。  すみません、私の理解力が悪く、確認させていただきたいのですが、  まず、Root Motion Nodeの設定項目には、 ・<None> ・<Root Transform> ・Root  のように、<Root Transform>とは別にRootもあり、  「Apply Root Motionオンの場合は、Rootでは不具合が生じますか?」という質問だったのですが、  ご教示いただいたご回答では、「<Root Transform>でもかまわない」と言及されていて、  その辺りで私の理解で追いついておらず、すみません。  ちょっと質問の角度を変えまして、  個別にスクリプトで動きを制御したい場合は、Apply Root Motionをオンにして、  アニメーションクリップで個別にスクリプトで制御したいアニメーションクリップに、  <Root Transform>を設定するかと思うのですが、  アニメーションで座標移動させたいアニメーションは、  Apply Root Motionオンにしておいて、Root Motion Nodeをどれに設定するのが適切でしょうか?  私の認識では、アニメーションクリップのデフォルトで設定されているRootが適切なのかなと  思っていました。  ただ、Bongo様の最初のご回答では、Noneに設定すべきというように見受けられました。  汎用的にどれが適切というのはなく、アニメーション次第だったりしますか?  (<Root Transform>はボーンの最上位のものを指し、<None>はどのボーンも使用しないという   ご教示もいただいたのですが、アニメーションで座標移動させたいアニメーションに何が適切なのか   理解できず、すみません。) ・質問2について。  ご回答ありがとうございます。フレームに関する厳密なご教示もしていただき、  ありがとうございます。理解しました。 ・質問3について。  すみません、私の理解力が悪く、確認させていただきたいのですが、  こちら、ご提示いただいたコードにおいて、  以下のように、空のOnAnimatorMoveを定義して確認してみました。  void OnAnimatorMove(){ }  すると、歩行もジャンプも座標移動しない、つまり、  Apply Root Motionがオフの状態の挙動のように見えました。  つまり、スクリプトでOnAnimatorMoveメソッドを定義するだけで、  Animatorのインスペクタの Apply Root Motionは、  [Handled by Script]という表示になってスクリプトで制御されるようになり、  OnAnimatorMoveメソッドの中で何も処理をしないと、毎回無条件でApply Root Motionが  オフの状態に設定され、  OnAnimatorMoveメソッドの中でApplyBuiltinRootMotionメソッドを実行する処理を書くと、  OnAnimatorMoveメソッドの中で、このApplyBuiltinRootMotionメソッドが実行された瞬間だけ、  Apply Root Motionがオンの状態に設定され、  OnAnimatorMoveメソッドが呼ばれても、ApplyBuiltinRootMotionメソッドが実行されなければ、  Apply Root Motionがオフの状態に設定される、という理解で合っていますか?  また、OnAnimatorMoveメソッドを定義しているスクリプトは、Animatorコンポーネントが  アタッチしているゲームオブジェクトにアタッチしなければいけないみたいですね  (検証して確認しました)。 ・質問4について。  ご回答ありがとうございます。理解できました。 ・質問5について。  StateMachineBehaviourのコードのご教示ありがとうございます。  検証して正常に動作することを確認しました。  適用したいステートごとに、同じStateMachineBehaviourのファイルをアタッチすれば  よいわけですね。 すみません、あともう1点気になってしまったのですが、 最初にご回答いただいコードで、ステートID(ハッシュ値)を取得する際にstaticを付けている理由は 何でしょうか?staticを外してみても、正常に動いているように見えました。 また、public staticとした場合は、外部スクリプトからもアクセスできるようにするためかなと理解できるのですが、publicはついていないようですので、気になりました。
Bongo

2021/07/18 12:10

質問1について なるほど、誤解しておりました。てっきり<Root Transform>のことをRootとおっしゃっているのかと思い込んでしまいましたが、Rootという名前のボーンもあるのですね。そのRootは先のコメントで申し上げたスケルトンの最上位(コメント中の例ではHips)に相当するもののはずです。Rootを選択した場合でも、やはり動作上問題なければそれで大丈夫かと思います。 質問3について はい、そういうことですね。揚げ足取り的ですみませんが、applyRootMotionのtrue、falseが切り替わるわけではないため「Apply Root Motionがオフの状態に設定され」、「Apply Root Motionがオンの状態に設定され」という表現は「Apply Root Motionがオフの状態と同等になり」、「Apply Root Motionがオンの状態と同等になり」の方が妥当かもしれませんね。ですがご質問者さんのおっしゃりたいことは伝わりますので問題ないかと思います。 追加の質問について staticを付けなくても動作上は問題ないはずです。私がstaticを付けたのは「ハッシュ値は引数として与えたステート名文字列だけに依存するので、いつどこのスクリプトで実行しても同一の結果になるはずだから(参照透過的である(https://ja.wikipedia.org/wiki/%E5%8F%82%E7%85%A7%E9%80%8F%E9%81%8E%E6%80%A7 )とかいうやつでしょうかね)、staticなしのインスタンスフィールドとしてわざわざインスタンス毎に個別にメモリを用意しなくても、全インスタンス共通のstaticフィールドとして持っておけば十分だろう」と思ってのことです。ですが、今回のケースではint型1個ぽっちですし、このキャラクター用スクリプトも何千、何万個もアタッチするなんてことはしないでしょうから、staticを付けなくてもかまいません。
退会済みユーザー

退会済みユーザー

2021/07/18 14:41

ご回答ありがとうございます。 ・質問1について。  Rootを選択した場合でも、動作上問題なければそれで大丈夫とのことで理解しました。  ありがとうございます。 ・質問3について。  いえ、正確な表現をご教示いただきありがとうございます。  理解できました、ありがとうございます。 ・追加の質問について。  なるほど、staticの使い所として、  インスタンス毎に個別にメモリを用意しなくても、  全インスタンス共通のフィールドとして持たせて大丈夫なものは、staticを付けるとよい、  ということですね。理解できました、ありがとうございます。 全ての疑問が解決しました。 ご丁寧かつ詳細なご教示、たくさんの質問にご回答いただき、大変ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.38%

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

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

質問する

関連した質問