UDN
Search public documentation:
DungeonDefenseDeveloperBlogJP
English Translation
中国翻译
한국어
Interested in the Unreal Engine?
Visit the Unreal Technology site.
Looking for jobs and company info?
Check out the Epic games site.
Questions about support via UDN?
Contact the UDN Staff
中国翻译
한국어
Interested in the Unreal Engine?
Visit the Unreal Technology site.
Looking for jobs and company info?
Check out the Epic games site.
Questions about support via UDN?
Contact the UDN Staff
ダンジョンディフェンス (Dungeon Defense) 開発者ブログ
Blog 1: 初日
皆さん、こんにちは。 インディー系の開発者にとって、わくわくするような時代がやってきました。デジタル配布の時代の到来とともに、巨大な市場が出現しました。小さいクリエイティブなチームが製作したあらゆる種類のゲームが恩恵を受けるようになりました。そればかりではありません。私たちは Epic を手に入れたのです。間違いなく、Epic は業界で最先端を走っています。その Epic が、熱意あふれる、未来の Shigeru Miyamoto (宮本茂) や Kim Swift のために、プラットフォームを開放したのです! 私はかなり前から Unreal を使って開発してきたので、この時流に乗ろうと考えました。今後数ヶ月に渡って、「UDK」(=「Unreal Development Kit」(Unreal 開発キット)) を使って簡単なミニゲームのデモを連続して製作してみるつもりです。目標は、オープンソース / オープンコンテンツの例を通じて「UDK」で多様なゲームプレイを実装する方法を、成長しつつあるこのコミュニティに対して提供することにあります。おそらく比較的シンプルなコードとコンテンツになるはずでなので、フルコースの「Unreal Tournament」よりも消化しやすいと思います。 それはともかくとして、さっそくいただきましょう! (今、すごくお腹も空いています)。 まずどんなゲームから作り始めようかと考えたとき、「Unreal」 を使用した三人称視点のさまざまなゲームについて疑問をもっている人たちがいることに気がつきました。(この種のゲームの作成は、やり方さえ分かってしまえば簡単です)。さらに、「PixelJunk Monsters」を一通りプレイし終わったところでしたので、Tower Defense 系 (塔を配置して城を防衛するゲームタイプ) ゲームに幾分気持ちが傾いてきました。戦略的に建物を配置して多数の小動物を撃退するゲームは、おそらくとても楽しいはず。 という次第で、最初に製作するのは、「Dungeon Defense」(ダンジョン防衛) という名前のゲームにしました。視点は、セミトップダウン ( 3/4 透視図法により近い) で、タイプは、アクション / 塔による防衛のハイブリッド型です。プレイヤーは魔法使いとしてプレイします。状況は、師匠が不在の間にその隠れ家を守ろうとして大変な目に遭遇するといったものです (「魔法使いの弟子」 のように)。隠れ家ではさまざまな「召喚」魔法を唱えることによって、魔法的ディフェンスを形成します。ディフェンスが破られた場合は、魔法の杖で撃退します。アクション戦術とリソース管理戦略のハイブリッドによってゲームが楽しくなります。ゲームの実装は本来非常に大変ですが、「Unreal」を使用すると快適なものになります。 さてそれで、週末に設計作業を行ってみました。アセットについて綿密に計画を立て、スキームを調整してみました。これによって今日、正式にプログラミングを開始する運びとになったのです。まず、GameInfo、Pawn/Player、Player、Controller、Camera の各クラスを実装しました。以下でクラスの概要を説明します。 GameInfo クラスには、ゲームの一般的なルールが含まれます。私の場合は、これまでのところ、自分のカスタムの PlayerPawn クラスと PlayerController クラスに反映させる、デフォルトの値をオーバーライドしただけです。PlayerPawn クラスは、もちろんワールドにおける物理的なキャラクターです。Controller クラスは、ユーザーの入力が PlayerPawn クラス 上のゲームプレイアクションに変換される場所です。カメラのクラスについては、UpdateViewTarget 関数を修正することによって、カメラの位置を Pawn 上方に据えるとともに (「Unreal Tournament」のように目から直接眺めません)、プレイヤーが眺める方向に少しカメラが動的にオフセットするようにしました。これによって、カメラがターゲットの方向に向かって回転するようになりました。 (2) Epic に含まれている RInterpTo 関数と VInterpTo 関数を利用して、Rotator と Vector の補間をそれぞれ処理しています。(これらは常に役立つものです)。これによって、現在のプレイヤーの位置 (および回転) からカメラを少し遅延させることができるため、プレイヤーの位置どおりに固定するよりもスムーズな動きを表現することができます。 PlayerController クラスの中では、PlayerMove 関数を変更して、ヨー (Yaw) の回転だけが変化するようにしました。ピッチ (Pitch) は変化しません。(これは、キャラクターの方向を 2D 平面に限定するためです)。最初は、マウスをスクロールさせながら Mouse Delta (差分) を利用してフレームごとに少しヨーを変更しようとしましたが、自然な感じが得られませんでした。PC にとっては不正確の極みでした。キャラクターは、私たちが操作した方向にきちんと向いている必要がありますよね? そこで、独自のコードを書いて、マウスの現在位置の canvas deprojection (キャンバス逆投影) に関する結果を得るようにしました。それから、その 3D ベクターをワールドに対してレイキャストすることによって、ユーザーが指しているワールドの部位を見つけ、キャラクターをその方向に向かせることにしました。 (3) もちろん、カメラの回転による入力方向も変換して、入力が「カメラに相対する」直感的な動きになるようにしました。(TransformVectorByRotation 関数の vector>>rotator 演算子に相当するものだと言えます)。プレイヤーポーンは、ほとんどデフォルトのポーンと同じです。ただし、楽しいアニメーションツリーの作業を行って、複数のカスタムなフルボディアニメーションをブレンドしました。これによって、シーケンスで複数のアニメーションを再生するときでさえポップが現れなくなります。 (4) アニメーションツリー システムを使って作業していると、「Unreal」のツールスイートを使用することがいかに楽しいことであるか思い起こされました。キャラクターのブレンディングを視覚的にリアルタイムのフィードバックを伴って調整していると、ハードコード化よりも確実に楽しいのです。 しかし、この作業はほんの 2 ~ 3 時間で済むため、まだ本日の作業を終了するわけにはいきません。次に、魔法使いの武器である杖 (staff) に取り組まなければいけません。Weapon クラスをサブクラス化して、武器が携えられた際に充填できるようにし、リリースされた時にのみ発砲するように変更しました。(杖にはさまざまな充填攻撃がほしいものです)。 (5) また、Projectile クラスをサブクラス化してさまざまな強さの発射物をサポートしました。これに応じてすべての視覚効果が徐々ににスケーリングされます。 (6) アーキタイプの参照を作成するほうが、直接的なクラスの参照よりもはるかに便利であると思いました。 (7) ゲームプレイのためにアーキタイプを指定するには Spawn 関数で行います (いわゆるアクタ テンプレートとして)。ゲームプレイのためにアーキタイプを作成すると、ゲームプレイ オブジェクトのライブラリ全体をエディタ内でリアルタイムに調整することができるようになります。値を修正しなければならなくなるたびに、デフォルトのプロパティを変更する必要がなくなります。また、これによって視覚的に値を設定することが簡単にできるようになるとともに、メディアのスワップアウトも容易になり、さらには、プロパティだけが異なる、同一のコアクラスからなる複数のバリエーションクラスを作成することも楽にできるようになります。アーキタイプの機能については後にその詳細に触れる予定ですが、イタレーションの重荷を軽減させてくれたと言って差し支えありません。もちろん、Remote Control もリアルタイムのイタレーションには本当に役立ちます。(実行ファイルを起動するときに -wxwindows の引数をつけることによってアクセスできます)。Remote Control の機能については、また別の投稿で述べるつもりです。 次に最初の敵である Goblin の AI Controller に取り組みました。「ステートロジック」を少しばかり書きました。これは、まずターゲットを選択します (ターゲット化の重み付けを返すインターフェースを実装する、すべてのアクタに基づきます)。 (8) さらに、この「ステートロジック」によって、パスファインドのタイミングとターゲットに直接進むタイミング、パスファインド / 移動の停止のタイミング、攻撃開始のタイミングが決定されます。 (9) AI スクリプトの詳細については後述するつもりです。また、敵の Goblin のために、かっこいい MeleeAttack (接近戦) のステートを実装しました。このためには、アニメーション通知を使用することによって、現在の「手のソケット」の位置と前回の位置との間における各フレームの Trace (ボックススイープ) を有効 / 無効にします。 (10) これによって Goblin がダメージを与えるためにスワイプした部分が、ハードコード化された値にではなく、アニメーションとそのタイミングに実際に基づくことになります。また、Goblin が「Trace された」アクタにダメージを与えるのは、一意のスワイプ 1 回ににつき 1 度だけにしています。これは、現在のスワイプ中に打撃を受けた全アクタのリストを維持し、それと照合することによって可能となります。 (11) これらがすべて実行されると、接近戦は素晴らしい出来映えになり、アニメーションが表現するとおりのものに見えます。 次に、どうしても基本となる「砲塔」を実装したくなりました。これは敵を攻撃するものです。このシンプルな動くことのないアクタのために AI Controller で時間を無駄にすることなく、Timer を使ってターゲットを選択します。(Controller だけではなく、ステートロジックは、どんなオブジェクトにも使用できることを忘れないでください)。 (12) また、LookAt Bone Controller をこの塔のアニメーションツリーに追加することによって、塔の最上部が選択されたターゲットに向くようにしました。 (13) アニメーションツリーがセットアップされると、他にやるべきことは、塔に対してどの方向に向くかを命じるコードを 1 行書くだけです。やった! ゲームタイプが明確化されてきたので、さらに作業を進めて、Crystal Core の実装に移りました。この Crystal Core は、敵が破壊を試みようとするもので、敵の主要な目標となっています。 (14) ターゲットとなりうるアクタ (15) のために作成した「インターフェース」を使用して、この Core に特別に高い優先順位を与えました。そのため、敵は、プレイヤーや塔よりもこれに引きつけられることになります。「インターフェース」を使用すると、完全に異なるクラスのアクタが共通のメソッドの集合をもてるようになります。それによって、アクタは同一の方法で操作されたりチェックされたりするようになります。したがって、この Crystal Core クラスとプレイヤーポーンは、直接継承関係にはないにもかかわらず、共通のインターフェースによって提供される、同一のターゲット化の重み付け関数を共に実装していることになります。これによって敵の AI は、汎用的な方法でアクセスして、どのエンティティがより重要なターゲットであるかを判断することができることになります。すばらしい! 最後に、プロジェクトの中心アーティストである Morgan Roberts がテスト用レベルを整えて、魔法使いの隠れ家を実に素晴らしい感じに仕上げてくれました。私は Kismet を少しばかり使って、敵の Wave (大軍) が繰り返しスポーンして Core 攻撃のために進軍するように設定しました。 (16) さてこのような感じで、基本的にプレイ可能なプロトタイプが約 1 日でできてしまいました。 ゲームプレイはすでに手強いものになってきているので、今後数日間はバランスに関する作業に真剣に取り組むことにします。もちろん、多数追加される機能の実装や、すでにできあがったものを調整する必要もあります。「Unreal」という素晴らしいツールのおかげで、このようなものを実装するのは本当に楽しいことなのです。 次の投稿では、上記で簡潔に触れたテーマのうちその多くを取り上げて詳しく解説します。また、興味深いと思ってもらえるような (あるいは特に役に立つ) コードや機能をいくつか概説していきたいと思います。また、見てもらえるような画像ができたら、スクリーンショットをアップすることにします。それでは、みなさんがすぐに、このささやかなゲームをプレイすることを楽しみにしています。 さっき考えていたピザを食べよう... - JeremyBlog 1: ファイル レファレンス
このブログで取り上げられている情報は、次にあげるファイルに含まれています。(それらのファイルは、Dungeon Defense のソースコードです)。カンマで分けられている行番号は、ファイル内で独立した行が複数あることを示しています。ハイフンで分けられている行番号は、ファイル内で行がその範囲にまたがっていることを示しています。- Main.uc: 618, 638
- DunDefPlayerCamera.uc: 240 - 248
- DunDefPlayerController.uc: 1561 - 1652
- DunDefPawn.uc: 222
- DunDefWeapon_MagicStaff.uc: 111
- DunDefProjectile_MagicBolt.uc: 24
- DunDefInventoryManager.uc: 13
- DunDefEnemyController.uc: 222
- DunDefEnemyController.uc: 764
- DunDefGoblinController.uc: 52
- DunDefGoblinController.uc: 32
- DunDefTower_ProjectileType.uc: 99
- DunDefTower_ProjectileType.uc: 95
- DunDefCrystalCore.uc: 19
- DunDefTargetableInterface.uc: 15
- DunDef_SeqAct_BasicActorSpawner.uc: 11
Blog 2: 3 日目
Dungeon Defense を開発してから 2 日目と 3 日目は、照準機能と AI ナビゲーションの改善とともに、ワールド内に防御「塔」を直感的に配置できるシステムの基礎を追加することに注力しました。それぞれの実装について説明させてください! 照準について言えば、当初のマウスベースによるスキームは、単に、マウスで指したところをプレイヤーキャラクターがヨー角で見るというものでした。このスキームはうまく機能していたのですが、3D 照準については別です。キャラクターがヨー角でのみ回転するのは、(ほぼ) 上からの視点からでは、ピッチ角の入力を直感的に決定できる実際的な方法がないからです。敵がプレイヤーの下や上にいる場合が問題になります。敵を攻撃したくてもできないため、ストレスになります。そこで、大きな修正を 2 つ施しました。1 つは、マウスベースのスキームに関してです。もう 1つはゲームパッドのスキームについてです。これらは極めてうまく機能してくれました。マウスベースのスキームに関しては、まず、マウスのスクリーン座標の「逆投影」視線がぶつかる点に照準を合わせたキャラクターのピッチ角を計算しました。 (1) ちょっとした脱線 : 「逆投影」とは 2D スクリーン空間から 3D ワールド空間に変換することを意味します (たとえば、スクリーンから線を投射するように)。一方、「投影」はワールド空間からスクリーン空間に変換することを意味します。両方とも、プレイヤーの HUD を通じてアクセスできる、対応する Canvas クラスの関数を使用して「Unreal」で実行することができます。 (2) 次にいずれの場合も、このピッチ角の値をキャラクターのアニメーションツリー内にある Bone Controller セットアップに渡します。こうすることによって、キャラクターは腰の部分で体を曲げて上下を見るようになります。 (3) このようにすると、PC ゲームでは自然な感じが最もよく出ます。 ただし、キャラクターの近くを指すとキャラクターが下を見がちな点は何とかしなければなりませんでした。(キャラクターが基本的に足下を見るのは、実際にそこを指しているからなのです)。そこで条件を加えることにしました。プレイヤーの近くを指した場合でも、狙ったところと高度の違いがさほど大きくなければ、単に前方を見るようにしたのです。 (4) これによって、カーソルをプレイヤーの近くに持ってきたときにプレイヤーが足下を見ながら走り回らなくても済むようになります。もちろん、Bone Controller にセットされているヨーとピッチに補間を適用しました。これによって、照準点が急激に変化しても、キャラクターの動作が極端に変化することがなくなりました。さらに、実際の照準点をポーン内に保存し (5) 、武器がそれを調べて、その地点に向けて発射物を明確に狙うようにしました。これは、Rotation (回転) を Rotator(TargetPoint-ProjectileSpawnPoint) に設定するとともに、Velocity (速度) を Speed*Normal(TargetPoint-ProjectileSpawnPoint) に設定することによって簡単に可能となります。 (6) この結果、ピンポイントのシューティングが可能になりました。PC ゲームの出来映えとしては妥当なところです。それでいて、私が望んでいたとおりにシンプルなままで、アーケードゲームのようなトップダウンの視点を保っています。 ゲームパッドコントロールのスキームに関しては、少しばかり異なった処置をしました。プレイヤーには素早く動く正確なポインティングデバイスがないため、ユーザーが指した正確な位置を見るなどということは論外です。それでも、なんらかの 3D 的照準が必要でした! そこで、自動照準関数を実装することにしたのです。この関数は、最大範囲内に存在する (もし存在すれば) 最善のターゲットを決定し、照準位置をそのターゲットの位置に設定するものです。 (7) 自動照準は依然として照準点システムを使用するため、PC のコントロールスキームのために私がすでに作成した「見る」メソッドに適合します。唯一の違いは、「どのようにして」照準点が選択されるかということです。 そこで、最善の自動照準ターゲットを選択するために、プレイヤーからの OverlappingActors 関数によるチェックを開始して、自動照準範囲にある「敵」タイプのアクタをすべて集めます。次に、どの潜在的なターゲットが自分に最も近く「かつ」自分の見ている方向に最も近いかを判断します。最も近くにいて、かつ見ている方向に最も近い (これは 「Normal(TargetLocation - EyeLocation) dot Vector(Rotation)」として計算されます) ターゲットが、それぞれにとって許容範囲にあり、かつ重み付けの範囲にある場合に理想のターゲットとなります。許容範囲と重み付けを調整し終わると、この自動照準選択メソッドはうまく機能しました。これで、ゲームパッドを使用して、(だいたい) 見ているものを垂直方向に自動的に狙うことができるようになりました。また、ターゲットに向けてヨー回転を少しばかりキャラクターの背骨 (Spine) に加えることによって、自動照準時に内積による許容量のために弾丸が斜めに飛んで行くようには見えなくなりました。 (8) これでゲームパッドコントロールがマウスと同等になりました。 これは余談ですが、私は DebugSphere を使用して自動照準ターゲットの地点を描画しました。これは、選択メソッドがどのくらいうまく機能しているかを見極めるのに役立ちます。実際、分析する際にはいつも DebugSphere、DebugLine、DebugBox を使用しています。プロトタイピングの段階では、これらを使用することを強くお勧めします。クラス内でコードにこれらを描画させたままにしておくこともできます。カスタムの bDebug (bool 型) を使用してオフに切り替えておけば、後で問題が発生したときやさらに調整が必要になったときにオンに戻すことができます。3D の操作に関連したコードがワールド内でどのように動作しているかをビジュアル化できるというのは、ゲームプレイ プログラマーにとって素晴らしいことなのです。 さて次に私は、AI パスファインド ルーチンを、「Unreal」の昔ながらの Waypoint-Pathnode ナビゲーションシステムから、新たなナビゲーション メッシュ システムに変更することにしました。すごいです! ものすごく簡単でした。しかし、結果は私の疲れきった目にも明らかでした。レベルの中にパイロン アクタを配置し、パスを作成すると、困難な作業はすべて自動的に実行されます。計算が完了するや否や (驚くほど速い) 、次のように、パスネットワークが環境の中に完全に実現されます。 レベルの構造が変化した場合にもパスノードの設定を再作成する必要はなく、基本的には、[rebuild paths] (パスをリビルドする) をクリックするだけで、すでに配置しているパイロンによって、すべての有効なパスを再計算するという大仕事が完了します。 ナビゲーション メッシュ システムを実際に使用するためのコードについては、これ以上ないほどシンプルです。(他のメッシュベースのナビゲーション技術を使用した経験に基づいています)。基本的には、以下に掲載したコードの数行だけが、Nav Mesh からもたらされたナビゲーション結果を AI Controller が歩くのに必要なものです。function InitNavigationHandle() { if( NavigationHandleClass != None && NavigationHandle == none ) NavigationHandle = new(self) class'NavigationHandle'; } event vector GeneratePathToActor( Actor Goal, optional float WithinDistance, optional bool bAllowPartialPath ) { local vector NextDest; //set our return value equal to our destination Actor’s Location. //In case it’s directly reachable or pathfinding fails, we’ll just return this. NextDest = Goal.Location; if ( NavigationHandle == None ) InitNavigationHandle(); //if the Actor isn’t directly reachable, then try to find the next navigation point towards it. //Otherwise we’ll just return its location to go there directly. if(!NavActorReachable(Goal)) { class'NavMeshPath_Toward'.static.TowardGoal( NavigationHandle, Goal ); class'NavMeshGoal_At'.static.AtActor( NavigationHandle, Goal, WithinDistance, true ); if ( NavigationHandle.FindPath() ) NavigationHandle.GetNextMoveLocation(NextDest, 60); } NavigationHandle.ClearConstraints(); return NextDest; }
//WithinRange just checks a small distance from the TargetActor, //otherwise just keep moving whereever GeneratePath tells us to go. while(!WithinRange(TargetActor)) { MoveTo(GeneratePathToActor(TargetActor),none,30,false); Sleep(0); }
Blog 2: ファイル レファレンス
このブログで取り上げられている情報は、次にあげるファイルに含まれています。(それらのファイルは、Dungeon Defense のソースコードです)。カンマで分けられている行番号は、ファイル内で独立した行が複数あることを示しています。ハイフンで分けられている行番号は、ファイル内で行がその範囲にまたがっていることを示しています。- DunDefPlayerController.uc: 1575
- DunDefHUD.uc: 122
- DunDefPlayer.uc: 304, 329
- DunDefPlayerController.uc: 1611
- DunDefPlayer.uc: 304
- DunDefWeapon.uc: 134, 157
- DunDefPlayer.uc: 241
- DunDefPlayer.uc: 315
- DunDefEnemyController.uc: 938
- DunDefPlayerController.uc: 577
- DunDefTowerPlacementHandler.uc: 340
- DunDefPlayerCamera.uc: 109
- DunDefPlayerCamera.uc: 163-174
- DunDefTowerPlacementHandler.uc: 89-135
- DunDefTowerPlacementHandler.uc: 304
- DunDefTowerPlacementHandler.uc: 438, 474
- DunDefTowerPlacementHandler.uc: 468
- DunDefTowerPlacementHandler.uc: 219-238, 396-404
- DunDefTowerPlacementHandler.uc: 492-516
- DunDefTowerPlacementHandler.uc: 240-249
- DunDefTowerPlacementHandler.uc: 524-525
- DunDefPlayerController.uc: 706
- DunDefTowerPlacementHandler.uc: 691
- DunDefTowerPlacementHandler.uc: 786, 795
- DunDefTowerPlacementHandler.uc: 801
- DunDefPlayerController.uc: 602
Blog 3: 7 日目
皆さん、こんにちは。 Dungeon Defense 開発チームは、前回のブログエントリー以来かなりの範囲を処理してきました。そのため、どこから書いて良いのか決めるのが難しいくらいです。それでも、前回の投稿から数日間に渡って私たちがやり遂げたことを概観してみることにしましょう。(これらのトピックについてはそれぞれ後に詳しく解説するつもりです)。- 剛体の Mana Token (トークン) を追加しました。これは、敵が落とすもので、近くにいるプレイヤーが吸い込みます。塔を召喚するときや魔法を唱える場合に消費するリソースです。
- 武器である「魔法の杖」をグレードアップできるシステムを追加しました。編集可能な構造体配列にある、一連のアーキタイプを使用します。(データ主導型システムです)。
- 分割スクリーンと動的なローカルプレイヤーの参加をサポートするようにしました。
- カスタム UI クラスを追加することによって、エディタ主導型のアニメーションシステムをサポートするようにしました。(Epic による既存の UI アニメーションの基盤の上に作成しました)。
- 多数の機能的プレースホルダー UI シーンを追加しました。具体的には、メインメニュー、ポーズメニュー、ゲームオーバー UI、個々のプレイヤーの HUD、共通のグローバル情報 HUD、ロード画面です。
- 非同期ロードをサポートするゲームロジックをセットアップしました (Seamless Travel (シームレスな移動))。これによって、レベルのロードがバックグラウンドで行われている間に、遷移画面をアニメート化することが可能になります。
- 新たなキャラクターアニメーション ノードを追加しました。(BlendBySpeed のバリアントです。どの物理ステートが movement (動き) として見なされるかを指定するオプションと、スピード乗数の上限を伴います)。また、プレイヤーキャラクターのアニメーションツリーが上半身のブレンディングをサポートするようになりました。
- AI の改善点 : AI がターゲットを直接見通すことができると判断したときに、パスファインドを中止させるようにしました。また、どれが理想のターゲットなのかを AI に定期的に再評価させるようにしました。さらに、フェイルセーフを追加して AI がスタックした (動けなくなった) か否かを検知させ、ナビゲーションシステムへの復帰を試みさせるようにしました。
- 以下のような、多数の Kismet アクションを追加することによって、(特に) フルの「Tower Defense ゲームプレイサイクル」をサポートしました。
さて、これらのトピックのいくつかについて、より詳しく見ていきましょう。まずは、剛体 Mana Token からです。 これはかなり単純です。Epic によって提供されている KActorSpawnable クラスを継承します。私は、セットアップ済みのこのクラスを利用して、静的メッシュコンポーネント (凸衝突が設定されています) に基づき、アクタに剛体物理を適用しました。 (1) 子クラスのデフォルトのプロパティをオーバーライドして、bWakeOnLevelStart=true としました (ただちにドロップするようになります)。また、bBlockActors を false に設定することによって、プレイヤーがスタックすることなくオブジェクトを通過することができるようにしました。(アーキタイプにおいて) この ManaToken には、小さな宝石のような静的メッシュをつけます。次に、DunDefEnemy が、Died() 関数においてさまざまな数の ManaToken を (アーキタイプから) スポーンするようにします。 (2) また、スケーリングした VRand() 関数 (ランダムな方向ベクター) によるインパルスをドロップした Token に適用し、敵から外に向かって飛んで行くようにしました。プレイヤーの範囲内にある Mana Token を調べて、あった場合は回収します。(つまり、破壊して Mana 値を Player Controller のトータルに加算します)。 (3) 最後に、各 Token を回収する際に実際に触れなくてもいいようにするために、定期的な OverlappingActors テストをプレイヤーの中に追加しました。(各 Token の中ではありません!)。これによって、近くにある Token すべてを見つけることができるようになると共に、プレイヤーに対して力が適用されて Token が吸い込まれるフラグを立てることができます。また、Token の速度 (velocity) の向きがプレイヤーの方向とは異なる場合に、逆方向の力をわずかに加えるようにしました。この力は基本的に「異方性の摩擦」を利用したもので、プレイヤーの方向により速く向かうようにさせるものです。 (4) ともかく、Token が飛び回り始めたとき、満足のいく吸い込み効果を得ることができました。 さて次に、ゲームプレイ中に起きる武器のグレードアップに関するサポートです。そのために、私は Summoning Tower (塔の召喚) ステートを PlayerController で拡張しました。(このステートは、基本的に入力をロックし、プレイヤーキャラクターに召喚アニメーションを再生させるものです)。 (5) この子ステートの名前を UpgradingWeapon としました。いくつかの対応する関数をオーバーライドして、異なるアニメーションとビジュアルエフェクトを再生するようにしました。 (6) このように、元となるステートの機能すべてを利用しつつ、必要なオリジナルの新機能だけを実装することができるのです。ステートの継承関係はゲームプレイプログラミングにとって、とてつもなく有益なコンセプトです。言語的な観点から見れば、UnrealScript に独特のものと言えます! さてこれで、Upgrade (グレードアップ) ボタンを押すだけで、プレーヤーがユニークなアニメーションを再生することができるステートにさせることができましたが、ここで武器に何らかの処理をしなければなりません。 Weapon Upgrade Entries (武器グレードアップ エントリ) という名の構造体配列を追加しました。これは、各グレードアップ レベルに関する情報を格納するためのものです。この情報に含まれているのは、Mana コスト、説明、グレードアップに必要な時間、(最も重要な情報として、) グレードアップが完了したときにスポーンしてプレイヤーに渡される武器のための Weapon Archetype Reference (武器アーキタイプの参照) です。 (7) なぜ構造体 (単に値しか含まれません) を使用してクラスを使用しなかったのでしょうか? それは、構造体がエディタのプロパティ エディタ内で動的に作成できるからです。これによって、Weapon Upgrade Entries 構造体配列の値をエディタ内で設定して、システム全体をデータ主導型にしておくことができるのです。 次に、サポートされている各グレードアップのレベル (最大レベル 5) に対応するエントリを格納する列挙型変数を追加しました。また、プレイヤーがグレードアップするたびに増える列挙型変数の値 (現在の列挙型変数の値 + 1) を決めました。これは、構造体配列の次の Weapon Upgrade Entry を得るための添字として使われます。PlayerController 内部で Upgrading Weapon の状態 (ステート) のまま待機します (グレードアップのためのループアニメーションが再生された状態にあります)。これはグレードアップのための構造体のエントリが指示する時間だけ続きます。その時間が切れたときに、新たな武器のためのアーキタイプがスポーンされます (古い武器を破壊するわけです)。 (8) これらは全部うまく機能しました。PlayerController のアーキタイプがもつ Weapon Upgrade Entries 構造体配列に値すべてが格納されているため、武器グレードアップに必要なコストと時間を微調整するイタレーションは、リモートコントロールを通じてリアルタイムにエディタ内で処理することができることになります。これこそ効率というべきものですね! また、分割スクリーンをサポートしようと考えました。1 人でも楽しくなってきたのだから、4 人のプレイヤーならば 4 倍楽しくなるはずです! (まあ、そんな感じでしょうか)。 分割スクリーンによるマルチプレイヤーをサポートするのは、非常に簡単です。これもまた、Epic が提供してくれる強力なフレームワークのおかげです。実際には次のようにすればいいだけでした。すなわち、まだプレイヤーが関連づけられていないコントローラーに関する Press Start (押してスタート) ボタンの入力を処理するとともに、その新たなコントローラー ID を使用して CreatePlayer 関数を呼び出しただけなのです。Input のサブクラス内にプレイヤーをもたないゲームパッドの Press Start 入力については、InputKey 関数で処理しました。プレイヤーがゲームパッドの Start を押すと、このキーの名前が InputKey 関数に渡され、それに対応する ControllerID が使われて CreatePlayer が呼び出されます。 (9) InsertInteraction() 関数を使って、この新たな Input クラスを ViewPortClient クラスの Interaction (インタラクション) リストに追加しました。それだけでお終いです。(10) Player #2 が Start を押すと、第 2 の PlayerController とそれに関連づけられているプレイヤー - ポーンが立ち上がり、さらにこれに応じてビューポートが自動的に分割されます。(分割スクリーンを望まない場合は、ViewportClient クラスで UpdateActiveSplitscreenType()(11) 関数をオーバーライドします。その場合、第 1 のプレイヤーのカメラ視線で全体の描画が行われることになります)。さてこれで、1 人のプレイヤーしか経験できなかったことが、複数のローカルプレイヤーも楽しめるようになりました。オンラインによるマルチプレイヤーの場合は、アクタ複製システムを使用するため、これよりも手間がかかります。それでも、Epic が提供する既存のフレームワークによって、それほど大変な手間にはなりません。これについては次回掲載時に解説することにします。 次に、ゲームで使用するための、基本となる機能的な UI に取り組んでみました。これによって、ゲームが完全にプレイ可能なシステムとして機能することになります (1 つのレベルにとどまらず、メインメニューから勝利の確定までの全プロセスにおいて)。UI アニメーションシステム (非常に強力なシステムです) について検討してみましたが、デフォルトのプロパティを通してしか編集することができません。そこで、UnrealScript の機能を利用することによって、UI アニメーションクラスの値を構造体にラップするとともに、拡張した UIScene クラス内で編集できるようにしました。 (12) カスタムの UIScene クラスが起動すると、これらの構造体の値が動的に作成される UI アニメーション オブジェクトにコピーされるようにします。 (13) このようにして、Epic によって作られた既存の UI アニメーション システムを利用しながらも、エディタ内でアニメーションの値を編集したり試したりすることができるようになったのです。 この新たな機能が準備できたので、単純なプレースホルダー UI を多数作成しました。これらの UI には、プレイヤー HUD UI (14) (カスタムの HUD クラスによって開かれます) のように、各プレイヤーのビューポートで描画されるためのものもあれば、1 人のプレイヤーによって所有されるのではなくグローバルでフルスクリーン用のものもあります。GameInfo クラスに関数をいくつか書いて、ゲームの持続的なステートに直接基づくグローバルな UI を表示してみました。(たとえば、建造局面 (build phase) における残り時間や、戦闘局面 (combat phase) での残っている敵の数などです)。 (15) UI のためにささやかな (プレースホルダー) オープンおよびクローズのためのアニメーションを作成しました。(エディタ内で調整します)。 これがうまく行ったので、ロード用の UI をアニメート化することにしました。 (16) これによって、メインメニュー (実際はメインメニュー UI を開くレベル) からゲームプレイレベルまで、見事な遷移が可能となります。「退屈する瞬間がまったくない」というような願望に基づいているわけです。これは、Epic の Seamless Travel (シームレスな移動) を使用することによって可能となります。これは、レベルをバックグラウンドでロードする際に、他のレベルを一時的な「遷移」マップとして使用するものです。私のゲームの場合、遷移マップは Loading Screen (ロード画面) の UI Scene (シーン) を開くものです。これは遷移マップが閉じられるまで表示されます。そのとき、目的のレベルのロードは完全にバックグラウンドで完了しています。 WorldInfo.SeamlessTravel (17) を呼び出すだけで、.INI ファイルで指定された遷移マップに入るとともに、最終目的のレベルがバックグラウンドでロードさることになります。シンプルで強力です。 もちろん、いわゆる「レベルストリーミング」の機能も備わっています。これは、ゲームプレイの進行中にレベルのある部分 (たとえば、ある建物の最初の部屋に入った時の内部) をストリームしたり、古い部分 (たとえば建物の内部に入ったときの外部のワールド) をアンロードしたりする機能のことです。ワールドが大きいゲームには特に役立ちますが、これはまったく別のプロセスです。Kismet とワールドエディタを使って処理することになります。Epic による完全なドキュメントが、 レベルストリーミングの手順 に掲載されています。 次に気がついたことは、キャラクターの動く速度を動的に調整することができると、キャラクターが動くアニメーションの再生レートの処理に非常に有益だということです。このためには、アニメーションツリーノードがすでに Epic によって用意されていますが、これに少しばかり機能を追加したくなりました。つまり、プレイヤーが特定の物理ステート (すなわち地面を歩いているステート) にある場合にのみ速度のスケーリングを行うようにするとともに、再生レートのスケールに上限を設定したかったのです。こうすれば、何らかの理由によって (たとえば、爆発から大きなはずみを受けた場合など)、プレイヤーが極めて速く動くときに、動きのアニメーションが結果的に不自然に見えることはありません。ありがたいことに、この作業は簡単でした。新たなアニメーションノードクラスは Epic の AnimNodeScalePlayRate から継承させて、Tick 関数を追加しただけです。Tick 関数では、オーナーである骨格メッシュのアクタが現在動いている速度をチェックします。(私の望みであったクランピング (値の制限) と物理チェックを実行しています)。 (18) この新たな Tick 関数をサポートするために TickableAnimNode インターフェースを作成するとともに (19) 、ポーンクラスに関するノードを OnBecomeRelevant() 関数に登録しました (また、OnCeaseRelevant() の登録を解除しました)。これによって、ポーンはノードをティックすることを知るようになります。エンジンの基本クラスを拡張して自分のクラスを作成し、UnrealScript を使って新たな機能を追加すれば、フレームワークのパワーを最大限に活用できます。これは、Kismet の機能を追加するときに、はっきりすることでもあります。そして、これが次に私がおこなったことなのです! (また、CustomAnimation ノードを追加しました。これは、キャラクターの上半身だけで再生されるようにフィルタリングがかけられたノードです。これの親としては、Spine(背骨) ボーンよりも上方をフィルタリングするように設定された AnimNodeBlendPerBone クラスを使用しています。したがって私のキャラクターは、脚を独立して動かしながら反応できるアニメーションを再生することができるのです)。 (20) さて、ローカルのマルチプレイヤー、機能的 UI、基本的なリソースと武器のグレードアップシステムがすべて終わったので、これらすべてをタワーディフェンス ゲームのサイクル (スタートから終了まで) の元にまとめることにします。これには少しばかりレベル スクリプティングをうまく扱う必要があります。(ハードコーディングすることもできるのですが、かなり不自然なものになり、他のゲームタイプやレベルへの拡張性も犠牲になってしまいます!)。そこで、Kismet を使用して この Build and Combat (建設と戦闘) サイクルを制御することにします。このサイクルは、基本的に次のような構成になっています。すなわち、プレイヤーに塔を建設する時間を与える (UI を通じて時間を通知する)、敵の Wave (大軍) をスポーンする (UI を通じて敵の数を通知する)、このサイクルを繰り返しながら敵の数と襲来インターバルを手順に沿ってスケーリングし、抗い難くなるまで徐々に難しくしていく。素晴らしい..^^ まず、潜在的な Kismet アクションを使用して敵の Wave (大軍) をスポーンさせます。このアクションは、すぐに終了 / 出力するのではなく、内部的に長期的に更新し、内部ロジックによる指示があったときにのみ終了します。独自の Enemy Wave Spawner (敵大軍スポーナー) アクションを作成しました。(SeqAct_Latent を拡張しているため Update() 関数をもちます。 (21) )。これには、構造体配列が含まれており、それぞれの構造体は、このアクションが開始されてから一定時間後に現れる敵の Wave (大軍) を定義しています。 (22) この Enemy Wave Spawner Kismet アクションは、敵の Wave (大軍) を全滅させたときにのみ終了し、最終的出力をアクティベートします。 (23) 次が特に興味深い部分です。Wave Entries 構造体配列をこのアクションのプロパティ内で直接編集することは可能でしたが、一方で、これらの Wave Entries を複数のスポーナー間で回す必要があり、また、手順に沿って値をスケーリングし、さらに、UI による情報 (「倒すべき敵の数」) として編集する必要もありました。 (24) そこで、SeqVar_EnemyWaveEntries という新たな Kismet Variable(変数) クラスを作成することにしたのです。このクラスは、構造体を単に内包しているものです。 (25) この Kismet Variable オブジェクトが、Variable 入力として Wave Spawner Kismet アクションに渡され、自らの使用のために構造体をコピーするのです。 (26) Kismet Variable オブジェクトを使用して Wave Entries 構造体を使用することで (Wave Spawner アクション内で直接編集可能な値を使用するのではなく)、Wave Entries 構造体を Kismet 内にビジュアルに回すことができるようになりました。これによって、Kismet 内の Wave Entry 変数を、自分で書いた他のアクションである ScaleEnemyWave にリンクすることが可能になります。ScaleEnemyWave は Wave (大軍) のエントリと float 型を入力として受け取ります。これらは 敵の数と敵襲来のインターバルのためのもので、Wave に乗じる倍率です。 (27) Combat (戦闘) サイクル後に Multiply Float Kismet アクションを使用してこれらの float 型の倍率を変更することによって、ラウンドごとにゲームを徐々に難しくすることができます。まもなくこのシステムを使ってさらに多くのことを実行するつもりです。たとえば、Wave (大軍) がランダムなアーキタイプを持てるようにしたり (これによってどのようなグループの敵に遭遇するか、はっきりとは分からなくなります)、スケール値に RandomFloat 変数を使用してスポーンされる敵の数とペースが、常に少しずつ変化するようにしたりすることなどを計画しています。 要するに、Kismet のおかげでレベルが Play-In-Editor (エディタ内再生) によるイタレーションにマッチするため、よりユニークなシーケンス (区切りとなる回数の Wave (大軍) 襲来時に付加的イベントを追加するなど) を作成することが可能となるのです。 (付加的イベントとは、たとえば Wave (大軍) 襲来 5 回ごとに凄い敵と戦うことになるようなことです。ゲームプレイ のオブジェクトがアーキタイプであるため、このようなイベントを実現するのは簡単です。) これから数日間、Kismet を使って Build-Combat-Wave (建設 - 戦闘 - 大軍) のサイクルを調整することになります。これは、設計者主導の、とても楽しい体験となりそうです。次回まで、作り続けましょう!
- 見事な、潜在的 (すなわち長期に及ぶ) 「Wave Spawner (大軍スポーナー)」アクション。これは、敵の集団を表す任意の構造体配列から長期に渡って来襲する敵をスポーンするアクションです。プレイヤーが特定の大軍を殲滅した時のために、適切な出力リンクを伴います。
- 敵の数と Wave (大軍) が押し寄せる間隔を動的にスケーリングするためのアクション。これによって、ゲームを手順に沿って徐々に難しくすることができるようになります。
- UI を開いてカスタムの情報を渡すことができる各種アクション。
- 「敗北条件」(Crystal Core の破壊) を検知するイベント。Crystal Core が実際に破壊される少し前に検知することによって、敗北時の早い段階でカットシーンを起動することができます。
Blog 3: ファイル レファレンス
このブログで取り上げられている情報は、次にあげるファイルに含まれています。(それらのファイルは、Dungeon Defense のソースコードです)。カンマで分けられている行番号は、ファイル内で独立した行が複数あることを示しています。ハイフンで分けられている行番号は、ファイル内で行がその範囲にまたがっていることを示しています。- DunDefManaToken.uc: 8
- DunDefEnemy.uc: 208
- DunDefPlayer.uc: 351
- DunDefManaToken.uc: 62
- DunDefPlayerController.uc: 812
- DunDefPlayerController.uc: 1266
- DunDefPlayerController.uc: 69
- DunDefPlayerController.uc: 1316-1320, 1280
- DunDefViewportInput.uc: 15
- DunDefViewportClient.uc: 474
- DunDefViewportClient.uc: 226
- DunDefUIScene.uc: 11
- DunDefUIScne.uc: 36
- DunDefHUD.uc: 27
- Main.uc: 223, 333, 482, 132
- Main.uc: 482
- Main.uc: 488
- DunDef_AnimNodeScaleRateBySpeed.uc: 17
- DunDefPawn.uc: 285
- DunDefPlayer.uc: 163
- DunDef_SeqAct_EnemyWaveSpawner.uc: 162
- DunDef_SeqAct_EnemyWaveSpawner.uc: 14
- DunDef_SeqAct_EnemyWaveSpawner.uc: 198, 231
- DunDef_SeqAct_OpenKillCountUI.uc: 31
- DunDef_SeqVar_EnemyWaveEntries.uc: 10
- DunDef_SeqAct_EnemyWaveSpawner.uc: 176
- DunDef_SeqAct_ScaleEnemyWave.uc: 53
Blog 4: 10 日目
勇気ある Unreal ファンのみなさん、ご機嫌いかがでしょうか。 前回のブログから数日経ちましたが、その間に我がチームは小規模ながら、また休日だったにもかかわらず、長足の進歩を遂げています。まず概観の説明、その後に各トピックの詳細に入っていきます。 *今回初めてアートワーク (骨格メッシュ) に取り組みました。クールなキャラクターデザインのために! ちょっと気取ったファンタジー常套句ですね! さて、後はアニメーションのために調節して、悲運の UT ロボットを取り替えます。これによってゲーム独自のファッション感覚に磨きがかかります。環境への更なる作業に加えて、武器 Mage Staff (魔法使いの杖) のベースとなるモデルを作りました。仮のビジュアルエフェクトの段階ですが、うまく機能しています。- 我慢できずに、メインメニュー上で気の利いた Render Target が使えるように実装してしまいました。つまり、1 ~ 4 プレイヤー用に、各プレイヤーキャラクターのアニメート化されたイメージをメインメニューで表示できるようになりました。(結局、メインキャラクターの色を換えたバージョンになるのですが)。これによって、誰がゲームにサインインしているかが分かります。接続しているゲームパッドがあればメニュー表示中に Start を押すことによって、それに続くゲームプレイにプレイヤーがサインインできます。また、このサインインに反応して render to texture (テクスチャへのレンダリング) のキャラクターが「アクティブな」アニメーションを再生します。(キャラクターは選択されていない場合にグレイアウト (灰色になって) 「待機中」の状態になっています)。カッコイイ!
- メインメニューをさらに調整して、小さな Canvas パーティクルシステムを作成しました。これは、カーソルの位置からパーティクルを発射するものです。このシステムは、将来他の UI エフェクトにも使用することができます。
- 私は狡猾にも、Matinee を使用してシネマティックス状態にあるときにプレイヤーが動いたり発射したりできないようにしてしまいました。ゲームプレイの導入部とゲームオーバー部のシネマティックスを実装する際に、Player Controller 上に適当な入力ブロックステートを設定したのです。また、プレイヤーが Start/Escape を押したときにシネマティックスをスキップするためのカスタムソリューションを実装しました。
- プレイヤー用 HUD にはかなり精力を傾け、だいたいのことは実装してしまいました。たとえば、マテリアルベースのヘルス値 / 進行状況バー (カスタム UI コントロール)、ステート反応型の魔法のアイコン、アニメート化された Mana Token インジケータなどです。また、HUD のオーバーレイ表示 (動的 Canvas 描画) も実装しました。塔 / Core 上にヘルス値バーが流れます。さらに、攻撃されている Core を指し示すウェイポイントが回転します。これらすべてが 2 ~ 4 の分割スクリーン上で正しく表示されます。素晴らしい。
- 武器のためにインパクト デカールを実装しました。これには、Epic の非常に強力な Material Instance Time Varying (マテリアルインスタンス時間変化) システム (非常に長い名前です) も使用しています。
- 遠隔攻撃をする敵が Archer (弓の射手) 軍団として機能するのための基本的な機能を実装しました。ステートを継承することによって楽に実装できました。ただし、望みどおりの動作をさせるために AI を調整しました。(これには、照準予報 (aim-prediction) や故意による誤差 (deliberate inaccuracy)、発射物方向に関する「補正係数」(fudge-factor) と私が好んで呼んでいるものなどが含まれます)。
Blog 4: ファイル レファレンス
このブログで取り上げられている情報は、次にあげるファイルに含まれています。(それらのファイルは、Dungeon Defense のソースコードです)。カンマで分けられている行番号は、ファイル内で独立した行が複数あることを示しています。ハイフンで分けられている行番号は、ファイル内で行がその範囲にまたがっていることを示しています。- DunDefPlayerSelectUICharacter.uc: 24
- UIImage_PlayerSelect.uc: 38, 48
- UI_CharacterSelect.uc: 97
- UI_CharacterSelect.uc: 65
- UIImage_PlayerSelect.uc: 38, 48
- DunDefViewportClient.uc: 108
- DunDefViewportClient.uc: 14
- DunDefViewportClient.uc: 122
- DunDefPlayerController.uc: 1713, 1726
- DunDefPlayerController.uc: 1832, 1809
- UIImage_HealthBar.uc: 55
- DunDefDamageableTarget.uc: 137
- UIImage_HealthBar.uc: 16
- DunDefDamageableTarget.uc: 154
- UIImage_SpellIcon.uc: 81
- UIImage_SpellIcon.uc: 90
- UIImage_SpellIcon.uc: 11-14
- DunDefCrystalCore.uc: 41-69
- DunDefProjectile.uc: 78-103
- DunDefDarkElfController.uc: 85-107, DunDefDarkElf.uc: 56
- DunDefDarkElfController.uc: 31, 71
- DunDefDarkElfController.uc: 62, 63, 71
- DunDefDarkElfController.uc: 74
Blog 5: 13 日目
皆さんこんにちは。 ここ 2、3 日ほど、開発で大忙しでした! マイルストーン (区切り) となる 2 週間目のビルドを準備するとともに、Frontend ツールを使用してクックを行い、パッケージ化して、我々のエーステスター軍 (友人と家族のことです^^) にリリースしました。これらの機構がしっかりと整っているため、ゆったりとゲームを楽しみ、難しい調整についてはメモをとることができました。美しくも楽しいミニゲームになってきました。私にとってさらに楽しみなことには、もう 2 週間すれば皆さん自身がこのゲームをプレイできるということです。 実装面については、次に、開発の主な事項に関する概観を掲載しました。ほとんどが先の「マイルストーン」の準備に関係しています。- Kismet の機能を追加しました。パラメータ補間を伴った任意のポストプロセスを切り替えることができる潜在的なアクションです。(いつでも必要なときにエフェクトをスムーズにフェードイン / アウトすることができるようになります)。また、ゲームに存在するプレイヤーの数によって、あらゆる Kismet の float 型変数をスケーリングするアクションを追加しました。(プレイヤーの数に応じた乗数の配列を使用します)。マルチプレイヤーのバランスを取ることは難しいため、重要なアクションです。より多くのプレイヤーが存在している場合に、より多くの敵がより短い間隔で Wave (大軍) が押し寄せて来るようにすることができます。ただし、必ずしも、固定化された線形のスケールではありません。
- 新たな塔のタイプを追加しました。これは Blockade (障害物) といいます。敵はこれを破壊あるいは迂回しようとします。(基本的な動的パスファインドを使用します)。また、Attacker Registration (攻撃者登録) システムを作成しました。これは、同時に攻撃できる敵の最大数を、そのターゲットになりうるオブジェクトが指定できるシステムです。(上限を超えた分の敵は先に進み、ふさわしい他のターゲットを得ようとします)。そのため、Blockade に敵が遭遇した場合、そのうち少数の敵がそれを倒そうとし、残りの敵はそれを迂回して進もうとします。
- ゲームでオプションとして選択できる Friendly Fire (味方への誤射) チェック機能を追加しました。これは UT (「Unreal Tournament」) にすでにありますが、このゲームでは UT のクラスを使用していないため、自分で書くことにしました。さらに、プレイヤーのスタート位置に関する追加ロジックを加えました。これによって、ゲームは、新たに参加する各プレイヤーのために、有効なスタート位置を順繰りに使用するようになります。そのため、 4 人のプレイヤーが同時に参加しても、それぞれの一意のスタート位置に必ずつくようになります。
- UI スキンを編集して、独自の「もの凄い」ボタンとフォントを加えました。また、音楽と SFX をゲームシーケンスとメニューのすべてに追加しました。SFX については、AudioComponent をこのゲームの標準である Effect Actor に追加しました。これによってスポーンされるビジュアル エフェクトのすべてが、ただちにオーディオをサポートするようになります。(すべてがアーキタイプの場合は、簡単にオーディオをゲームのイベントほとんどに追加することができます。残りの少数のサウンドエフェクトは通常、アニメーション シーケンス ブラウザを使ってアニメーションに結びつけられます)。
- ゲームをクックしてパッケージ化しました。(ケーキを焼く (bake) かのように)。
- PostProcessEffect を FadeUp 配列または FadeDown 配列に加えようとしたときに、そのエフェクトがすでにその配列のどちらかに存在している場合は、新たに追加する前に古い配列からそのエフェクトを削除する必要があります。さもなければ、同時に 2 つの補間が処理されることになり、混乱が生じます。このようなケースは、Kismet 上で急速に FadeUp / FadeDown 配列の入力をトリガーした場合に起こります。(たとえば、Touched / Untouched を通じて当該のアクションにリンクされているトリガー ボリュームの端のあたりでダンスをする場合などです)。 (7)
- 適切な分割スクリーンのサポートについては、当該アクションは確かに Instigator を受け取り、その PlayerController.LocalPlayer のエフェクトだけを改変します。(オプションとして全員のエフェクトを改変することもできます)。ただし、マテリアルエフェクトが一意のパラメータ値をもつには、一意の MIC (マテリアルインスタンス定数) が必要となります。また、この MIC は、現在のところポストプロセス チェーン エディタ自体の中で直接指定することはできません。そこで私は、マテリアル エフェクトのマテリアルが一意であるか否かを調べる関数を書きました。(そのためには、エフェクトにある現在のマテリアルが、MIC ではないか、あるいは、親が MIC ではないかのどちらかであるということを確かめます。いずれのケースも (このゲームの場合)、まだ一意になっていないことを意味します)。もし一意でないならば、新たな MIC を作成し、元のマテリアルがその親となるようにセットし、作成した新たな MIC をマテリアルエフェクトに適用します。 (8) このようにして、分割スクリーンの一意なポストプロセス変更を適切に行うことができます。よかった!
Blog 5: ファイル レファレンス
このブログで取り上げられている情報は、次にあげるファイルに含まれています。(それらのファイルは、Dungeon Defense のソースコードです)。カンマで分けられている行番号は、ファイル内で独立した行が複数あることを示しています。ハイフンで分けられている行番号は、ファイル内で行がその範囲にまたがっていることを示しています。- DunDef_SeqAct_ScaleFloatForPlayerCount.uc: 10
- DunDef_SeqAct_ScaleFloatForPlayerCount.uc: 22-26
- DunDef_SeqAct_TogglePostProcessEffects: 70-79
- DunDef_SeqAct_TogglePostProcessEffects: 240
- DunDef_SeqAct_TogglePostProcessEffects: 171-233
- DunDef_SeqAct_TogglePostProcessEffects: 236, 248
- DunDef_SeqAct_TogglePostProcessEffects: 139-155
- DunDef_SeqAct_TogglePostProcessEffects: 137, 117
- DunDefTower_Blockade.uc: 21
- DunDefEnemyController.uc: 456-460
- DunDefEnemyController.uc: 492-509
- DunDefEnemyController.uc: 522-530
- DunDefEnemyController.uc: 536
- DunDefEnemyController.uc: 563
- DunDefEnemyController.uc: 538, 545
- DunDefEnemyController.uc: 549
- DunDefEnemyController.uc: 656
- DunDefEnemyController.uc: 648, 602
- DunDefTargetableInterface.uc: 26, 29
- DunDefDamageableTarget.uc: 65, 73
- DunDefDamageableTarget.uc: 61, 93
- DunDefEnemyController.uc: 259, 272
- DunDefEnemyController.uc: 217, 283
- DunDefTargetableInterface.uc: 21, DunDefPawn.uc: 67
- DunDefPawn.uc: 148
- Main.uc: 274, 286
- DunDefEmitterSpawnable.uc: 154
- DunDef_UIAction_PlaySound.uc: 13
- Main.uc: 156
Blog 6: 17 日目
UDK 開発者の仲間のみなさん、こんにちは! Dungeon Defense は最終段階にさしかかっています。ここ数日間、私は AI の動作を洗練させました。さらにその後は、リプレイバリューを高めてくれるメタゲームシステムのいくつかに集中して取り組みました。また、最近完成した多数のアートワークを統合しました。開発プロセスで一番わくわくする瞬間というのは、すばらしいアートによってゲームプレイに息吹が与えられ、それが「Unreal」の技術によって見事にレンダリングされる瞬間です。 さて、コードの側ではどのようなことが行われたか、まずざっと見ていきましょう。その後で各事項について詳細な説明を試みたいと思います。- 敵の AI を改良しました。敵がナビゲーションネットワークからはじき出された場合 (たとえば、ダメージなどのはずみを受けた場合など) や、何らかの理由でターゲットにたどり着けない場合に備えて、「スタック (身動きとれない状態)」チェックを定期的に行うようにしました。これらの場合には、敵 AI は、新たなターゲットを探し、ナビゲーションパスに動的に戻ろうとします。また、Aggro システムを追加しました。(MMO ユーザーにはなじみ深いものです)。これは、プレイヤーが敵にダメージを与えた場合、敵がそのプレイヤーをターゲットにしてより攻撃的になるというシステムです。その際、ダメージの時期や規模といった条件とともに、その他すべてのターゲットファクタにおける重み付けが考慮されます。これによって、敵がより生き生きとするだけではなく、戦術上の深みも加わります。(たとえば、強力な敵から重要な拠点を守るために、ダメージを与えて引きつけ、その拠点から敵を遠ざけるという戦術が考えられます)。
- 基本的なグローバル UI 通知システムを追加しました。これは、ミッション目標やその他のゲーム情報がフルスクリーンで表示されるものです (たとえ分割スクリーンでゲームが行われていても)。複数の通知が重ねられていき、時間経過とともに徐々にスクリーンからフェードアウトします。
- スコアシステムを追加しました。このシステムでは、プレイヤーが敵を倒した場合や、Wave をクリアした場合に得点を得ることができるようになります。スコアが加算されるたびに、Award Name (授与名) が添付されます。これは、スコアを得た理由 (ボーナスや特別得点授与など) を短いテキストで表示するものです。
- また、スコアシステムとハイスコアシステムを結びつけました。これによって、保存されたトップテン (上位 10 傑) のスコアがメインメニューとゲームオーバー画面で表示されるようになります。ゲーム中にトップテンのスコアを達成すると、ゲームの終了時にハイスコアにエントリするために名前を入力するように促されます。複数のプレイヤーの場合にも動作します!
- 基本的なオプション用 UI を追加しました。これによって、オプションを保存し、ゲームプレイ中に変更することもできるようになります。
- ゲームプレイの調節やバランス調整、仕上げのための微調整を多岐にわたって行ってきましたが、みなさんに楽しんでいただければと思います。 ^^
Blog 6: ファイル レファレンス
このブログで取り上げられている情報は、次にあげるファイルに含まれています。(それらのファイルは、Dungeon Defense のソースコードです)。カンマで分けられている行番号は、ファイル内で独立した行が複数あることを示しています。ハイフンで分けられている行番号は、ファイル内で行がその範囲にまたがっていることを示しています。- DunDefEnemyController.uc: 972
- DunDefEnemyController.uc: 715
- DunDefEnemyController.uc: 915
- DunDefEnemyController.uc: 11
- DunDefEnemyController.uc: 109
- DunDefEnemyController.uc: 201, 167
- UI_GlobalHUD.uc: 11
- UI_GlobalHUD.uc: 27
- DunDef_SeqAct_ShowNotification.uc: 41
- Main.uc: 233
- UI_PlayerHUD.uc: 51
- DunDef_SeqAct_ShowNotification.uc: 36
- DunDefPlayerController.uc: 309
- Main.uc: 345
- UILabel_ScoreIndicator.uc: 53, 112
- UILabel_ScoreIndicator.uc: 59, 115
- UILabel_ScoreIndicator.uc: 103
- UILabel_ScoreIndicator.uc: 32
- UILabel_ScoreIndicator.uc: 65, 103
- Data_HighScores.uc: 11-19
- Data_HighScores.uc: 35-64
- UI_GameOver.uc: 75, Main.uc: 186-209
- UI_AddingHighScore.uc: 18-30, Data_HighScores.uc: 35-64
- UIPanel_HighScores.uc: 16-30
- UI_Options.uc: 31-43
- UI_Options.uc: 164-165, 55
Blog 7: 23 日目
皆さん、こんにちは! 前回ブログ以来、ここ 4 ~ 5 日ほどもの凄く忙しかったです。それで非常に多くのことを成し遂げることができました。私たちは現在、このささやかなデモゲームの仕上げに入っているところです。あらゆる要素を磨き上げています。皆さんももうすぐご自分で確かめることができます。そのようなことで、ゲームのバランスとコンテンツの統合に関して、かなり多くの変更を決定しました。ゲームのいろいろな面が、すばらしいメディア (表現媒体) を伴って生き生きとしてくるのを見ると、いつも心が躍ります。もちろん、機能も数多く追加しました。それでは、一緒に主なものを見ていくことにしましょう。- Kismet を使用して、lava pit (溶岩の穴) を変更しました。以前は、大ダメージを与える物理ボリュームでしたが、それに代えて、プレイヤーを安全にテレポートするものにしました。適用されるダメージは少なくなりました。ただし、敵は即死します。このためには、トリガーとなるボリュームにタッチしたものすべてのクラスタイプを調べる Kismet の条件が必要になります。
- 適切なカメラの追跡を追加しました (補間付き)。これによって、カメラが壁を突き抜けなくなります。またカメラの回転方法を変更しました (マウス制御スキームにおいて)。単にマウスをスクリーンの端に動かすだけで良くなりました。また、カメラの FoV (視野角) をビューポートのアスペクト比を使って動的にスケーリングしました。これによって、ワイドスクリーンの解像度または水平方向の分割スクリーンを使用する場合にプレイヤーが遠くまで見ることができるようになります。
- 浮遊するパーティクルエフェクトがマウスカーソルの下に発生するようにしました。これによって、撃とうとしている場所が示されます。また、マウスカーソルが敵の上にあるときは、Particle Color パラメータを使用して色を変化させます。オーナープレイヤーだけがこのパーティクルエフェクトを自身のビューの中で見ることができます。bOnlyOwnerSee アクタ / コンポーネント オプションを使用しています。
- 「ガストラップ」を追加しました。これは、ガスが消えるまで敵を咳き込ませて侵攻を遅らせるものです。その間、塔が敵を徹底的に攻撃します。
- 塔を売ることができる機能を追加しました。塔配置システムからステートを継承しています。もちろん、それに要した費用と塔の現在におけるヘルス値の何割かしか戻ってきません。(減価償却、経済学の常識ですね)。
- 連続して参加するプレイヤーそれぞれが、一意にキャラクターマテリアルがカラー化されることによって、お互いを簡単に見分けることができるようになりました。
- メインメニューに多数のオプションを追加しました。(ガンマ、SFX、音楽ボリュームのスライダー、解像度セレクター、フルスクリーン / ポストプロセス切り替え)。
- ゲームの外観をすっかり変えました! 基本的には漫画的な要素を強くしました。そのためには、ジオメトリ アウトライン化ポストプロセス マテリアルとコントラストの調整を採用しました。私は楽しげな感じを Dungeon Defense で表現したかったのですが、それをはっきりと表すようにしました。
- 解像度の選択とフルスクリーン / ウインドウの切り替え : これについては Checkboxes の配列を使用します。この配列には、ゲームでサポートしている解像度 (1024x768、1280x720 など) の文字列データが含まれます。これらのチェックボックスに ButtonClicked デリゲートを設定します。これは、他の解像度のチェックボックスにチェックが入っていないかどうかを確認するためのものです。もし他にもチェックが入っているならば、それを解除します。(これによって、一度に 1 つの解像度だけが選択されることになります)。また、通常の「チェック解除」の動作をできないようにします。クリックした時にチェックボックスの値を true にすることによって、有効にすることはできても無効にすることができないようにします。 (16) フルスクリーン表示 / ウインドウ表示の切り替えは、単に、デフォルトの on/off の動作をもつチェックボックスで行います。最後に、プレイヤーが [OK] ボタンをクリックすると、現在選択されている解像度のチェックボックスがもつ文字列の値とフルスクリーン切り替えの値を使って、SetRes [resolution][fullscreen] コンソールコマンドを呼び出します。SetRes コンソールコマンドは、実際の解像度を変更するとともに、INI ファイルの中に最新の値を保存します。 (17)
- ポストプロセスの切り替え : これについても、ポストプロセスを切り替えるチェックボックスを追加します。チェックボックスの値は、ViewportClient クラスの config bool 型変数に格納します (他にグローバルなポストプロセスの config 値がないように思われるため)。このポストプロセス bool 値が false の場合は、 ViewportClient 初期化関数の中で、コンソールコマンドの show postprocess (ポストプロセスを表示) を実行することによって、ポストプロセスを off に切り替えます。 (18) これは、ポストプロセスの bool 値がオプションメニューを通じて切り替えられた場合も、そのたびごとに実行され、ViewportClient クラスにある SaveConfig() 関数が呼び出されて、このポストプロセスの bool 値がユーザーの INI ファイルに保存されます。 (19)
- ガンマ スライダー : Slider をオプションメニューに追加し、その最小 / 最大値を適切なガンマ値に設定し、さらに、コンソールコマンドの Gamma [SliderValue] をオプションメニューにいるあいだ毎フレーム実行します。(このためにスクリプトのコールバックを追加するのは気が進まなかったのです)。 (20) また、現在のガンマ値を config 変数として ViewportClient クラスに保存します (そうしなければ保存されません)。さらに、この値をアクティブなガンマ値として ViewportClient 初期化関数にセットします (コンソールコマンドを使用します)。 (21)
- 音楽と SFX のスライダー : これについては、ゲームの全 SoundCues 上で SoundClass を指定するとともに、AudioDevice 上に SoundMode をセットすることによって、各 SoundClass のボリュームをコントロールできるようにする必要があります。Epic の SoundModesAndClasses.upk を自分のゲームの起動パッケージに追加し (そこに備わっている Sound クラスをすべて利用できるようになります)、SoundCue クラスのデフォルトのプロパティを編集して、デフォルトの SoundClass が SFX となるようにします。音楽キューが 「Music」 SoundClass のものであることをエディタで明示的に設定するとともに、ViewportClient の初期化においては「Default」 SoundMode を AudioDevice 上にセットします。エディタで、2 つのエフェクトを「Default」SoundMode の Effects 配列に追加します。1 つは「SFX」 SoundClass のためのもので、もう 1 つは「Music」 SoundClass のためのものです。これによって、それぞれのボリュームを個別に制御できるようになります。次に、現在の SoundMode がもつ Effects 配列の VolumeAdjuster 値を変更する Set Volume 関数を書きます。(配列のインデックスは、先にセットアップした「SFX」および「Music」SoundClass に対応します)。 (22) 最後に、SFXボリューム と Music ボリュームの config float 型変数を ViewportClient クラスに追加し、オプションメニューでこれらの変数に、それぞれの UI スライダーの値をセットします。 (23) オプションメニューにいる間は、SetVolume 関数を呼び出すことによって、スライダー主導の値が AudioDevice の中に継続的に更新されて入ります。 (24) さてこれで何と、リアルタイムに SFX と音楽を個別に調整することができるようになりました!
Blog 7: ファイル レファレンス
このブログで取り上げられている情報は、次にあげるファイルに含まれています。(それらのファイルは、Dungeon Defense のソースコードです)。カンマで分けられている行番号は、ファイル内で独立した行が複数あることを示しています。ハイフンで分けられている行番号は、ファイル内で行がその範囲にまたがっていることを示しています。- DunDef_SeqCond_IsOfClass.uc: 14
- DunDefPlayerCamera.uc: 243
- DunDefPlayerCamera.uc: 278
- DunDefPlayerCamera.uc: 288, 295
- DunDefPlayerCamera.uc: 286
- DunDefPlayerCamera.uc: 145, 205
- DunDefPlayerCamera.uc: 354
- DunDefPlayerController.uc: 1550-1557, DunDefHUD.uc: 56, 62
- DunDefPlayer.uc: 494-504
- DunDefPlayerController.uc: 226
- DunDefPlayerController.uc: 1586
- DunDefPlayerController.uc: 1594-1596, 1606-1609
- DunDefPlayer.uc: 88
- DunDefPlayerController.uc: 270
- DunDefPlayerController.uc: 224, DunDefPlayer.uc: 114
- UI_OptionsMenu.uc: 140-152
- UI_OptionsMenu.uc: 71
- DunDefViewportClient.uc: 297
- UI_OptionsMenu.uc: 68, DunDefViewportClient.uc: 359
- UI_OptionsMenu.uc: 94
- DunDefViewportClient.uc: 299, 354
- DunDefViewportClient.uc: 318
- DunDefViewportClient.uc: 51-52, UI_OptionsMenu.uc: 39-40
- UI_OptionsMenu.uc: 95
Blog 8: 26 日目
皆さんこんにちは。最後のエントリを読んでくださってありがとうございます! (これまでのところ) ゲーム開発に集中した強烈な 4 週間でしたが、全体的にはすばらしい経験をしてきました。画像を使って説明しましょう。 * 1 週目の終わりは次のような感じでした。 * 4 週目の終わりには次のようになりました。 期待どおり、「Unreal」はプロセス全体を通じて見事に機能してくれました。そのおかげで、私たちチームは短期間で実に多くのことを成し遂げることができました。これを読みながら、皆さんが Dungeon Defense を楽しんでいただければと思います。(いえ、読むのとまったく同時ということではなくて。皆さんがマルチタスク機能を搭載しているのでしたら別ですが!) 私を含めてチームは、このささやかなゲームに関する皆さんの感想をとても聞きたがっています。また、このゲームをどのように作成したのかということに関するどんな質問にも答えたいと思っています。そこでぜひ、UDK のフォーラムに参加してみてください。私もそこにいて、思いついた解決策などを披露しています。 これからしばらくは、このブログシリーズの締めくくりとして、ゲームへの全体的なアプローチについて、また「Unreal」を使用したプロトタイプの開発について論じてみたいと思います。以下は個人的な意見ですので、あらゆる状況に適用できるものではありません。また、遍く認められた意見でもありません。ただ、皆さんがゲームを作成する上で、いくらかは役に立つのではないかと考えています。 題して「Unreal ゲーム開発と Jeremy の 8 つのクレージーなルール (おやおや)」です。(ハヌカー (ユダヤ教のお祭り) にちょうど間に合いました)。1. 早い段階でゲームの構造をしっかりと組み立て、後はイタレーション、イタレーション、イタレーション 私は、これまで数多くのプロジェクトに携わってきましたが、そこから学んだことの 1 つには、家を建てる前にはしっかりとした土台が必要になるということがあります。言い換えれば、アート面の製作活動やレベル開発に入る前にすでに、ゲームプレイの構造が面白くかつ楽しくなるような機能をもっていなければならないということです。このような言い方をすると、あまりにも当たり前のことに聞こえるかもしれませんが、実際には、いきなりコンテンツ開発にどっぷり浸かり、それが非常に楽しくて、結局は、土台を最初に作るのを忘れてしまうことがあるのです。頭痛の種を減らすには、コストがかかるアセットを作成する前に (すなわち、少なくても設計処理の前に)、どのようなゲームを作成するのかということを正確に把握するだけではなく、その時点までに当該ゲームがプレイに値するものになっていなければなりません。(これは必ずしもバグがなく、ビジュアル的な説得力があるということを意味しません)。 さらに言うならば、ゲームプレイの構造を早い段階にきっちり整えると、それだけイタレーションの時間を多く取ることができます。繰り返し修正作業を行うことによってゲームが洗練します。イタレーションは、ゲーム開発サイクルの多くの局面で必要になります。それでも、なるべく多くを、開発前の / 構想的なプロトタイピングの段階で解消できれば、それに越したことはありません。 もちろん、すでに述べたように、「Unreal」には迅速なイタレーションをサポートする優れたツールがそろっています。たとえば、Remote Control (リアルタイムで値を変更できます)、Archetypes (値をハードコード化せずに、基本的にデータ主導で扱えます)、Play In Editor (同一のアプリケーションインスタンスにおいてアクティブに修正を加えているレベル内でプレイできます) などです。これらツールのそれぞれを同時に利用するならば (たとえば、Play In Editor 内で Remote Control を開くことができます)、イタレーションを非常に速く行うことができます。ゲームプレイにはとても役に立ちます。 2. プレースホルダー アセットを使用してゲームプレイをプロトタイピングする これまで、こんな経験はありませんか? 3D アーティストにキャラクターを用意してもらって、テクニカルアーティストにそれを整えてもらい、アニメーターに多数のアニメーションを作成してもらったあげく、これらメディアのどれ一つとしてゲームが必要としているものには適さなかったという経験が。そんな経験がないのでしたら、それは運がいいですね。私はそのような失敗をしたことがあります。言うまでもないことですが、アーティストたちは面白くないです。当然ですよね。プログラマーと設計者は、最終的なアートワークを生産パイプラインに投入する前に、そのアートがゲームプレイの目的のためにどのように作成されるべきであるかを知るとともに、担当アーティストに明確にそのことを伝えるべきです。このために私が見つけた最善の方法は、「プレースホルダー」アセットを使うということです。これは、 (たとえば) 人間型キャラクターや武器などのシンプルな汎用的なバージョンのことであって、最終的なアセットを表現するために使用することができます。理想的には、プレースホルダーの大きさ、形、(骨格メッシュの場合は) ボーン構造は、最終的なアセットとだいたい同じになるべきでしょう。ただし、仕組みの複雑さによってはそうする必要はありません。 プレースホルダーアセットを使用すると、「早い段階でゲームの構造をしっかりと組み立てる」(ルール #1 参照) ことができるばかりではなく、担当アーティストが、最終的なメディア (表現媒体) に取りかかる前に、目標とされている動作がゲーム内で機能している様子を見ることができるようになります。プレースホルダーによる実装は、考えられうる限り最も効率的な伝達手段です。さらに、アーティストは自らプレースホルダーアセットを最終的なアセットにダイレクトに交換できます。つまり、抽象的な空間においてのみイタレーションするのではなく、実際のゲームプレイのコンテンツ内においてビジュアルな環境でイタレーションできるということになります。ありがたいことに、「Unreal」を使用することによって一時的なアセットを最終的なアセットに簡単に換えることができます。いくつかの参照 (メッシュや AnimSet など) を Archetype または DefaultProperties において変更するだけで済みます。(コードで直接参照をつける必要はありません!) これでもう大丈夫です。斯くて戒律 #2 は終わりぬ。 3. 「Unreal」の流儀に従う 「Unreal」を使用してゲームプレイの成果を出すには、通常、実に多くの方法があります。しかし、理想的な方法ということになると、かなり少なくなりがちです。そのような理想的な「Unreal の流儀」を実行するには、UnrealScript のインターフェースによって提供される機能をフルに活用しなければなりません。この機能は、C++ や Java などのような基本的なプログラミング言語のそれを遥かに凌駕します。順不同に例をいくつかあげてみます。 [*]一定時間の間、あることを遅れさせたり実行させたりする場合は、潜在性の (latent) ステート関数 (Sleep や MoveTo) または Timer を使用してください。ステートメントが Tick にある場合、Time ベースの処理を多数、実行すべきではありません。 [*]プレイヤーのまわりにいる特定のアクタに関する情報を得る場合は、AllActors を実行して、ワールド内に存在する各アクタからの距離をそれぞれチェックすべきではありません。OverlappingActors を使用して、プレイヤーの半径を与えるべきです。 [*]プレイヤーキャラクター全身に防具のアタッチをセットアップする場合は、それぞれのためにアクタを作成するべきではありません。新たなメッシュコンポーネント (カスタムの Component クラスでも構いません) を動的に作成して、ポーンにアタッチしましょう! [*]1 つのテクスチャによってのみ変化するマテリアルを多数作成する場合は、それぞれのためにユニークな基本マテリアルを作成すべきではありません。親マテリアルを共有するマテリアルインスタンス定数を使用して、ディフューズ テクスチャ パラメータを換えるべきです。 [*]それから、決して、絶対に、間違っても、構造体は参照で渡すものと決めてかからないでください。デフォルトでは、関数のパラメータで out キーワードを使用しない限り、構造体はディープコピーされます。不必要に構造体をディープコピーすると、重くなりメモリも浪費され、なおかつ、構造体変数が一意ではない参照であるとロジックで想定した場合は、バグ発生の温床となっていまいます。Epic はこのことを説明するために UDN のトピックをまるごと 1 つ充てています。^^ UnrealScript とエンジンのフレームワークは、ゲームの実装が極めて確固としたエクスペリエンスとなるように作られています。これは、純粋な C++ による開発から得られた実感です (他の方と同じように私もそのような開発に従事していました)。「Unreal」を使用して開発していると、考えていた以上に独自の機能を発見することでしょう。「Unreal」で、あることを実装しようとしてもがいているときは、エンジンにすでに備わっている機能をあなたが単に知らないだけの場合があります。「ひょっとしたらあるかもしれない」と思ったら、だいたいあるものです。Epic のコード読んで、サンプルを見て、興味をひく UDN の記事を集めると (Dungeon Defense を調べても良いかもしれませんね)、完全ではないにしても、使用パターンが分かるようになるでしょう。それによって、この超パワフルなフレームワークを自由自在に使いこなすことができるでしょう。そして正しい方向に進み... *4. コードベースを調べる! UnrealScript エンジン フレームワークのコードベースは非常に豊富です。また、とても良くドキュメト化されています。ただ、どこから理解していけば良いのか時として途方にくれることがあるでしょう。最初から最後まで読むというのはあまり賢明なやり方とは言えないでしょう。レッドブルの点滴を受け続ければ別でしょうけども。(ただし、手始めに Actor.uc と Object.uc に関して学んでみることはお勧めできるかと思います)。 究極的に、恐ろしい巨大なコードベースを便利な百科事典にする最善の方法とは何でしょうか? ファイルの中をすべて検索する (Find All in Files) ことです。これは、Visual Studio (フリーバージョンは Visual Studio Express) のようなコード用 IDE やその他の編集プログラムに備わっている検索機能です。たとえば、「Cursor」とか「Force」といったキーワード (あるいは通常検索するようなものであれば何でも) を Epic のコードファイルのいくつか、またはすべてから検索することによって、それに関連して Epic がすでに提供している機能について多くのことが学べます。その機能によって、あらゆる種類の一般的なゲームで必要となることを扱うことができます。経験則 : 自作しようとする前に、Epic のコードベースを探し、すでに自分が必要とするものが作られていないか調べてみよ! 探しているものがすでに作られている場合がかなりあって、びっくりするかもしれません。いえ、Gears of War をプレイしていたら、驚かないでしょうね。 5. nFringe を使用する 、以上。 nFringe は素晴らしい。(よし、これは Pixel Mine 社の方に渡さなければ)。nFringe のインテリセンスとコード解析はだいたいいつも適切で、構文チェックも頑固なバグを減らすのに大いに役立ちます (単なる間の抜けたバグとは違って)。nFringe を使用することによって、コードの生産性は飛躍的に向上します。(私の場合だけかもしれませんが)。また、Epic のコードベース (とあなた自身のコードベース) を検索するスピードが非常に速くなります。インテリセンスとメンバーリストによって、簡単にクラスからクラスへと移動することができるようになり、変数や関数、ステートを素早く調べることができるようになります。 もしあなたが 「Unreal」を使用して初めてプログラミングするのであれば、nFringe によって非常に幸先の良いスタートが切れるはずです。このことは、いくら強調しても強調し足りないくらいです。ただし、現在問題が 1 つだけあります。「Unreal」には強力なデバッガがついていますが、nFringe にはロックがかかっているため、アクセスするには商用版の nFringe のライセンスを Pixel Mine 社から購入しなければならないのです。(このバージョンが、現在プロの開発者に唯一利用できるものです)。Pixel Mine 社さん、お願いですから、このとんでもなくもの凄いツールを一般大衆が使えるようにしてください。そうすれば、大衆はあなたの前にひれ伏すことでしょう! と言っても過言ではないですかね。でも、そうですね、nFringe をすぐにダウンロードしましょう。(もし、もっていなければ Visual Studio Express も)。そして、これまで書いたことがないようなコードを書いてみてください。 *6. あらゆるデバッギングの方法を意のままに使えるようにする nFringe を使用してもしなくても、「Unreal」のフレームワークでゲームプレイをデバッグする方法は数多くあります。最高の結果を出すには、さまざまなテクニックをすべて利用する必要があります。数ある方法の中でも、次の方法は私が好んでいる方法です。 [*]描画をデバッグする (球、ライン、ボックスなど): これは、3D 空間で起きていることを視覚化するためのものです。3D 変換の計算結果を確認する場合や、単に大きさを知る場合に便利です。 [*]ログステートメント : 嗚呼、ログよ。 : スパムみたいな場合もありますが、とても有益です。特に、nFringe のデバッガが機能しない場合に! さて、ログを利用すれば、ほとんどあらゆるデータタイプを出力ウインドウに即座に表示することができます。(コンソールコマンドの showlog を使用して呼び出します)。また、@ の記号を使って複数の文字列を連結させ、1 行にできるだけ多くの情報を得ることができます。ただし、Tick の関数内に置きっぱなしにしないように注意してください。ログがスパム化するために、ゲームが重くなってしまいます。したがって、アクタクラスで bDebug のトグル (切り替え) が設定されている場合にのみログをアクティブにするのが最善です。(bDebug は、必要に応じてランタイム時に切り替えることができる編集可能な変数です)。 [*]Unlit (アンリット) モード、ワイヤーフレーム モード : 現在取り組んでいるものがグラフィカルなもので、かつ、レベルの光源処理に混乱が生じている場合、F2 を押すことによって Unlit モードに切り替えることができます。壁を透視しなければならない場合は (ずるいぞ!)、 F1 を押してワイヤーフレーム モードに切り替えられます。見えない敵が何をやっているのかを知るには、驚くほど便利です(のぞいたな!) [*]Show Collision (コリジョンの表示) コンソールコマンドによって、ワールド内のすべてのコリジョンを視覚化します。これは、コリジョンに関係すると考えられる問題に直面した場合に役立つでしょう。玄関に入れないですって? それはひょっとすると、ゲームのバグではなくて、同僚のレベルデザイナーが大きな不可視の障害物メッシュを入り口に置いて隠したのかもしれません。あなたに意地悪するために。Show Collision によってすべてがあらわになります! (より実際的に言えば、ポーンのコリジョンサイズを見るには大変役立ちます)。 [*]Remote Control 上で Time Dilation (時間の遅れ) という設定値を使用して、文字通りゲーム内の時間を遅らせます。(現実の時間を遅らせるわけではないですよ。そうだったらすごいですが、「Unreal」はまだそこまで強力ではありません。。。)。微細なゲームプレイ部分において、ビジュアル エフェクトとアニメーションがどのようになっているのかを正確に調べることによって、不自然な点を探す場合に大変役に立ちます。タイミング関係の問題を解決するために有用です。 [*]Remote Control はまた、ゲームプレイ中にこれまでスポーンされた動的なアクタをすべて表示することができます。それには、アクタリスト上で時計のアイコンをクリックします。破壊されたはずのアクタがまだ生きていないか、ダブルチェックするのに役立ちます。(たとえば、発射物が衝突した後でも、まだそのあたりに残っていないか、など)。 [*]すべての Kismet アクションには Output Comment To Screen (コメントをスクリーンに出力する) オプションがあります。このオプションが有効になっている場合、コメントがインゲームのコンソール画面に表示されます。どのようなアクションがいつトリガーされているのかを調べるのに役立ちます。あるいは、(プロフェッショナルな Kismet 権威者になるには) コンソールコマンド Kismet アクション (「話す」ための) を、任意の Kismet 変数を表示するために私が作成した Concatenate String (文字列連結) アクションと組み合わせて使用します。^^ 数あるデバッギング アプローチの中でも上記のものを使用し、さらに、(願わくは) いつか、だれでも利用できる nFringe デバッガを使うようになれば、あなたは大きな目をしたバグつぶしの魔神になっていることでしょう。すばらしいことです。 *7. Kismet に明るくなろう。ただし... Kismet よ、我々が如何に汝を愛していることか。汝は、レベルデザイナーにゲームを実装する力を与え、ゲームデザイナーに素早くプロトタイピングする能力を与える。汝は与え...そして奪う。レベルのインタラクションを実装する場合、さらにはレベル志向の特定のゲームプレイ構造をプロトタイピングする場合であっても、Kismet は、途方もなく素晴らしく、天下無双のツールとなります。ただし、弱点もあります。まず、とりわけオブジェクト指向というわけではなく、クラスの継承関係を作ることができないということです。また、UnrealScript ほど速くはありません。さらに、UnrealScript には備わっているデバッグ機能をすべて備えているわけではありません。 したがって、たいていのゲームにおいて最終バージョンを作成する場合、Kismet を通じてあらゆることを行おうとするのは、現実的なやり方とは言えません。もちろん、お望みであれば、Kismet を使って可能な限りゲームプレイをプロトタイピングしても良いでしょう。(実際、多くのことができるのですから)。ただし、憶えておかなければならないことがあります。最終的な製品のためには大半を書き直さなければならないということです。Kismet を使用して悪戦苦闘しているのであれば、私見では、UnrealScript を使用することを検討すべきだと思います。(あるいは、機能をさらに拡張する新たな Kismet アクションを書くか)。 私の全般的なルール : 継続的なレベルの設計において起きることであれば、Kismet を使用すべきである。動的にスポーンされ、動的なオブジェクトの動作に関係するものについては (レベル自体ではなく)、UnrealScript を使用するべきである。このルールは、大好きなKismet (どんなことでもしてあげたいほどです) を使用した長い経験から得られたものです。だからこれから言うことで私を悪く思わないでください。ゲーム全体を動的にスポーンする Prefab を使用して書くことは理論的に可能です。しかし、私見では、ある時点を超えると障碍になるのです。でも親愛なる読者の皆さん、ご心配には及びません。私の Kismet に対する愛情は決して褪せることはありません。Dungeon Defense について言えば、ハイレベルのゲームプレイロジック全体が Kismet で制御されているのですから。 *8. 最終的には、楽しめるということ 皆さん、私たちは独立系の開発者です。(Cliffy B、君を除いて。君は凄いセレブになったから! これを読んでくれているかな)。すなわち、楽しめるかどうかということに意識を集中させているのです。(何と言うべきか、つまりその、何億円もの予算に意識を集中しているのではなく)。もちろん、それは我々の多くにとって目標でしょう (みんな、自分の立場は分かっているものです)。そしてもっとパワーを求めてもいるのです。「Unreal」はあなたのその目標とするものへ導いてくれます。 ただし、決して忘れてほしくないことがあります。世の中に対して自分のゲームが素晴らしく、皆がプレイする価値があるということを証明しようとあなたが奮闘しているとき、どのくらいグラフィックスがきれいであるとか (役立ちはしますが)、プレイ時間がどのくらいであるとか (あぁ、人月喰いの Oblivion)、メインキャラクターの乳房にいくつのポリゴンがあるかなどは問題ではないのです。重要なことは、プレイヤーが、インタラクティブなエクスペリエンスを楽しみ、それによってすばらしいフィードバックが得られ、満足のいく見返りを受けられるかということなのです。 皮肉なことに、現場で開発してる設計者がこのことについていつも正しい判断を日々下せるとは限らない、ということがあります。そこで、友人や家族、同僚、ペットの犬たちに節目節目でプレイしてもらって、ゲームの具合についての評価を語って / 吠えてもらうことにしましょう。その評価はいつも心地よいものとは限らないでしょう。建設的な意見であっても良薬は口に苦しになる場合もあるでしょう。しかし、これがあなたとゲームのためになるはずです。「Unreal」は実際、世界で最も強力なゲーム作成テクノロジーです。ただし、それをどのように使うか、バランスのとれた楽しいゲームにするか、それとも、う?ん、Monster Madness みたいなゲームにするか、それはあなた次第なのです!それでは皆さん。がんばって、これからも夢を現実に変えていきましょう^^ -Jeremy Stieglitz