いつ固定または可変の時間ステップを使用する必要がありますか?


256

ゲームループは固定または可変の時間ステップに基づいている必要がありますか?常に優れているのですか、それとも正しい選択はゲームによって異なりますか?

可変時間ステップ

物理の更新には「最後の更新からの経過時間」引数が渡されるため、フレームレートに依存します。これは、計算を行うことを意味する場合がありposition += distancePerSecond * timeElapsedます。

長所:スムーズで、コーディングが簡単
短所:非決定的で、非常に小さいまたは大きなステップで予測できない

deWiTTERSの例:

while( game_is_running ) {
    prev_frame_tick = curr_frame_tick;
    curr_frame_tick = GetTickCount();
    update( curr_frame_tick - prev_frame_tick );
    render();
}

固定タイムステップ

更新は、各更新が一定期間のものであると想定するため、「経過時間」を受け入れない場合もあります。計算はとして行うことができますposition += distancePerUpdate。この例には、レンダリング中の補間が含まれています。

長所:予測可能、決定論的(ネットワーク同期が容易?)、より明確な計算コード
短所:v-syncを監視するために同期されない(補間しない限りグラフィックが不安定になる)、最大フレームレートが制限される(補間しない限り)、フレームワーク内で動作しにくい可変時間ステップ(PygletFlixelなど)を想定

deWiTTERSの例:

while( game_is_running ) {
    while( GetTickCount() > next_game_tick ) {
        update();
        next_game_tick += SKIP_TICKS;
    }
    interpolation = float( GetTickCount() + SKIP_TICKS - next_game_tick )
                    / float( SKIP_TICKS );
    render( interpolation );
}

いくつかのリソース


6
ゲームに可変タイムステップを使用し、物理学に固定ステップを使用する
ダニエルリトル

7
可変タイムステップは、固定タイムステップでは「すべての計算をtimeElapsed変数と混同する必要がない」ため、コーディングが簡単だとは言いません。それはそれほど難しいことではありませんが、私はプロとして「コーディングが簡単」を追加しません。
ペック

確かに、可変時間ステップを補間する必要がない方法について言及していたと思います。
ニックゾンネフェルト

@pekあなたに同意します。可変時間ステップには、より単純なコード化されたゲームループがありますが、その変動に対処するためにエンティティにコードを追加して、「ペースを合わせる」必要があります。固定タイムステップは、ゲームループのコーディングがより複雑です(時間近似の変動を正確に補正し、追加する追加遅延またはスキップする更新の数を再計算して修正を維持する必要があるため)が、エンティティのコーディングは単純になります常に同じ時間間隔に対処する必要があります。全体として、どのアプローチも他のアプローチよりも明らかに単純ではありません。
シヴァンドラゴン

あなたはこのツールからこれらのvisuallを確認することができます。 s3.amazonaws.com/picobots/assets/unity/jerky-motion/...それはあなたのフレームレートが変化したとき、彼らがどのように見えるかについての考えを与えるものではありませんが
バディ

回答:


134

質問に関連する2つの問題があります。

  • 物理学のステップレートをフレームレートに関連付ける必要がありますか?
  • 物理学は一定のデルタでステップする必要がありますか?

グレンフィールダーのFix your time stepで、彼は「Free the Physics」と言います。つまり、物理更新レートはフレームレートに関連付けられてなりませ

たとえば、ディスプレイのフレームレートが50fpsで、シミュレーションが100fpsで実行されるように設計されている場合、ディスプレイを更新するたびに2つの物理ステップを実行して物理を同期させる必要があります。

Erin CattoによるBox2Dの推奨事項では、彼もこれを支持しています。

したがって、タイムステップをフレームレートに結び付けないでください(本当に、本当にそうする必要がある場合を除きます)。

物理学のステップレートをフレームレートに関連付ける必要がありますか?番号。


固定ステップと可変ステップに関するエリンの考え:

Box2Dは、積分器と呼ばれる計算アルゴリズムを使用します。積分器は、離散的な時点で物理方程式をシミュレートします。...また、時間ステップが大きく変わるのも好きではありません。可変時間ステップは可変結果を生成するため、デバッグが困難になります。

固定ステップと可変ステップに関するグレンの考え:

タイムステップを修正するか爆発する

...車のシミュレーションでショックアブソーバに一連の非常に硬いばねの制約がある場合、dtのわずかな変更によって実際にシミュレーションが爆発する可能性があります。...

物理学は一定のデルタでステップする必要がありますか?はい。


物理的な更新レートをフレームレートに固定せずに、一定のデルタで物理をステップさせる方法は、時間アキュムレータを使用することです。私のゲームでは、さらに一歩踏み込んでいます。着信時間に平滑化機能を適用します。このように、大きなFPSスパイクは物理学を過度にジャンプさせず、代わりに1フレームまたは2フレームでより迅速にシミュレートされます。

固定レートでは、物理はディスプレイと同期しません。これは、ターゲット物理レートがターゲットフレームレートに近い場合に当てはまります。フレームレートが物理レートよりも大きいのは悪いことです。一般に、余裕がある場合は、ターゲットFPSの2倍の物理更新レートをターゲットにした方が良いでしょう。

物理的な更新レートを大きくする余裕がない場合は、フレーム間のグラフィックの位置を補間して、物理が実際に移動するよりも描画されたグラフィックがよりスムーズに移動するように検討してください。


1
私はマシンをアップグレードする前後にThe Floor is Jellyをプレイしましたが、それは愚かでした:物理学は実際にゲームループから呼び出されたため(そしてフレームレートに関連付けられていたため)同じことではありませんでしたタイマー。私の古いマシンは非常に悪かったので、スローモーションと速すぎるモーションを絶えず切り替え、ゲームプレイに大きな影響を与えました。今では非常に速い動きです。とにかくそのゲームは、この問題がどれほど問題になり得るかを示す良い例です(それでもかわいいゲームです)。
MasterMastic 14

55

本当に3つのオプションがあると思いますが、あなたはそれらを2つだけとしてリストしています:

オプション1

何もしない。特定の間隔(たとえば、1秒間に60回)で更新およびレンダリングを試みます。それが遅れる場合、それを聞かせて、心配しないでください。CPUがゲームに追いついていない場合、ゲームはぎくしゃくしたスローモーションになります。このオプションは、リアルタイムのマルチユーザーゲームではまったく機能しませんが、シングルプレーヤーゲームでは問題なく、多くのゲームで正常に使用されています。

オプション2

各更新間のデルタ時間を使用して、オブジェクトの動きを変化させます。特にゲーム内で加速も減速もせず、一定の速度で移動する場合は、理論的には素晴らしいです。実際には、多くの開発者はこれを不適切に実装しているため、衝突検出と物理学の一貫性が失われる可能性があります。一部の開発者は、この方法はそれよりも簡単だと考えているようです。このオプションを使用する場合は、ゲームを大幅に強化し、たとえば大部分の人が使用する標準オイラーではなくVerlet物理インテグレーターを使用して衝突検出に光線を使用するなど、いくつかの大きな銃の数学とアルゴリズムを引き出す必要があります単純なピタゴラス距離チェックではなく。しばらく前にStack Overflowでこれについて質問し、いくつかの素晴らしい回答を得ました。

https://stackoverflow.com/questions/153507/calculate-the-position-of-an-accelerating-body-after-a-certain-time

オプション3

Gafferの「タイムステップを修正する」アプローチを使用します。オプション1のように固定ステップでゲームを更新しますが、経過時間に基づいてレンダリングされたフレームごとに複数回更新します。これにより、個別のステップのままでゲームのロジックがリアルタイムに追いつきます。このように、オイラーインテグレーターのような簡単に実装できるゲームロジックと単純な衝突検出は引き続き機能します。また、デルタ時間に基づいてグラフィカルアニメーションを補間するオプションもありますが、これは視覚効果専用であり、コアゲームロジックに影響を与えるものではありません。アップデートが非常に集中的な場合、潜在的にトラブルに巻き込まれる可能性があります-アップデートが遅れると、追いつくためにより多くのアップデートが必要になり、ゲームの応答性がさらに低下する可能性があります。

個人的には、オプション1が好きで、それを回避することができ、オプション3をリアルタイムに同期する必要があるときが好きです。あなたが何をしているのかを知っているときは、オプション2が良いオプションになり得ることを尊重します。


オプション2に関して:ピタゴラスのアプリケーションでブルートフォースが非常に強い場合を除き、レイキャストがピタゴラスの距離チェックよりも速くなることはありませんが、ブロードフェーズを追加しないとレイキャストも非常に高価になります。
カイ

4
等しくない時間ステップでVerletを使用する場合、お風呂で赤ちゃんを捨てます。Verletが安定しているのは、エラーが後続のタイムステップでキャンセルされるためです。時間ステップが等しくない場合、これは発生せず、爆発する物理領域に戻ります。
drxzcl

オプション3-Joel MartinezのXNAアプローチの説明のように聞こえます。
右上

1
これは本当に良い答えです。3つのオプションにはすべて場所があり、適切なタイミングを理解することが重要です。
アダムネイラー14

私が取り組んだすべてのMMO(EQ、Landmark / EQNext [cry]、PS2 [briefly]、およびESO)では、常に可変フレーム時間を使用しています。私はその特定の決定の当事者ではなく、事実の後に現れ、他の人が決定したことを利用しました。
マークストアー

25

XNA Frameworkが固定タイムステップを実装する方法が本当に気に入っています。所定の描画呼び出しに少し時間がかかりすぎると、「追いつく」まで繰り返しupdateを呼び出します。Shawn Hargreavesがここで説明しています:http :
//blogs.msdn.com/b/shawnhar/archive/2007/11/23/game-timing-in-xna-game-studio-2-0.aspx

2.0では、描画動作が変更されました。

  • 現在の時間に追いつくために必要な回数だけUpdateを呼び出します
  • Drawを1回呼び出す
  • 次のアップデートの時間になるまで待ちます

これに対する私の意見の最大のプロは、あなたが言及したものです。それは、その時間変数をあちこちに含める必要がないので、あなたのゲームコード計算のすべてをとても簡単にします。

注:xnaは可変タイムステップもサポートしています。これは単なる設定です。


これは、ゲームループを実行する最も一般的な方法です。ただし、モバイルデバイスで作業する場合、バッテリの寿命にはあまり適していません。
knight666

1
@ knight666; より長いタイムステップを使用して、反復回数を減らすことでバッテリー寿命を節約することを提案していますか?
ファルストロ

これは依然として変数の更新です。更新デルタは、フレームが一定の値(つまり、1/30秒)ではなくレンダリングにかかった時間に基づいて変化します。
デニスマンシー

1
@デニス:私が理解しているように、更新関数は固定デルタで呼び出されます
...-RCIX

3
@ knight666 Uh-どうやってそれを理解しますか?vsyncがオンになっていて、どもっていない場合-これらの方法は同じである必要があります!また、vsyncをオフにしている場合、必要以上に頻繁に更新しており、おそらくアイドル状態にしないことでCPU(したがってバッテリー)を浪費しています!
アンドリューラッセル

12

別のオプションがあります-ゲームの更新と物理の更新を分離します。ゲームのタイムステップに合わせて物理エンジンを調整しようとすると、タイムステップを修正すると問題が発生します(統合にはより多くのタイムステップが必要なため、より多くのタイムステップを必要とするため、制御不能になるという問題)、または可変にして物理的に不安定になります。

私がよく見かける解決策は、異なるスレッドで(異なるコアで)固定タイムステップで物理学を実行させることです。ゲームは、取得できる最新の2つの有効なフレームを指定して、補間または外挿します。内挿は遅延を追加し、外挿は不確実性を追加しますが、物理学は安定し、タイムステップを制御不能にしません。

これは実装するのは簡単ではありませんが、将来の証明になる可能性があります。


8

個人的には、可変時間ステップのバリエーションを使用します(これは、固定と可変のハイブリッドの一種です)。このタイミングシステムをいくつかの方法でストレステストし、多くのプロジェクトで使用していることに気付きました。私はすべてにそれをお勧めしますか?おそらくない。

私のゲームループは、更新するフレームの量を計算し(これをFと呼びます)、F個の個別のロジック更新を実行します。すべてのロジックの更新は、一定の時間単位(ゲームでは1/100秒であることが多い)を前提としています。すべてのF個の個別の論理更新が実行されるまで、各更新は順番に実行されます。

論理ステップで個別に更新するのはなぜですか?連続ステップを使用しようとすると、計算された速度と移動距離に膨大な値のFが乗算されるため、突然物理的な不具合が発生します。

これをうまく実装しないと、F =現在の時間-最後のフレーム時間の更新が行われます。ただし、計算があまりにも遅れると(CPU時間を盗む別のプロセスのように制御できない状況が原因で発生する場合があります)、すぐにひどいスキップが発生します。すぐに、維持しようとした安定したFPSがSPFになります。

私のゲームでは、「スムーズ」なスローダウンを許可して、2つのドロー間で可能なロジックキャッチアップの量を制限しています。クランプすることでこれを行います:F = min(F、MAX_FRAME_DELTA)これは通常、MAX_FRAME_DELTA = 2/100 * sまたは3/100 * sです。そのため、ゲームロジックから離れすぎているときにフレームをスキップする代わりに、大量のフレーム損失を破棄し(速度が低下します)、いくつかのフレームを回復し、描画して、再試行します。

これを行うことにより、プレーヤーコントロールが実際に画面に表示されるものとより密接に同期するようにします。

最終製品の擬似コードは次のようなものです(デルタは前述のFです)。

// Assume timers have 1/100 s resolution
const MAX_FRAME_DELTA = 2
// Calculate frame gap.
var delta = current time - last frame time
// Clamp.
delta = min(delta, MAX_FRAME_RATE)
// Update in discrete steps
for(i = 0; i < delta; i++)
{
    update single step()
}
// Caught up again, draw.
render()

この種の更新はすべてに適しているわけではありませんが、アーケードスタイルのゲームでは、ミスフレームよりも多くのことが行われているため、ゲームの速度が遅くなり、プレーヤーのコントロールが失われます。また、フレーム損失によって再現性のないグリッチが発生する他の可変時間ステップアプローチよりもこの方法を好みます。


その最後の点に強く同意します。ほとんどすべてのゲームで、フレームレートが低下すると入力が「遅く」なります。これは一部のゲーム(マルチプレイヤーなど)では不可能ですが、可能であればさらに優れています。:Pそれは、長いフレームを持ち、ゲーム世界を「正しい」状態に「ジャンプ」させるよりも、単に気分がいいです。
イプスィーク

アーケードマシンのような固定ハードウェアがない場合、アーケードゲームがハードウェアが追いつかないときにシミュレーションを遅くすると、遅いマシンの不正行為でプレイすることになります。

「不正行為」を気にする場合にのみ重要なジョー。最近のゲームのほとんどは、プレイヤー同士の競争ではなく、楽しい体験をすることです。
イアン

1
Iain、ここではアーケードスタイルのゲームについて具体的に説明しています。これは伝統的にハイスコアリスト/リーダーボード駆動です。私は大量のshmupsをプレイしており、スコアを消去したい人がリーダーボードに人為的に減速してスコアを投稿しているのを見つけた場合は知っています。

答えを減らそうとはしていませんが、レンダリングを物理更新レートに直接結び付けない固定ステップとして解釈しますが、物理に追いつくことはレンダリングよりも優先されます。それは間違いなく良い品質を持っています。
アーロンLS

6

このソリューションはすべてに適用されるわけではありませんが、別のレベルの可変タイムステップがあります-世界の各オブジェクトの可変タイムステップです。

これは複雑に思えますが、複雑になる可能性がありますが、ゲームを個別のイベントシミュレーションとしてモデリングすると考えてください。各プレーヤーの動きは、モーションの開始時に開始し、モーションの終了時に終了するイベントとして表すことができます。イベントの分割を必要とするインタラクション(たとえばコリジョン)がある場合、イベントはキャンセルされ、別のイベントがイベントキュー(おそらく、イベント終了時刻でソートされた優先度キュー)にプッシュされます。

レンダリングは、イベントキューから完全に切り離されます。表示エンジンは、必要に応じてイベントの開始時間と終了時間の間のポイントを補間します。この推定では、必要に応じて正確であるか、ずさんなことがあります。

このモデルの複雑な実装については、宇宙シミュレーターEXOFLIGHTをご覧ください。従来の固定タイムスライスモデルではなく、イベントベースのモデルであるほとんどのフライトシミュレータとは異なる実行モデルを使用します。このタイプのシミュレーションの基本的なメインループは、擬似コードでは次のようになります。

while (game_is_running)
{
   world.draw_to_screen(); 
   world.get_player_input(); 
   world.consume_events_until(current_time + time_step); 
   current_time += time_step; 
}

スペースシミュレータで使用する主な理由は、精度を損なうことなく任意の時間加速を提供する必要性です。EXOFLIGHTの一部のミッションは、ゲームに数年かかる場合があり、32倍の加速オプションでも不十分です。使用可能なsimには1,000,000倍以上の加速が必要です。これはタイムスライスモデルでは困難です。イベントベースのモデルでは、1秒= 7ミリ秒から1秒= 1年までの任意の時間率が得られます。

タイムレートを変更しても、simの動作は変わりません。これは重要な機能です。必要な速度でシミュレータを実行するのに十分なCPU能力がない場合、イベントが累積し、イベントキューがクリアされるまでUIの更新を制限する場合があります。同様に、シムを必要なだけ早送りし、CPUを無駄にせず、精度を犠牲にしないようにします。

要約すると、1台の車両を(Lunge-Kutta統合を使用して)長いゆるやかな軌道でモデル化し、同時に別の車両を地面に沿って跳ね返ることができます-グローバルタイムステップがないため、両方の車両は適切な精度でシミュレーションされます。

短所:複雑さ、およびこのモデルをサポートする既製の物理エンジンの欠如:)


5

固定タイムステップは、浮動小数点の精度を考慮し、更新の一貫性を保つのに役立ちます。

これは単純なコードなので、試してみて、ゲームで動作するかどうかを確認すると便利です。

now = currentTime
frameTime = now - lastTimeStamp // time since last render()
while (frameTime > updateTime)
    update(timestep)
    frameTime -= updateTime     // update enough times to catch up
                                // possibly leaving a small remainder
                                // of time for the next frame

lastTimeStamp = now - frameTime // set last timestamp to now but
                                // subtract the remaining frame time
                                // to make sure the game will still
                                // catch up on those remaining few millseconds
render()

固定タイムステップを使用する場合の主な問題は、高速のコンピューターを使用しているプレーヤーが速度を利用できないことです。ゲームが30fpsでのみ更新される場合の100fpsでのレンダリングは、30fpsでのレンダリングと同じです。

そうは言っても、複数の固定時間ステップを使用することも可能です。60fpsを使用して簡単なオブジェクト(UIやアニメーションスプライトなど)を更新し、30fpsを使用して非自明なシステム(物理学など)を更新し、さらに遅いタイマーを使用して、未使用のオブジェクト、リソースなどの削除などのバックグラウンド管理を実行できます。


2
ゲームを慎重に作成すると、renderメソッドは補間を実行して、30fpsでのレンダリングと実際には同じではない30fpsの更新を行うことができます。
リケット

3

あなたが既に述べたことに加えて、あなたがあなたのゲームに持ちたいと思う感覚に帰着するかもしれません。常に一定のフレームレートが保証される場合を除き、どこかで速度が低下する可能性が高く、固定タイムステップと可変タイムステップは大きく異なります。修正すると、ゲームがしばらくスローモーションになる効果がありますが、これは意図した効果になる場合があります(ボスを倒した後に大きな爆発がスローダウンを引き起こす斑鳩のような古い学校スタイルのシューティングゲームを見てください)。可変タイムステップは、時間の観点から物事を正しい速度で動かし続けますが、位置などの突然の変化が見られる場合があり、プレーヤーがアクションを正確に実行するのが難しくなる可能性があります。

固定タイムステップがネットワーク上で物事を容易にすることは実際にはわかりません。1台のマシンですべてが最初にわずかに同期がずれてスローダウンしますが、別のマシンでは同期がずれることはありません。

私は常に可変的なアプローチに個人的に傾いてきましたが、それらの記事には考えるべき興味深いことがいくつかあります。それでも、特にPCで達成可能な非常に高いレートと比較してフレームレートが一定の60fpsであると人々が考えるコンソールでは、非常に一般的な固定手順を見つけました。


5
オリジナルの投稿のGaffer on gamesリンクを必ずお読みください。これはそれ自体が悪い答えだとは思わないので、それを否定するつもりはありませんが、私はあなたの議論に同意ません。
ファルストロ

固定タイムステップの結果としてのゲームのスローダウンは、コントロールの欠如のためであるため、意図的になることはないと思います。制御の欠如は、定義により偶然に降伏することであり、したがって意図的にすることはできません。それはあなたが念頭に置いていたものである可能性がありますが、それは私がそれに行きたい限りです。ネットワーキングの固定タイムステップに関しては、明確なプラスがあります。同じタイムステップを持たない2台の異なるマシンの物理エンジンを同期させることは不可能です。同期する唯一のオプションは、すべてのエンティティ変換を送信することになるため、帯域幅が非常に大きくなります。
カイ

0

Gafferの「タイムステップを修正する」アプローチを使用します。オプション1のように固定ステップでゲームを更新しますが、経過時間に基づいてレンダリングされたフレームごとに複数回更新します。これにより、個別のステップのままでゲームのロジックがリアルタイムに追いつきます。このように、オイラーインテグレーターのような簡単に実装できるゲームロジックと単純な衝突検出は引き続き機能します。また、デルタ時間に基づいてグラフィカルアニメーションを補間するオプションもありますが、これは視覚効果専用であり、コアゲームロジックに影響を与えるものではありません。アップデートが非常に集中的な場合、潜在的にトラブルに巻き込まれる可能性があります-アップデートが遅れると、追いつくためにより多くのアップデートが必要になり、ゲームの応答性がさらに低下する可能性があります。

個人的には、オプション1が好きで、それを回避することができ、オプション3をリアルタイムに同期する必要があるときが好きです。私はあなたが何をしているのかを知っているとき、オプション2が良いオプションになり得ることを尊重しますが、それから十分に離れておくために十分に私の制限を知っています


少なくとも答えを盗むつもりなら、その人を信用してください!
プリムロック

0

60fpsに同期された固定タイムステップにより、ミラーのスムーズなアニメーションが得られることがわかりました。これは、VRアプリケーションにとって特に重要です。それ以外は身体的に吐き気を催します。

可変タイムステップはVRには適していません。可変タイムステップを使用するUnity VRの例をご覧ください。不快です。

ルールは、3DゲームがVRモードでスムーズである場合、非VRモードで優れていることです。

これら2つを比較する(Cardboard VRアプリ)

(可変タイムステップ)

(固定タイムステップ)

一貫したタイムステップ/フレームレートを実現するには、ゲームをマルチスレッド化する必要があります。物理学、UI、レンダリングは専用のスレッドに分離する必要があります。それらを同期するのは恐ろしいPITAですが、結果はあなたが望むミラースムーズレンダリングです(特にVRの場合)。

モバイルゲームは特に魅力的です。組み込みのCPUとGPUのパフォーマンスは限られているため、困難です。GLSL(スラング)を控えめに使用して、CPUから可能な限り多くの作業をオフロードします。GPUにパラメーターを渡すとバスリソースが消費されることに注意してください。

開発中は常にフレームレートを表示してください。実際のゲームは、60fpsに固定することです。これは、ほとんどの画面およびほとんどの眼球のネイティブ同期レートです。

使用しているフレームワークは、同期要求を通知するか、タイマーを使用できる必要があります。これを実現するためにスリープ/待機の遅延を挿入しないでください-わずかな変動でも顕著です。


0

可変時間ステップは、可能な限り頻繁に実行する必要がある手順用です:レンダリングサイクル、イベント処理、ネットワーク関連など。

固定タイムステップは、予測可能で安定したものが必要な場合に使用します。これには、物理​​学と衝突検出が含まれますが、これらに限定されません。

実際には、物理​​学と衝突検出は、それ自身の時間ステップで、他のすべてから切り離されるべきです。このような手順を小さな固定時間ステップで実行する理由は、それらを正確に保つためです。インパルスの大きさは時間に大きく依存します。間隔が大きくなりすぎると、シミュレーションが不安定になり、地面を跳ね返るボールフェーズやゲーム世界から跳ね返るような狂ったことが起こりますが、どちらも望ましくありません。

他のすべては可変タイムステップで実行できます(専門的に言えば、多くの場合、レンダリングを固定タイムステップにロックすることをお勧めします)。ゲームエンジンが応答するためには、ネットワークメッセージやユーザー入力などをできるだけ早く処理する必要があります。つまり、ポーリングの間隔はできる限り短くすることが理想的です。これは一般的に変数を意味します。

それ以外はすべて非同期で処理できるため、タイミングが重要なポイントになります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.