Quantcast
Channel: historia Inc –株式会社ヒストリア
Viewing all 989 articles
Browse latest View live

[UE4] 応募作品50突破!!第4回UE4ぷちコン応募作品一挙公開!その3

$
0
0

 

※コンテストの告知ページはこちらです。

・応募作品50突破!!第4回UE4ぷちコン応募作品一挙公開!その1はこちらです。

・応募作品50突破!!第4回UE4ぷちコン応募作品一挙公開!その2はこちらです。

[エントリーNo.39]Mirror’s Eye

malony 様

▼応募者コメント▼

潜入&捜索撃破ゲーム。敵は鏡に反射した姿が見えるのみで、通常の視界では捉える事が出来ないので注意して進む必要がある。

ただし、敵からプレイヤーは鏡を通しては見ることが出来ないので、それを利用して…

武器は角材(近接)、手榴弾の2つ。

 

[エントリーNo.40]レッド&ブルーと謎の浮遊大陸

どギー@ゆーま 様

▼応募者コメント▼

このゲームは2人プレイ用の2.5Dシューティングゲームです。それぞれキーボードとゲームパッドで操作します。プレイヤーのおもな操作は、移動、通常弾、反射弾、

ボムです。通常弾と反射弾は壁や敵、一部の敵の弾で跳ね返ります。敵の弾の中には分かりやすい赤と青のものがあります。

その弾は自機と同じ色であれば吸収することができます。吸収することで仲間を守ることができる上、

エネルギーがたまっていき、最大まで行くと通常弾が強化されるほか、エネルギーを使ってボムを撃つことができます。

敵の弾を吸収してカウンター、これも反射要素となります。できるだけ多くの敵を倒し、残機を多く残すことでスコアを競います。

 

[エントリーNo.41]Star Collector

AKIRA 様

▼応募者コメント▼

今回は「反射」がテーマという事ですが流星群が星空の中で、反射を利用してさまざまな動きをしながら流れてきたら美しいだろうなと考え、夜空を流れる流れ星をプレイヤーが次々と拾い集めていくという趣旨で

作品を制作させていただきました。

 

[エントリーNo.42]反射ローグ

湊 和久 さま

▼応募者コメント▼

反射とローグを組み合わせました。フォントと音源以外は、絵素材も全部作りました(炎エフェクトより執筆した本のアセットより流用)。

 

[エントリーNo.43]Nice Shot!!

Haming 様

▼応募者コメント▼

よく跳ねる玉と反射角を使って的(キューブ)を台から落とすゲームです。角度を調節しながら撃つべし!撃つべし!

[エントリーNo.44]早撃ち アストロロボ・SSK

チームTL 様

▼応募者コメント▼

アストロロボ・SSKの今度の敵は前線基地ヒストリア近くの遺跡に眠っていた謎のゴーレム。調査に出たSSKはカウンターアタックを得意とするゴーレムたちに囲まれてしまった!

反射神経を研ぎ澄まして謎のゴーレムを倒せ!

というストーリーの反射神経早撃ちゲームです。

 

[エントリーNo.45]跳弾サバイバー

sleepingdragon 様

▼応募者コメント▼

跳弾を使って敵を倒しながら進むゲームです。弾丸の反射(跳弾)の回数を一定数満たして攻撃しないと倒せない敵もいます。

最終的に各ステージのボスを倒せば次のステージに進みます。

 

[エントリーNo.46]Reflexearn(リフレキシアン)

tea_basira 様

▼応募者コメント▼

タイトルはReflex(反射)とEarn(稼ぐ)をまぜた造語です。ゲームもそんな感じになっているシューティングになります。

 

[エントリーNo.47]Reflection Road

EXE 様

▼応募者コメント▼

弾を反射させて道を作りゴールを目指すゲームです。半透明のブロックは、弾と反射して作成した道のみ無効化しますので半透明のブロックに当たらないように撃ちゴールを目指します。

クリア時の評価は、クリアまでの時間と使用した弾数から計算されます。

 

[エントリーNo.48]Reflective Great Beam

らりほま 様

▼応募者コメント▼

BGMのリズムに合わせて自動で発射されるビームをブロックに当てて破壊するゲームです。ビームは同じ色のものにダメージを与えますが、違う色のものに当たると色を吸収して反射します。

反射を上手く使ってブロックを破壊し、スコアを稼ぎましょう。

ビームが発射されるタイミングで同じ色のボタンを押すと攻撃力が上がるといった、リズムゲームの要素もあります。

3Dモデルやモーションなど、様々なアセットの自作を行いましたが、今回は特にBGM制作に力を入れました。

UE4関連のネタも散りばめましたので、是非ご覧ください。

 

[エントリーNo.49]Variable Reflection

おかず@pafuhana1213 様

▼応募者コメント▼

スタート位置から発射される光を、「反射」ブロックを操作することで、ゴールまで導くゲームです。

じっくりと事前に作戦を練るタイプではなく、

試行錯誤を繰り返していく脳筋タイプなパズルゲームにしてみました。

あと、VR+ゲームパッド専用です。

 

[エントリーNo.50]Decision

パマギー 様

▼応募者コメント▼

反射ということで球の反射を用いたパズルアドベンチャーです。制限された球数でレベルをこなしていきます。レベル毎にギミックは変化していきます。

手探り的なアドベンチャーを目指したので動画内の説明は極力省きました。簡易的ですが曲は自前です。

 

[エントリーNo.51]臨界侵食α7: ブロッケン・オーバーグロウス

Reminisce 様

▼応募者コメント▼

試作兵器の実験場に現れた謎のキューブ「サイファー」と、成長し続ける巨人「ブロッケン」。これらを調査し撃破するため、試作兵器「CcA」を駆使し、目にも止まらぬ速さで飛翔せよ!

際限なく加速する操作感、スピード感、浮遊感を重視したガチのアクションゲームを目指しました。

 

[エントリーNo.52]Reflex Reflector

でんあつ 様

▼応募者コメント▼

2色の「反射面」を持つプリズムを回転させ、敵の光弾を反射するゲームです。光弾をうまく反射させ敵に当てることで敵を倒すことができ、大幅に得点が入ります。

光弾の反射に失敗するとダメージを受け、ダメージがたまるとゲームオーバーです。

だんだん敵の攻撃が速くなるため、最終的には反射神経頼りで光弾を反射することになります。

ハイスコアはランキングに登録することができます。

 

[エントリーNo.53]だいぶ道頓堀

栗坂こなべ 様

▼応募者コメント▼

ゲームではなく、観賞用の観光コンテンツです。道頓堀川の水にネオンが映える美しい繁華街を再現したくて作りました。

オープンワールドなミナミの街を自由に歩き回って鑑賞できます。

地図やロケハンを元にした、ほぼ忠実な大きさの道頓堀です。

まだまだ作り込み途中で、細部がフラットなままですが、

最終的にはパッと見実写に見える道頓堀アセットの公開を目指しています。

テーマ的には道頓堀の川やらなんやらで反射しつつ、自動車を弾き飛ばします。

 

[エントリーNo.54]暗黒迷宮ダッシュツLEVEL2

mwsss 様

▼応募者コメント▼

光の玉を投げて迷宮を脱出するゲームです。

 

[エントリーNo.55]Mirror Writing

Tatsunoru 様

▼応募者コメント▼

ネットワーク対戦で魔法をうちあい、反射しあうというゲームです。文字を書くとそれが魔法となり、その魔法文字の鏡文字を書くことで反射できます。

 

[エントリーNo.56]AM 2:00

ユキマサ 様

▼応募者コメント▼

光の反射でできる影がテーマです。内容としては一人称視点のホラーゲームです。よくある形式のゲームですが、

敵は普通には視認できず近距離から強い光を当てて確認するという形式にしました。

もうすこしホラーゲーム要素を盛り込んでこれからも作りこんでいこうと思っています。

 

[エントリーNo.57]跳霊の剣

おぎまふ 様

▼応募者コメント▼

2頭身のキャラが敵の攻撃を反射し、蹴散らして突き 進んでいく爽快3Dアクションゲームです。

 

[残念賞]Sphere Rush!

U 様

▼応募者コメント▼

このゲームは 発射台から自動で発射される同じ色のスフィアを繋げて消すパズルゲームです消しきれなかったスフィアが発射台にまでたどり着いてしまうとゲームオーバーになります

そうならないようにプレイヤーは発射台を操作して、スフィアを繋げて消しましょう

今回のぷちコンのテーマである「反射」がスフィアを壁に打った時の「反射」する動きと、

次に発射される玉を予測し「反射」的な判断力が必要になる要素がこのゲームの反射要素です

残念ながら、応募受付時間終了してしまってからの応募となった作品となります。

 

・応募作品50突破!!第4回UE4ぷちコン応募作品一挙公開!その1はこちらです。

・応募作品50突破!!第4回UE4ぷちコン応募作品一挙公開!その2はこちらです。

 


[UE4] 第4回UE4ぷちコン、ノミネート作品発表!なんと8作品!!

$
0
0

第4回UE4ぷちコンのノミネート作品発表です。

57作品ものご応募の中から、ノミネート作品が決まりました!!

たくさんのご応募、誠にありがとうございました。

ノミネート作品は6作品の予定でしたが、熱い意見がぶつかり合う審議の結果、8作品となりました。

 

ノミネートされました8作品は

10月18日(日)パシフィコ横浜で開催の【UNREAL FEST 2015 YOKOHAMA】会場にて展示いたします。

この中から参加者さまの投票によって最優秀賞が決まります。ぜひご来場くださいませ。

(※参加の際は事前登録をお願い致します→https://atnd.org/events/70010)

 

それでは、ノミネート作品をご覧ください!

審査員:エピック・ゲームズ・ジャパン 下田 純也さま、株式会社ヒストリア代表取締役 佐々木 瞬

 

[エントリーNo.16]ロールシャッハ

ぶっさん 様

 

[エントリーNo.35]REFLEKT

@NKDTR 様

 

[エントリーNo.36]ソルベークの歌

kurosawa 様

 

[エントリーNo.39]Mirror’s Eye

malony 様

 

[エントリーNo.43]Nice Shot!!

Haming 様

 

[エントリーNo.46]Reflexearn(リフレキシアン)

tea_basira 様

 

[エントリーNo.48]Reflective Great Beam

らりほま 様

 

[エントリーNo.57]跳霊の剣

おぎまふ 様

 

ノミネート、おめでとうございます!

※コンテスト告知ページはこちらです。

  • 応募作品50突破!!第4回UE4ぷちコン応募作品一挙公開!その1はこちらです。
  • 応募作品50突破!!第4回UE4ぷちコン応募作品一挙公開!その2はこちらです。
  • 応募作品50突破!!第4回UE4ぷちコン応募作品一挙公開!その3はこちらです。

[UE4] 実機のプロファイリング!(Android編)

$
0
0

今回は実機を使用してのプロファイルの方法を書いていこうと思います。

ただし今回ご紹介する方法で使用できる端末は限られていますので注意してください。(※1)

 

・手順その1  NvidiaのAndroidWorksをダウンロードする

今回プロファイルをするためにNVIDIAの開発ツールセット[Nvidia GameWorks]の[Tegra System Profiler]を使用します。(※2)

ダウンロードするが完了すると「AndroidWorks-1R2-win.exe」というのが取得できますのでこれをダウンロードの手順にそって進めます。

完了することで[Tegra System Profiler]が使用できる状態になります。

 

・手順その2  [Tegra System Profiler]の起動

無題

起動すると画像のような画面表示されます。

そしてプロファイリングを行いたい端末をUSBに繋ぐと有効なプロジェクトがリスト表示されますので右クリックで選択します。

すると画像の下のほうにある[Andrioid package name]に指定したプロジェクト名が表示されますのでそのまま[Next]を押します

 

・手順その3  プロファイルを開始

[Analysis Options]で設定(基本そのままでも大丈夫です)後、[Start]で解析をはじめます。

無題_1

[Stop]を押すと終了しそれまでの解析結果をReportとして表示してくれます。

解析結果の見方はドキュメントに詳しく記載されていますが、CPUの使用率とそのとき呼び出された関数などが確認できます。(※3)

実行しながら確認できないのは残念でずが、Reportは複数もてることが可能です。

無題_2

 

 

今回は実機でのプロファイリングでしたがエディター上でもエミュレータのプロファイリングは可能です

そちらはUE4のドキュメントや、以前ブログでも紹介させていただいたコンソールコマンドなどもご参考にしていただけたらと思います。

 

※1:こちらでお持ちの端末がサポート対象かご確認してください

※2:[実機でデバックしよう]で紹介したときでも書いたのですがすでにダウンロード済みだった場合はこの手順はスキップしてください。

※3:解析終了後でもUSBに実機は繋いでいないと見れないので注意してください。

[UE4] 第4回UE4ぷちコン、最優秀賞作品発表!

$
0
0

第4回UE4ぷちコンの最優秀作品の発表です。57作品ものご応募ありがとうございました!

今回は10月18日(日)パシフィコ横浜で開催された【UNREAL FEST 2015 YOKOHAMA】にてノミネートされた8作品を展示し、

(ノミネート作品はこちら!)

参加者さまの投票によって最優秀賞が決まりました!

それでは、最優秀賞作品をご覧ください!

 

[最優秀賞]跳霊の剣(投票数51票)

おぎまふ様

 

 

[2位]ソルベークの歌(投票数40票)

kurosawa様

 

[3位]ロールシャッハ(投票数34票)

ぶっさん様

 

 

沢山のご投票ありがとうございました!

また次回も開催予定(冬くらい?)なので、その際にはぜひよろしくお願い致します!

[UE4] 4.9におけるパッケージングまとめ ~小規模タイトル編~

$
0
0

皆様、アンリアルフェスはお疲れ様でした。
懇親会等で多くの方と交流でき、とても有意義な時間を過ごすことができました。
日に日にUE4の勢いが増していっているので、ヒストリアとしても負けないように良いコンテンツを作っていきたいですね。

さて今回はUE4.9におけるパッケージング方法についてまとめてみました。

 

UE4におけるパッケージングの仕組みについて

UE4ではレベルを起点に、紐付くアセットをかき集めてパッケージングするという方式を取っています。

Packaging9

ちなみにレベルや各アセットに関して、具体的にどのアセットが紐付いているのかを確認したい場合、「アセットを右クリック」→「Asset Actions」→「Migrate」をすることで簡単に確認ができます。

以下は StarterContent に含まれている「Minimal_Default」レベルに紐付くアセットのリストです。

Packaging03

 

個人制作等の小規模タイトル向けのパッケージング手法

エディタ上からパッケージングする方法がオススメです。

以前はエディタ上からのパッケージングだと強制的に全レベルを内包するようになってしまっていたため、外部アセット等に含まれている使用していないレベルですらパッケージング対象となってしまい、Migrate等を駆使してプロジェクト内の不要アセットを削除していく作業がほぼ必須となり使いにくいものでした。

どのバージョンからなのかは確認できていませんが、UE4.9ではパッケージングに含むレベルを明示的に指定できるようになっています。
プロジェクト設定を開き、Packagingタブから以下の項目を設定します。

  • Cook only maps (this only affects cookall) にチェックを入れる
    →これでパッケージングする際に指定のレベルのみ含まれるようにします
  • List of maps to include in a packaged build にパッケージに含みたいレベルを指定する

Packaging01

 

これらの設定を行った後、「File」→「Package Project」から任意のプラットフォームを選ぶとパッケージングできます。

試しにパッケージングしてみる

以下の条件でパッケージングし、パッケージのサイズを比較してみました。

  • 新規プロジェクトを作成後、無料で公開されている「Infinity Blade: Grass Lands」をプロジェクトに追加 ※サイズ検証結果をわかりやすくするためです
  • プラットフォームは Windows (64-bit)
  • 検証は「Cook only maps」を設定せずに全レベルを含む状態と、StarterContent に含まれている「Minimal_Default」レベルのみを含む状態

結果は以下の通りです。

Development Shipping
全レベル 1106 MB 1065 MB
Minimal_Default のみ 297 MB 253 MB

サイズが大きく違っており、設定が有効になっていることが確認できます。

また、パッケージングにかかる時間も短縮することができるため、パッケージング時のみに発生するバグの確認等をする場合に、テストレベルを作って繰り返し検証することもやりやすいかと思います。

 

パッケージに含まれていないレベルを参照しようとしたらどうなる?

当たり前ですが、正常な挙動にはなりませんのでご注意下さい。

例えばレベルAにてレベルBを OpenLevel していた場合、パッケージにレベルBを含んでいなかったらレベルAが再読み込みされるようになります。

 

パッケージングに関する設定ファイルの場所

「Config」ディレクトリ内の「DefaultGame.ini」内にエディタ上から設定された内容が反映されています。

[/Script/UnrealEd.ProjectPackagingSettings]
BuildConfiguration=PPBC_Development
StagingDirectory=(Path=”D:/Blog/PackagingTest/Package”)
FullRebuild=False
ForDistribution=False
IncludeDebugFiles=False
UsePakFile=True
bGenerateChunks=False
bBuildHttpChunkInstallData=False
HttpChunkInstallDataDirectory=(Path=)
HttpChunkInstallDataVersion=
IncludePrerequisites=True
IncludeCrashReporter=True
InternationalizationPreset=English
-CulturesToStage=en
+CulturesToStage=en
DefaultCulture=en
bCookAll=False
bCookMapsOnly=True ←指定のレベルのみを含むかどうか
bCompressed=False
+MapsToCook=(FilePath=”/Game/StarterContent/Maps/Minimal_Default”) ←パッケージに含むレベル
+MapsToCook=(FilePath=”/Game/StarterContent/Maps/Advanced_Lighting”)←パッケージに含むレベル

 

このように設定は ini ファイルとして管理されているため、例えば指定のディレクトリ以下に含まれる全レベルを集め、ini ファイルの中身を書き換えるようにすることでパッケージ設定への追加漏れを防ぐツール等も作ることは可能です。

 

次回は ProjectLauncher からのパッケージングや、Jenkins等からも実行できるようにバッチ化の方法について書く予定です。

[UE4] 4.9におけるパッケージングまとめ ~ProjectLauncher & Console編~

$
0
0

前回に引き続き、パッケージングについて書いていきます。

今回は ProjectLauncher を使ったパッケージングと、コンソールコマンドからのパッケージングを紹介します。

 

ProjectLauncher とは

ProjectLauncher とは、開発作業やテストを簡素化及び迅速化する「Unreal Frontend」に含まれるツールです。

Unreal Frontend

以下、ProjectLauncher に関する公式ドキュメントからの引用です。

ユーザのゲームをビルド、クック、デプロイ、起動します。UFE はローカルまたはネットワーク上どちらかで接続して、 異なるターゲットプラットフォームで実行される複数のターゲットデバイスへ、同時にデプロイすることができます。単一またはマルチプレイヤー クロス プラットフォームゲーム のテストのワークフローを大幅に迅速化するために、デバイスごと (サポートするプラットフォーム) に複数インスタンスを起動したり、 インスタンスごとに異なった役割を設定します。

 

UE4.9.2 ではエディタのメニューから「Window」→「Project Launcher」と選択すると表示されます。

 

プロファイルを作成する

ProjectLauncherを使うために、ProjectLauncher の起動オプション設定をまとめたプロファイルを作成する必要があります。

エディタのメニューから「Window」→「Project Launcher」として ProjectLauncher を表示し、プロファイル作成ボタンを押します。

Packaging10

 

 

プロファイル設定画面が表示されます。

Packaging11

設定できる項目が多いので、順番に説明していきます。

 

1.プロファイル設定名と説明

プロファイル設定はファイルとして出力され、識別するための名前と説明を入力できます。

 

2.プロジェクト設定

対象とするプロジェクトを設定できます。

プルダウンを選択し、「Browse…」から対象プロジェクトの uproject ファイルを選択して下さい。

 

3.ビルド設定

今回はビルドが必要なので、ビルド設定の中にあるチェックボックスを有効にします。

有効にすると Build Configuration がプルダウンで選べるようになるので、Development や Shipping を選んで下さい。

 

4.クック設定

クック設定では右のプルダウンから「By the book」を選択します。

その後、設定画面が追加されるので、各種設定を行っていきます。

 

下記に主要な設定項目を列挙します。

Cooked Platforms クック対象のプラットフォームです。
Cooked Cultures クック対象の言語です。
Cooked Maps クック対象のレベルにチェックを入れることができます。

※現状(4.9.2)だとここに表示されるレベルは Content/Maps 以下にあるものだけです。
 修正予定とのことですが、ハマりポイントなのでご注意下さい。

Only cook modified content 有効にすることで、前回クック時から差分があるものだけがクックされるようになります。

基本的にはクック時間の削減のために有効にしておくのが推奨ですが、こちらにチェックを入れていた場合は使われなくなったアセットでも既にクック済みだった場合は無条件にパッケージに含まれてしまいますのでご注意下さい。

Compress content コンテンツを圧縮するかどうか。
Store all content in a single file (UnrealPak) コンテンツをパックするかどうか。

パックすることで、転送時間を削減することが可能です。

パッケージングにどのコンテンツが含まれているのかを確認したい場合はチェックを外して下さい。

Cooker build configuration ビルド設定(Development、Shipping等)です。

 

5.パッケージ設定

今回はローカルにパッケージングされたものを保存するので、右のプルダウンから「Package & store locally」を選択します。

 

6.デプロイ設定

各種デバイスに対してデプロイして確認ができますが、今回はパッケージングが目的なのでデプロイはしません。

右のプルダウンから「Do not deploy」を選択します。

 

今回は最終的に以下の様な設定にしました。

Packaging12

Packaging13

 

パッケージングする

ProjectLauncher ウィンドウに戻り、作成したプロファイルの右側にある「Launch this profile」ボタンを押すと、パッケージングが始まります。

正常にパッケージングが完了すると、以下の様な画面になります。

Packaging14

 

パッケージの格納場所

パッケージングが成功すると、以下のディレクトリに保存されます。

[プロジェクトディレクトリ]/Saved/StagedBuilds/[プラットフォーム]/[プロジェクト名].exe

実行して正常に遊べれば完了です。

 

コンソールからのパッケージング

Jenkins等で定期ビルドしたい場合はコンソールからパッケージングができないといけません。

実は ProjectLauncher がパッケージングした際に出力したログの中に、コンソールから実行できるコマンドが含まれています。

該当のコマンドは3行目に出力されています。

Automation.ParseCommandLine: Parsing command line: BuildCookRun -project=D:/Blog/Packaging/Packaging.uproject -noP4 -clientconfig=Shipping -serverconfig=Shipping -nocompile -nocompileeditor -rocket -utf8output -platform=Win64+Win64 -targetplatform=Win64 -build -cook -map=PackagingTest -unversionedcookedcontent -pak -compressed -stage -package -cmdline=PackagingTest -Messaging -addcmdline=-SessionId=085A7C22408059A8EE4ECE8126C08E42 -SessionOwner=rhara -SessionName=’Test Profile’

 

これを AutomationTool から実行するために加工します。

AutomationTool は下記のディレクトリにあります。

C:\Program Files\Epic Games\4.9\Engine\Binaries\DotNET\AutomationTool.exe

 

まず、先程のログにはセッション関係のコマンドがあるので、そちらは不必要なので取り除きます。

それから BuildCookRun 以降のものをそのまま引数として使用すれば良いので、最終的には下記のようなコマンドとなります。

AutomationTool.exe BuildCookRun -project=D:/Blog/Packaging/Packaging.uproject -noP4 -clientconfig=Shipping -serverconfig=Shipping -nocompile -nocompileeditor -rocket -utf8output -platform=Win64+Win64 -targetplatform=Win64 -build -cook -map=PackagingTest -unversionedcookedcontent -pak -compressed -stage -package -cmdline=PackagingTest -Messaging

これをコマンドプロンプトから実行し、正常にパッケージングができれば完了です。

 

出力先を変更したい場合

先程のコマンドに以下のコマンドを追加して下さい。

-archive -archivedirectory=[出力先ディレクトリ]

 

問題点

できればプロファイルの更新によってコンソールコマンドを変更しなくても良いように、プロファイル指定でコンソールからパッケージングしたかったのですが、そちらは見つけることができませんでした。

AutomationTool や UnrealFrontend のコマンドライン引数も確認してみましたが、それらしきものが見つからず…。

 

また、プロファイル設定は下記のディレクトリに保存されており、開発者間で共有しにくいのも問題点として挙がっています。

C:\Users\[ユーザー名]\AppData\Local\UnrealEngine\4.9\Saved\Launcher

こちらは将来的に改善されることを願っています。

 

終わりに

まだまだパッケージング周りは問題点もありますが、以前のバージョンから比べると確実に機能が揃ってきて安定してきています。

今後もどんどん改善されていくと思うので、情報が新しくなったらまた差分をまとめたりしたいと思います。

 

[UE4] 環境変数を利用してビルドオプションを変更する

$
0
0

今回は、最近発見したちょっと便利な .Build.cs の使用方法をご紹介したいと思います。

プログラマ向けの内容になります。

 

例えば、

  • 新機能の開発中に、全体に共有したくはないけれどバックアップのためにコミットはしておきたい
  • 製品版では必要な機能だけれど、開発中は無効にしておきたい
  • 手元のビルドでは有効にしておきたいが、Jenkinsで共有されるビルドには反映したくない

などといったケースは、ゲーム開発ではよくあることと思います。

 

こういった場合、C++では #ifdef などのプリプロセッサ命令を使うのが一般的です。

通常のC++プロジェクトでの開発ではVisualStudioなどのプロジェクト設定に定義を記述し、目的ごとに構成を用意するところですが、UE4のC++プロジェクトでは基本的には「Development Editor」「Shipping」などの、予め用意された構成を使用することになります。

 

UE4プロジェクトの場合は、プロジェクト設定に記述する代わりに、モジュールの「.Build.cs」ファイルに記述することで、define定義を追加することが出来ます。

「.Build.cs」ファイル内で、「Definitions」という配列変数に要素を追加することで、そのモジュール内で指定したdefineが定義された状態でC++ビルドが実行されます。

.Build.cs 
 Definitions.Add(“MY_LOCAL_TEST”);
.cpp 
#ifdef MY_LOCAL_TEST

 

これだけでは、定義を変更するためには、毎回「.Build.cs」ファイルを書き換える必要があり、それほどのメリットは感じられません。

しかし、「.Build.cs」は単なる設定ファイルではなく、実行されるC#言語のコードです。
つまり、C#言語の機能による動的な変更が可能であるということです。

 

例えば、下記のような使い方が可能です。

 .Build.cs
if(System.Environment.GetEnvironmentVariable(“BUILD_OPTION_XXX”) == “1”)
{
Definitions.Add(“BUILD_XXX”);
}

これで、環境変数によって、目的のビルド環境のみで有効になるようなコードを記述することが出来ます。

他にも、System.Environment.UserName を用いて個人環境のみで有効なデバッグコードを実装したり、外部テキストファイルに独自の設定を記述して読み込み、反映させるようなことも可能です。

 

Jenkins等でバッチビルドする場合には、コンソールコマンドの「set」と組み合わせれば、簡単にビルドを切り替えられるようになります。

ある程度以上の規模のプロジェクトでは、とても便利に使えるテクニックだと思いますので、是非お試し下さい。

[UE4] 実機でデバックしてみよう!(iOS編)

$
0
0

今回は以前書かせていただきましたAndroid実機を使ってのデバックのiOS編です。C++を使用したプロジェクトではXcodeを使用する必要があるためMacでの開発環境があることを前提に話を進めさせていただきます。

 

デバック時の開発環境

  • MacBookPro(Retina 2013)
  • OS X Yosemite
  • XCode  version 7.1

 

確認したデバイス

  • iPhone6       iOS9.1
  • iPod 第6世代  iOS 8.4

 

手順その1:テストするデバイスを追加する

まずはじめに、デバックするためにデバイスを開発用として登録する作業を行います。ドキュメントのiOSのクイックスタートが非常にわかりやすいのでこれの手順にそってプロビジョニングの作成、デバイスの登録などを済ませます。

 

手順その2:プロジェクトをXCodeから起動する

[プロジェクト名.xcodeproj]を指定しXCodeを起動させます。もしない場合は[プロジェクト名.uproject]を右クリックし[サービス]から[Generate Xcode Project]を選択してください。このあたりはWindowsと同じですね。

無題

 

 

手順その3:XCodeからエディターを起動

無題2

UE4ではすでに幾つかのスキームが設定されているのですがその中の[プロジェクト名Editor – Mac]を選択しデバイスを[Mac]にして起動します。画像ではテスト用に今回[MyProject]というプロジェクト名だったので[MyProjectEditor – Mac]となっているのがわかると思います。

 

手順その4:デバイスにインストールするためLaunchを選択する

無題3

 

今回使用するデバイスを指定しインストールを行いましょう

 

手順その5:XCodeからデバイスを指定起動する

無題

デバイスにインストールできたことが確認できたら今起動してるエディターはもう使用しませんので終了させます。再度エディターを起動したxcodeprojのスキームを今度は[プロジェクト名 – iOS]にしデバイスを先ほどインストールしたものを指定します。

ここで注意なのですがこのときにCleanやBuildはせずそのまま起動します

これはXcodeのBuild Settings->Build Locations->Per configuration Build Products Pathを見るとここで指定してるのが「プロジェクト名フォルダ/UE4/Binaries/IOS/Payload」になっており手順4行ったLaunchしたときに生成されたappを指定してることがわかります。無事起動できればあとは自分の作成したコードの好きな位置にブレイクポイントをすれば普通に変数の内容など確認することができると思います。

 

長くなりましたが以上で実機でのデバック方法についての説明を終わりたいと思います。お疲れ様でした。


[UE4] StarterContentを後から足したり、削除したりする方法

$
0
0

プロジェクト作成時には不要だと思ったStarterContent、後から欲しくなったことはないでしょうか?

わざわざ新規プロジェクトから移行しなくても、簡単に既存のプロジェクトに追加することが出来ます。

 

不要になったStarterContent内のアセット、削除したはずなのに、いつの間にか復活していた。。。 そんな経験は無いでしょうか?

プロジェクト作成時に追加されたStarterContent内のアセットは、削除しても復元されます。

 

今回は、この辺りのStarterContentの出し入れの方法をご紹介します。

 

***

 

StarterContentを含むプロジェクトを作り、Config/DefaultGame.ini の記述を覗いてみましょう。
下記のような記述があるハズです。

[StartupActions]
bAddPacks=True
InsertPack=(PackSource=”StarterContent.upack”,PackName=”StarterContent”)

 

実は、StarterContentは、最初に単にアセットがコピーされるだけでなく、「このパッケージをこのプロジェクトに追加登録する」という状態になっています。
パッケージが追加された状態であれば、エディタ起動時に内容がチェックされ、不足したファイルなどは大元のエンジンのインストールフォルダから再コピーされます。
これが、削除してもいつの間にか復元されてしまう挙動の正体です。

 

つまり、後からStarterContentを足したい場合、DefaultGame.ini をテキストエディタで開いて、上記の内容をコピペして再起動すればOKです。
逆に削除したい場合は、DefaultGame.ini から上記の記述を消したうえで、アセットを削除すればOKです。

 

StarterContentに限らず、マーケットプレイスからダウンロードして「プロジェクトに追加する」タイプのアセットパッケージは、同様の仕組みとなっています。
これを知ってしまうと、今度はStarterContentの中身を編集するのが少し怖くなってきますね...

[UE4] BumpOffsetはこんな使い方もできる!

$
0
0

初めまして!新人アーティストの渡邊です!今回は未熟ながらBumpOffsetについて説明します。

BumpOffsetとは

アンリアル エンジン 4 用語であり、一般的に知られている「視差マッピング」のことを指します。Bump Offset 表現式は、ジオメトリを追加することなく深度の錯覚を与えることができます。BumpOffset マテリアルは、グレースケールの 高さマップ を使用して深度情報を与えます。高さマップの値が明るければ明るいほど、マテリアルは「飛び出し」、それらの領域はカメラがサーフェスを移動するにつれて視差 (シフト) します。高さマップの暗い領域は、「遠くに離れて」おり、最小限のシフトをします。

アイテム 説明
プロパティ
HeightRatio 高さマップ から渡された深度の乗数です。値が大きければ大きいほど、極端な深度になります。通常は 0.02 から 0.1 の範囲です。
ReferencePlane エフェクトを与えるために、テクスチャ領域の高さの概算を指定します。値を 0 にするとテクスチャが完全に表面から離れて歪んだ表示となり、 0.5 (デフォルト値) にすると表面に部分的な凹凸が発生します。
入力値
Coordinate (座標) 表現式で変更するベースのテクスチャ座標を受け取ります。
Height (高さ) 高さマップとして使用するテクスチャ (または値) を受け取ります。
HeightRatioInput 高さマップ から渡された深度の乗数です。値が大きければ大きいほど、深度が激しくなります。通常は 0.02 から 0.1 の範囲です。この入力を使用すると、Height Ratio プロパティのすべての値に置き換わります。

引用元  <https://docs.unrealengine.com/latest/JPN/Engine/Rendering/Materials/ExpressionReference/Utility/index.html>

 

BumpOffset1

ハイトマップを入力すると通常の視差マッピングですが、[constant]を入力することでレイヤーのように視差マッピングすることができます。

UE4のラーニングにあるShowdown_VR_Demoのアセットでわかり易いマテリアルがありました。(M_Shop2Glass_01)

BumpOffset_0012_レイヤー-3

・奥の壁

・中間の机

・手前のガラス
というように3段階の視差がついています。
1つのマテリアルでここまで表現できるのはすごいですね!初めて見たときは目から鱗でした!!

ノードはこのようになっています。

BumpOffset_0011_レイヤー-4

丁寧にコメントがついているので非常にわかり易いですね!

ですが念のために視差を作っているノードをそれぞれ見ていきましょう。

 

手前に来るレイヤー

BumpOffset_0010_レイヤー-5

一番手前なので、視差をつける必要はありません。

 

 

中間のレイヤー

BumpOffset_0009_レイヤー-6

BumpOffsetに[Constant]を入力し、0.3と入力します。

このとき重要になってくるのが、BumpOffsetのプロパティです。

BumpOffset_0008_レイヤー-7

デフォルト値は
Height Ratio 0.05
Reference Plane0.5

このままだと、のっぺりしたままなので、プロパティの2つの値を調整して、見た目を作っていきます。

 

奥のレイヤー

BumpOffset_0007_レイヤー-8

BumpOffsetに[Constant]を入力し、0.5と入力します。

プロパティの設定は以下の通りです。

BumpOffset_0006_レイヤー-9

 

 

以上でレイヤーのような視差を作ることができます!

背景だけでなく、キャラクターの瞳の表現にも使えるかも!

 

もっと詳しく知りたい方はUE公式ドキュメント

 

 

[UE4] ノードの入力ピンによって出力ピン等の内容を動的に変える仕組みについて ~解説編~

$
0
0

こちらは UE4 Advent Calendar 2015 4日目の記事です。
プログラマ向けの内容となります。
実装例編はコチラ


ゲーム等のインタラクティブなコンテンツを作っている時によく使うノードに「SpawnActorFromClass」や「ConstructObjectFromClass」があります。
これらのノードは他と少し違う機能を備えているのですが、お気付きでしょうか?
その機能とは、「入力ピンで指定したクラスによって、他の入力ピンや出力ピン内容が動的に変更される」というものです。

 

例えば SpawnActorFromClass を配置して、入力ピンに Character クラスを指定するとします。
SpawnActorFromClass で入力ピンのクラスを指定する前は以下の様な状態ですが、

DynamicPinNode_00

Character クラスを指定すると、出力ピンの型が「Actor Reference」から「Character Reference」に変わります。
また、新たな入力ピンとして「Instigator」が出現していたり、地味にノードタイトルが変わっていたりします。

DynamicPinNode_01

今回はこれをプロジェクト用に拡張してみます。

 

SpawnActorFromClass を例に取った構成の解説

BlueprintFunctionLibrary のような一般的なノードを作成するのであれば、クラスに static 関数として定義して UFUNCTION マクロを用いて関数指定子を適切に設定することで作成できます。
しかし今回のような特殊なノードを作成したい場合は、UK2Node クラスを継承して実装してあげる必要があります。

 

SpawnActorFromClass ノードを例に取ります。
このノードは UK2Node クラスを継承した UK2Node_SpawnActor クラスで実装されています。
カスタムノードを作成するために override できる関数をいくつか紹介します。
※ SpawnActorFromClass 内のコードは載せませんが、後で具体的な拡張事例を載せてますのでそちらをご覧下さい。

関数名 用途 & SpawnActorFromClass での拡張例
AllocateDefaultPins 入力/出力ピン情報を作成します。SpawnActor では In/Out 実行ピンの他、以下のピンが入力ピンとして定義されています。
  • BlueprintPin … スポーンするアクターのクラス
  • WorldContextPin … スポーンするアクターのクラスが必要としていれば定義
  • TransformPin … スポーンする際のトランスフォーム情報
  • bNoCollisionFailPin … スポーン先の位置にブロックコリジョンがあった場合にどうするか

更に出力ピンとして ResultPin が定義されています。

ReallocatePinsDuringReconstruction 何かしらのタイミングで入力/出力ピン情報に変更が出た場合の処理です。SpawnActor では AllocateDefaultPins して、スポーン予定のアクターのプロパティを参照して bIsExposedToSpawn(スポーンする際にプロパティを入力できるようにするフラグ)があれば、それらを入力ピンとして追加するようになっています。
ExpandNode ノード動作を確定させます。UK2Node はエディタモジュールなので、ランタイムには含まれません。つまり実際にノード実行時に何をするのか、という事をクラス内に定義することができないため、処理内容をここで確定させて KismetCompiler に情報として与えるという事が必要になります。具体的には、KismetCompilerContext から Intermediate ノードをスポーンし、呼ぶ関数リファレンスを関連付け、入力ピンのリンクを Intermediate ノードに紐付けるという事を KismetCompilerContext に通知する、といった事を必要な数だけ繰り返します。

SpawnActor では、

  1. UGameplayStatics::BeginSpawningActorFromBlueprint
  2. ExposedToSpawn が有効なプロパティを順に設定
  3. UGameplayStatics::FinishSpawningActor

といった順番で関数がリンクされています。

コンパイル時にリンクして Intermediate として出力されるため、実行時には関連付けた関数リファレンスが直接呼ばれる形になります。

GetNodeTitle ノードのタイトルを指定します。タイトルを入力クラスに合わせて動的に変更させることも可能です。
GetNodeTitleColor ノードのタイトル色を指定します。
GetClassPinBaseClass ノードの入力クラスピンに指定できるベースクラスを指定できます。これを使用することで、例えば Character クラスを継承したものしか候補に現れないといった事も可能になります。
GetMenuCategory ノードのカテゴリを指定できます。指定がなかった場合は Game カテゴリとなります。

 

兎にも角にも一番のキモは ExpandNode です。
ここで何をやっているのかが理解できれば、大抵のカスタムノードの挙動は理解できると思います。

これら以外にも拡張のための関数がいくつか定義されていますので、もっと深く知りたい方は K2Node_SpawnActor や K2Node_ConstructObjectFromClass、K2Node_CreateWidget あたりを読んでみると理解が進むかと思います。

 

ちなみに単純に SpawnActor や ConstructObjectFromClass の候補を絞りたいといった時は、継承して GetClassPinBaseClass をオーバーライドするだけでいいので簡単です。

 

実装例

上記の実装例に関しては別ページにまとめてありますので、コチラからどうぞ。

 

注意事項

今回紹介したノードの入力/出力ピンを動的に変更する仕組みは、あくまでエディタ上で編集する際に決定するものでしか内部で判断できません。

つまり入力ピンとして Enum を用意し、その値を元に出力ピンの型を変えたいといった場合は、Enum の値が実行時に変化するために出力の型を編集時に決定させることができません。

(ただ、UK2Node_Switch みたいに動的に出力実行ピンの実行先を変更することはできるので、通信内容をデシリアライズして内容に合わせて処理分岐、みたいなことは可能かと思います)

 

終わりに

C++を記述する必要はありますが、ノード拡張周りができるようになると、Blueprint からできることの幅が一気に広がります。

興味がある方は色々なノードの内部実装周りを見てみると良いかと思います。


明日は @kurosaurus さんの記事です。

SpriteStudio 周りを書いてもらえるとのことで、とても期待ですね。

[UE4] ノードの入力ピンによって出力ピン等の内容を動的に変える仕組みについて ~実装例編~

$
0
0

こちらは UE4 Advent Calendar 2015 4日目の記事です。
プログラマ向けの内容となります。
解説編をまだ読んでいない方はコチラ


実装サンプル

今回は実装サンプルとして、以下のようなカスタムノードを作ってみました。

 

DynamicPinNode_04

こんなノードがあって、

 

DynamicPinNode_05

SampleDataA を選択すると、SampleDataSettingA を引数に取る設定コピーノードになります。
出力の型は SampleDataA のリファレンスです。

 

DynamicPinNode_06

同じような SampleDataB もあります。

 

DynamicPinNode_07

もちろん SampleDataA に対応するものに対して SampleDataB のものは入力できません。

 

これを実装すると、以下のようになります。
詳しい解説は大きくなりすぎるので割愛しますが、気になる方は読んでみてください。

 

まず、SampleData 系の定義です。

#pragma once

#include "Object.h"
#include "SampleData.generated.h"

////////////////////////////////////////////////////////////////////////////////
// サンプルデータ基底

UCLASS()
class DYNAMICPINNODE_API USampleData : public UObject
{
	GENERATED_BODY()

public:
	// 設定クラス取得
	virtual UClass* GetSettingClass() const { return nullptr; }
};


////////////////////////////////////////////////////////////////////////////////
// サンプルデータA

UCLASS()
class DYNAMICPINNODE_API USampleDataSettingA : public UObject
{
	GENERATED_BODY()

public:
	int32 PropertyA;
};

UCLASS()
class DYNAMICPINNODE_API USampleDataA : public USampleData
{
	GENERATED_BODY()

	virtual void PostInitProperties() override
	{
		Super::PostInitProperties();

		if (!HasAnyFlags(RF_ClassDefaultObject))
		{
			Setting = NewObject<USampleDataSettingA>(GetTransientPackage(), USampleDataSettingA::StaticClass());
		}
	}

public:
	// 設定コピー関数
	void CopySettingA(const USampleDataSettingA& InSetting)
	{
		UE_LOG(LogTemp, Log, TEXT("******* Copy SettingA"));
		Setting->PropertyA = InSetting.PropertyA;
	}

	// 静的設定コピー関数(IntermediateNodeによる呼び出し用)
	UFUNCTION(BlueprintCallable, Category="Sample", meta=(BlueprintInternalUseOnly = "true"))
	static USampleDataA* CopySettingA_Static(USampleDataA* Target, const USampleDataSettingA* Setting)
	{
		if (Target == nullptr || Setting == nullptr)
		{
			return Target;
		}

		Target->CopySettingA(*Setting);

		return Target;
	}

	// 設定クラス取得
	virtual UClass* GetSettingClass() const override
	{
		return USampleDataSettingA::StaticClass();
	}

private:
	UPROPERTY()
	USampleDataSettingA* Setting;
};


////////////////////////////////////////////////////////////////////////////////
// サンプルデータB

UCLASS()
class DYNAMICPINNODE_API USampleDataSettingB : public UObject
{
	GENERATED_BODY()

public:
	float PropertyB;
};

UCLASS()
class DYNAMICPINNODE_API USampleDataB : public USampleData
{
	GENERATED_BODY()

	virtual void PostInitProperties() override
	{
		Super::PostInitProperties();

		if (!HasAnyFlags(RF_ClassDefaultObject))
		{
			Setting = NewObject<USampleDataSettingB>(GetTransientPackage(), USampleDataSettingB::StaticClass());
		}
	}

public:
	// 設定コピー関数
	void CopySettingB(const USampleDataSettingB& InSetting)
	{
		UE_LOG(LogTemp, Log, TEXT("******* Copy SettingB"));
		Setting->PropertyB = InSetting.PropertyB;
	}

	// 静的設定コピー関数(IntermediateNodeによる呼び出し用)
	UFUNCTION(BlueprintCallable, Category="Sample", meta=(BlueprintInternalUseOnly = "true"))
	static USampleDataB* CopySettingB_Static(USampleDataB* Target, const USampleDataSettingB* Setting)
	{
		if (Target == nullptr || Setting == nullptr)
		{
			return Target;
		}

		Target->CopySettingB(*Setting);

		return Target;
	}

	// 設定クラス取得
	virtual UClass* GetSettingClass() const override
	{
		return USampleDataSettingB::StaticClass();
	}

private:
	UPROPERTY()
	USampleDataSettingB* Setting;
};

 

そして上で例に挙げたコピーノードを作成するノードクラスです。

#pragma once

#include "K2Node.h"
#include "EdGraph/EdGraphNodeUtils.h" // for FNodeTextCache
#include "K2Node_CopySampleDataSetting.generated.h"

UCLASS()
class DYNAMICPINNODEED_API UK2Node_CopySampleDataSetting : public UK2Node
{
	GENERATED_UCLASS_BODY()

	// Begin UEdGraphNode interface.
	virtual void AllocateDefaultPins() override;
	virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
	virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override;
	virtual FText GetTooltipText() const override;
	virtual bool HasExternalDependencies(TArray<class UStruct*>* OptionalOutput) const override;
	virtual bool IsCompatibleWithGraph(const UEdGraph* TargetGraph) const override;
	virtual void PinConnectionListChanged(UEdGraphPin* Pin);
	// End UEdGraphNode interface.

	// Begin UK2Node interface
	virtual bool IsNodeSafeToIgnore() const override { return true; }
	virtual void ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins) override;
	virtual void GetNodeAttributes( TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes ) const override;
	virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
	virtual FText GetMenuCategory() const override;
	virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
	// End UK2Node interface

private:
	void CreatePinsForClass(UClass* InClass, TArray<UEdGraphPin*>* OutClassPins = nullptr);

	UEdGraphPin* GetClassPin(const TArray<UEdGraphPin*>* InPinsToSearch = NULL) const;
	UEdGraphPin* GetTargetPin() const;
	UEdGraphPin* GetSettingPin() const;
	UEdGraphPin* GetThenPin() const;
	UEdGraphPin* GetResultPin() const;

	UClass* GetSampleDataClass(const TArray<UEdGraphPin*>* InPinsToSearch = NULL) const;
	UClass* GetClassPinBaseClass() const;

	bool IsSpawnVarPin(UEdGraphPin* Pin);
	void SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const;
	void OnClassPinChanged();

private:
	FNodeTextCache CachedNodeTitle;
};

#include "DynamicPinNodeEd.h"
#include "GraphEditorSettings.h"
#include "BlueprintGraphDefinitions.h"
#include "BlueprintUtilities.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "UnrealEd.h"
#include "EditorStyle.h"
#include "KismetCompiler.h"
#include "BlueprintNodeSpawner.h"
#include "EditorCategoryUtils.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "SampleData.h"
#include "K2Node_CopySampleDataSetting.h"

#define LOCTEXT_NAMESPACE "SampleData"

struct FK2Node_CopySampleDataSettingHelper
{
	static FString ClassPinName;
	static FString TargetPinName;
	static FString SettingPinName;
};

FString FK2Node_CopySampleDataSettingHelper::ClassPinName(TEXT("Class"));
FString FK2Node_CopySampleDataSettingHelper::TargetPinName(TEXT("Target"));
FString FK2Node_CopySampleDataSettingHelper::SettingPinName(TEXT("Setting"));

UK2Node_CopySampleDataSetting::UK2Node_CopySampleDataSetting(const FObjectInitializer& ObjectInitializer)
	: Super(ObjectInitializer)
{
}

void UK2Node_CopySampleDataSetting::AllocateDefaultPins()
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	// 入力クラスによって変更されないピン情報はここに記述

	// 入力実行ピン
	CreatePin(EGPD_Input, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Execute);

	// 出力実行ピン
	CreatePin(EGPD_Output, K2Schema->PC_Exec, TEXT(""), NULL, false, false, K2Schema->PN_Then);

	// コピー対象クラス
	UEdGraphPin* ClassPin = CreatePin(EGPD_Input, K2Schema->PC_Class, TEXT(""), GetClassPinBaseClass(), false, false, FK2Node_CopySampleDataSettingHelper::ClassPinName);
	SetPinToolTip(*ClassPin, LOCTEXT("ClassPinDescription", "コピー対象クラス"));

	Super::AllocateDefaultPins();
}

FText UK2Node_CopySampleDataSetting::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
	if (TitleType == ENodeTitleType::ListView || TitleType == ENodeTitleType::MenuTitle)
	{
		return LOCTEXT("GetNodeTitleMenu", "Copy Sample Data Setting");
	}
	else if (auto SampleDataClass = GetSampleDataClass())
	{
		if (CachedNodeTitle.IsOutOfDate(this))
		{
			FFormatNamedArguments Args;
			Args.Add(TEXT("ClassName"), SampleDataClass->GetDisplayNameText());
			CachedNodeTitle.SetCachedText(FText::Format(LOCTEXT("GetNodeTitleFormat", "{ClassName} 設定をコピーします"), Args), this);
		}
		return CachedNodeTitle;
	}
	return LOCTEXT("GetNodeTitleDefault", "サンプルデータ設定をコピーします");
}

void UK2Node_CopySampleDataSetting::PinDefaultValueChanged(UEdGraphPin* ChangedPin)
{
	if (ChangedPin && (ChangedPin->PinName == FK2Node_CopySampleDataSettingHelper::ClassPinName))
	{
		OnClassPinChanged();
	}
}

FText UK2Node_CopySampleDataSetting::GetTooltipText() const
{
	return LOCTEXT("NodeTooltip", "サンプルデータ設定をコピーします");
}

bool UK2Node_CopySampleDataSetting::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const
{
	UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph);
	return Super::IsCompatibleWithGraph(TargetGraph) && (!Blueprint || FBlueprintEditorUtils::FindUserConstructionScript(Blueprint) != TargetGraph);
}

bool UK2Node_CopySampleDataSetting::HasExternalDependencies(TArray<class UStruct*>* OptionalOutput) const
{
	UClass* SourceClass = GetSampleDataClass();
	const UBlueprint* SourceBlueprint = GetBlueprint();
	const bool bResult = (SourceClass != NULL) && (SourceClass->ClassGeneratedBy != SourceBlueprint);
	if (bResult && OptionalOutput)
	{
		OptionalOutput->AddUnique(SourceClass);
	}
	const bool bSuperResult = Super::HasExternalDependencies(OptionalOutput);
	return bSuperResult || bResult;
}

void UK2Node_CopySampleDataSetting::PinConnectionListChanged(UEdGraphPin* Pin)
{
	if (Pin && (Pin->PinName == FK2Node_CopySampleDataSettingHelper::ClassPinName))
	{
		OnClassPinChanged();
	}
}

void UK2Node_CopySampleDataSetting::ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins)
{
	AllocateDefaultPins();
	UClass* SampleDataClass = GetSampleDataClass(&OldPins);
	if( SampleDataClass != NULL )
	{
		CreatePinsForClass(SampleDataClass);
	}
}

void UK2Node_CopySampleDataSetting::GetNodeAttributes( TArray<TKeyValuePair<FString, FString>>& OutNodeAttributes ) const
{
	UClass* SampleDataClass = GetSampleDataClass();
	const FString SampleDataClassStr = SampleDataClass ? SampleDataClass->GetName() : TEXT( "InvalidClass" );
	OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Type" ), TEXT( "CopySampleDataSetting" ) ));
	OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Class" ), GetClass()->GetName() ));
	OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "Name" ), GetName() ));
	OutNodeAttributes.Add( TKeyValuePair<FString, FString>( TEXT( "ObjectClass" ), SampleDataClassStr ));
}

void UK2Node_CopySampleDataSetting::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
	UClass* ActionKey = GetClass();
	if (ActionRegistrar.IsOpenForRegistration(ActionKey))
	{
		UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
		check(NodeSpawner != nullptr);

		ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
	}
}

FText UK2Node_CopySampleDataSetting::GetMenuCategory() const
{
	return LOCTEXT("MenuCategory", "Sample");
}

void UK2Node_CopySampleDataSetting::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* TargetGraph)
{
	Super::ExpandNode(CompilerContext, TargetGraph);

	static FName CopySettingA_FunctionName = GET_FUNCTION_NAME_CHECKED(USampleDataA, CopySettingA_Static);
	static FName CopySettingB_FunctionName = GET_FUNCTION_NAME_CHECKED(USampleDataB, CopySettingB_Static);
	static FString Class_ParamName = FString(TEXT("Class"));
	static FString Target_ParamName = FString(TEXT("Target"));
	static FString Setting_ParamName = FString(TEXT("Setting"));

	UK2Node_CopySampleDataSetting* CopySampleDataSettingNode = this;
	UEdGraphPin* NodeExec = CopySampleDataSettingNode->GetExecPin();
	UEdGraphPin* ClassPin = CopySampleDataSettingNode->GetClassPin();
	UEdGraphPin* TargetPin = CopySampleDataSettingNode->GetTargetPin();
	UEdGraphPin* SettingPin = CopySampleDataSettingNode->GetSettingPin();
	UEdGraphPin* NodeThen = CopySampleDataSettingNode->GetThenPin();
	UEdGraphPin* NodeResult = CopySampleDataSettingNode->GetResultPin();

	UClass* SampleDataClass = ( ClassPin != NULL ) ? Cast<UClass>(ClassPin->DefaultObject) : NULL;
	if ( ( 0 == ClassPin->LinkedTo.Num() ) && ( NULL == SampleDataClass ) )
	{
		CompilerContext.MessageLog.Error(*LOCTEXT("CopySampleDataSettingNodeMissingClass_Error", "クラスが設定されていません").ToString(), CopySampleDataSettingNode);
		CopySampleDataSettingNode->BreakAllNodeLinks();
		return;
	}
	if ( SampleDataClass != USampleDataA::StaticClass() && SampleDataClass != USampleDataB::StaticClass() )
	{
		CompilerContext.MessageLog.Error(*LOCTEXT("CopySampleDataSettingNodeMissingClass_Error", "無効なクラスです").ToString(), CopySampleDataSettingNode);
		CopySampleDataSettingNode->BreakAllNodeLinks();
		return;
	}


	//////////////////////////////////////////////////////////////////////////
	// 'USampleData::CopySettingStatic' を呼び出すノードを作成

	UK2Node_CallFunction* CallCopyNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(CopySampleDataSettingNode, TargetGraph);

	if (SampleDataClass == USampleDataA::StaticClass())
	{
		CallCopyNode->FunctionReference.SetExternalMember(CopySettingA_FunctionName, SampleDataClass);
	}
	else if (SampleDataClass == USampleDataB::StaticClass())
	{
		CallCopyNode->FunctionReference.SetExternalMember(CopySettingB_FunctionName, SampleDataClass);
	}

	CallCopyNode->AllocateDefaultPins();

	UEdGraphPin* CallCopyExec = CallCopyNode->GetExecPin();
	UEdGraphPin* CallCopyTargetPin = CallCopyNode->FindPinChecked(Target_ParamName);
	UEdGraphPin* CallCopySettingPin = CallCopyNode->FindPinChecked(Setting_ParamName);
	UEdGraphPin* CallCopyResult = CallCopyNode->GetReturnValuePin();

	CompilerContext.MovePinLinksToIntermediate(*NodeExec, *CallCopyExec);

	if ( TargetPin )
	{
		CompilerContext.MovePinLinksToIntermediate(*TargetPin, *CallCopyTargetPin);
	}

	if ( SettingPin )
	{
		CompilerContext.MovePinLinksToIntermediate(*SettingPin, *CallCopySettingPin);
	}

	if ( NodeResult )
	{
		CallCopyResult->PinType = NodeResult->PinType;
		CompilerContext.MovePinLinksToIntermediate(*NodeResult, *CallCopyResult);
	}

	//////////////////////////////////////////////////////////////////////////
	// 出力実行ピンに、'USampleData::CopySettingStatic' 実行結果ピンを紐付ける

	UEdGraphPin* LastThen = FKismetCompilerUtilities::GenerateAssignmentNodes(CompilerContext, TargetGraph, CallCopyNode, CopySampleDataSettingNode, CallCopyResult, GetSampleDataClass());

	CompilerContext.MovePinLinksToIntermediate(*NodeThen, *LastThen);

	// 処理が終了したのでリンクを外す
	CopySampleDataSettingNode->BreakAllNodeLinks();
}

void UK2Node_CopySampleDataSetting::CreatePinsForClass(UClass* InClass, TArray<UEdGraphPin*>* OutClassPins)
{
	check(InClass != NULL);

	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
	const USampleData* ClassDefaultObject = CastChecked<USampleData>(InClass->GetDefaultObject(false));

	// 入力クラスによって変更するピン情報はここに記述

	// コピー先インスタンス
	UEdGraphPin* TargetPin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), InClass, false, false, FK2Node_CopySampleDataSettingHelper::TargetPinName);
	SetPinToolTip(*TargetPin, LOCTEXT("TargetPinDescription", "コピー先インスタンス"));

	// 出力ピン
	UEdGraphPin* ResultPin = CreatePin(EGPD_Output, K2Schema->PC_Object, TEXT(""), InClass, false, false, K2Schema->PN_ReturnValue);
	SetPinToolTip(*ResultPin, LOCTEXT("ResultPinDescription", "コピー後のインスタンス"));

	if (OutClassPins)
	{
		OutClassPins->Add(TargetPin);
		OutClassPins->Add(ResultPin);
	}

	// コピーする設定
	UClass* SettingClass = ClassDefaultObject->GetSettingClass();
	if (SettingClass)
	{
		UEdGraphPin* SettingPin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), ClassDefaultObject->GetSettingClass(), false, false, FK2Node_CopySampleDataSettingHelper::SettingPinName);
		SetPinToolTip(*SettingPin, LOCTEXT("SettingPinDescription", "コピーする設定"));

		if (OutClassPins)
		{
			OutClassPins->Add(SettingPin);
		}
	}
}

UEdGraphPin* UK2Node_CopySampleDataSetting::GetClassPin(const TArray<UEdGraphPin*>* InPinsToSearch /*= NULL*/) const
{
	const TArray<UEdGraphPin*>* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins;

	UEdGraphPin* Pin = NULL;
	for( auto PinIt = PinsToSearch->CreateConstIterator(); PinIt; ++PinIt )
	{
		UEdGraphPin* TestPin = *PinIt;
		if( TestPin && TestPin->PinName == FK2Node_CopySampleDataSettingHelper::ClassPinName )
		{
			Pin = TestPin;
			break;
		}
	}
	check(Pin == NULL || Pin->Direction == EGPD_Input);
	return Pin;
}

UEdGraphPin* UK2Node_CopySampleDataSetting::GetTargetPin() const
{
	UEdGraphPin* Pin = FindPin(FK2Node_CopySampleDataSettingHelper::TargetPinName);
	check(Pin == NULL || Pin->Direction == EGPD_Input);
	return Pin;
}

UEdGraphPin* UK2Node_CopySampleDataSetting::GetSettingPin() const
{
	UEdGraphPin* Pin = FindPin(FK2Node_CopySampleDataSettingHelper::SettingPinName);
	check(Pin == NULL || Pin->Direction == EGPD_Input);
	return Pin;
}

UEdGraphPin* UK2Node_CopySampleDataSetting::GetThenPin()const
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	UEdGraphPin* Pin = FindPinChecked(K2Schema->PN_Then);
	check(Pin->Direction == EGPD_Output);
	return Pin;
}

UEdGraphPin* UK2Node_CopySampleDataSetting::GetResultPin() const
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	UEdGraphPin* Pin = FindPin(K2Schema->PN_ReturnValue);
	check(Pin == NULL || Pin->Direction == EGPD_Output);
	return Pin;
}

UClass* UK2Node_CopySampleDataSetting::GetSampleDataClass(const TArray<UEdGraphPin*>* InPinsToSearch /*=NULL*/) const
{
	UClass* SampleDataClass = NULL;
	const TArray<UEdGraphPin*>* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins;

	UEdGraphPin* ClassPin = GetClassPin(PinsToSearch);
	if(ClassPin && ClassPin->DefaultObject != NULL && ClassPin->LinkedTo.Num() == 0)
	{
		SampleDataClass = CastChecked<UClass>(ClassPin->DefaultObject);
	}
	else if (ClassPin && ClassPin->LinkedTo.Num())
	{
		auto ClassTarget = ClassPin->LinkedTo[0];
		SampleDataClass = ClassTarget ? Cast<UClass>(ClassTarget->PinType.PinSubCategoryObject.Get()) : nullptr;
	}

	return SampleDataClass;
}

UClass* UK2Node_CopySampleDataSetting::GetClassPinBaseClass() const
{
	return USampleData::StaticClass();
}

bool UK2Node_CopySampleDataSetting::IsSpawnVarPin(UEdGraphPin* Pin)
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	return(	Pin->PinName != K2Schema->PN_Execute &&
			Pin->PinName != K2Schema->PN_Then &&
			Pin->PinName != FK2Node_CopySampleDataSettingHelper::ClassPinName);
}

void UK2Node_CopySampleDataSetting::SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const
{
	MutatablePin.PinToolTip = UEdGraphSchema_K2::TypeToText(MutatablePin.PinType).ToString();

	UEdGraphSchema_K2 const* const K2Schema = Cast<const UEdGraphSchema_K2>(GetSchema());
	if (K2Schema != nullptr)
	{
		MutatablePin.PinToolTip += TEXT(" ");
		MutatablePin.PinToolTip += K2Schema->GetPinDisplayName(&MutatablePin).ToString();
	}

	MutatablePin.PinToolTip += FString(TEXT("\n")) + PinDescription.ToString();
}

void UK2Node_CopySampleDataSetting::OnClassPinChanged()
{
	const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();

	TArray<UEdGraphPin*> OldPins = Pins;
	TArray<UEdGraphPin*> OldClassPins;

	for (int32 i = 0; i < OldPins.Num(); i++)
	{
		UEdGraphPin* OldPin = OldPins[i];
		if (IsSpawnVarPin(OldPin))
		{
			Pins.Remove(OldPin);
			OldClassPins.Add(OldPin);
		}
	}

	CachedNodeTitle.MarkDirty();

	UClass* SampleDataClass = GetSampleDataClass();
	TArray<UEdGraphPin*> NewClassPins;
	if (SampleDataClass != NULL)
	{
		CreatePinsForClass(SampleDataClass, &NewClassPins);
	}

	RewireOldPinsToNewPins(OldClassPins, NewClassPins);

	DestroyPinList(OldClassPins);

	UEdGraph* Graph = GetGraph();
	Graph->NotifyGraphChanged();

	FBlueprintEditorUtils::MarkBlueprintAsModified(GetBlueprint());
}

#undef LOCTEXT_NAMESPACE

 

こちらはエディタ時のみに使用するクラスになるので、プロジェクトのエディタモジュール側に定義します。
ちなみにエディタモジュールの実装に関してはコチラの記事を参考にして下さい。

 

また、カスタムノードを実装時には以下のように参照するモジュールを追加してあげる必要もありますのでご注意ください。

using UnrealBuildTool;

public class DynamicPinNodeEd : ModuleRules
{
	public DynamicPinNodeEd(TargetInfo Target)
	{
		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EditorStyle" });

		PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });

        PrivateDependencyModuleNames.AddRange(
            new string[]
            {
                "EditorStyle",
                "KismetCompiler",
                "UnrealEd",
                "GraphEditor",
                "Kismet",
                "BlueprintGraph",
                "DynamicPinNode",
            }
        );
    }
}

 

自作プラグインでの拡張事例

自分が個人で作成・公開している WebApi プラグイン というものがあります。
プラグインの内容は割愛しますが、こちらの実装でも今回の仕組みが活用されています。

 

WebApi クラスをインスタンス化する機能を提供する「Create WebApi」というノードがあります。
こちらは ConstructObjectFromClass とほぼ同機能のノードとなりますが、それに加えて以下の拡張が入っています。

  • 入力ピンのクラス指定候補に WebApi クラス or それを継承したものしか出てこない
  • 出力ピンのデフォルト型が WebApi クラスのリファレンスとなっている
    (動的に入力ピンのクラス指定が変わるような実装でも、WebApi クラスのAPIはキャストせずに使用できる)

DynamicPinNode_02

こちらのノードの実装例は以下を参照して下さい。

K2Node_CreateWebApi.h
K2Node_CreateWebApi.cpp

 

また、WebApiRequestBodyBase クラスをインスタンス化し、更にプロパティコピー元のインスタンスを指定が可能な「Create WebApiRequestBody」というノードも実装されています。

こういった感じに入力ピンは自由に増やすことが可能です。

DynamicPinNode_03

こちらのノードの実装例は以下を参照して下さい。

K2Node_CreateWebApiRequestBody.h
K2Node_CreateWebApiRequestBody.cpp


 

解説編はコチラ

 

[UE4] 枠線の太さはそのままで拡大縮小できるUIの作り方

$
0
0

ユーザーに何かを伝えたい時、メッセージをダイアログ表示することがあるかと思います。
また、そのようなダイアログはメッセージ量や使用する場面で大きさを変えたりすることも少なくありません。
今回はそのようなケースで使える機能を一つ紹介したいと思います。

 

拡大縮小できるダイアログを作る

まず、以下のようなダイアログ背景用のテクスチャを用意します。

scale9_01

 

これをUMGで普通に表示すると以下の様になります。

scale9_02

 

しかしここで「X軸方向に2倍した大きさで表示したい」という要望がありました。
ひとまず単純に ImageSize の X を2倍にしてみます。

scale9_03

 

幅は2倍になりましたが、枠線が太くなってしまい、見栄えが悪いですね。。
しかし、こういったケースでも UMGのスタイリング機能 を使えば綺麗なダイアログを表示することができます。

 

UMGで 512×512 のテクスチャを配置し、X軸方向に2倍するために ImageSize を 1024×512 に設定すると、プロパティは以下の様になっているかと思います。

scale9_06

 

ここで以下の様にプロパティを変更してみましょう。

Draw As Box
Margin 0.18 ※画像の形状によって変わります

scale9_07

 

これでダイアログを表示すると、以下の様になります。

scale9_04

 

枠線は拡大縮小されず、綺麗なダイアログが表示されましたね!

 

画像スタイルをBoxに設定することで、表示する画像が3×3の領域に分割されます。
それの真ん中以外の領域(枠の領域)の大きさをMarginで設定することで、拡大縮小時に必要な箇所のみをスケールするようになります。
以下、公式ドキュメントからの引用画像です。

scale9_08

 

まとめ

今回紹介した機能は少し地味ですが、とても有用な機能です。
ちなみにこの機能を使えば、以下の様なものが作れます。

scale9_05

 

是非、有効活用して下さい。

[UE4]Collectionを活用して作業効率アップ!

$
0
0

製作が進んでくると扱うファイル数が増えて、お目当てのアセットが見つけにくくなりますよね。

コンテンツブラウザのショートカット機能”Collection”を活用すれば、そんな悩みも解決です!

 

Collectionを作成する

Image01

1.コンテンツブラウザの右下の”View Options”を押下

2.”Show Collections”を選択して下さい

↓コンテントブラウザの左側の一覧の下に、新たな領域が現れます。

Image02

3.”+”ボタンを押下

4.”Local Collection”を選択。適当な名前を入力

↓このように、”Local Collection”が追加されました。

Image03

アセットをCollectionに登録する

Image04

 

5.コンテントブラウザ上で、Collectionに追加したいアセットを右クリック

6.”Manage Cllections”から、先ほど作成したCollection名を選択

↓アセットがCollectionに追加されました!

Image05

Collectionに登録されているアセットはショートカットのようなものです。(複製される訳ではありません)

よく使うアセットをCollectionに登録しておけば、アセットを探す手間が省けて作業効率をアップできるかも!?

 

また、プロジェクトをソースコントロールに繋ぎ込んでいれば、”Shared Collection”を使って他の作業者とCollectionをシェアする事も可能です。

是非ご活用ください!

[UE4] UE4コミュニティふりかえり

$
0
0

※この記事はUnreal Engine 4 (UE4) 其の弐 Advent Calendar 2015の19日目の記事です。

こんにちは、先月「設立2周年を迎えました」という記事を書いて投稿しそこなっていた代表の佐々木です。やっちまいました。いまさら感漂いますが、後日公開したいと思います。

今回は年末ということもあり、いい機会なのでコミュニティを中心にこの2年間をふりかえってみたいと思います。読み物的に「そんなこともあったな」と楽しんでいただければと。こんな機会じゃないと、こういうことは書かなそうですしね(笑)

 

2014年3月20日 UE4サブスクリプション開始

UE4のコミュニティは事実上ここから始まりました。月額2000円ほどでソースコード含めてすべて公開。発表時はPCに張り付いてこのブログを更新していた覚えがあります。

 

2014年3月23日 Facebook Unreal Engineユーザー助け合い所開設

Unityユーザー助け合い所が非常にいいコミュニティだなと思っていたので、Unreal Engine版を作りました。公式でAnswerHubというフォーラムもありますが、使いやすい方を使えばいいんじゃないかなと思ったのと、お知らせ的な情報はこちらのほうが流しやすいのかなと。いまでは登録が1600人を超えていて、すごいですね。最近自分は有効的に使えてないので、もうちょっと使っていこうかと思います。

 

2014年4月12日 Unreal Engine 4 ビギナー勉強会開催

募集ページを見ると、サブスクリプション発表後、2日後に募集開始しているようです。本当に勢いだけで募集かけてますね(笑) もともと自分がGamePMやゲームコミュニティーサミットといったコミュニティ活動が好きだったので、「30人くらいでいっちょやるか!」と勢いで立ち上げたら、300人集まって焦りました。Epic Games Japan代表の河崎さんにも駆けつけていただき、盛り上がりました。また、極み本の著者である湊さんもこのとき登壇されています。なつかしー。のちにヒストリアに加わったメンバーもこの場に何名か居たようです。

 

2014年5月17日 第1回 UE4ぷちコン開始

サンプルを眺めるだけだともったいない! みんなが何か作る動機になるようなコンテストをやろう!と思い立って、Epic河崎さんに話したところ、やりましょうと即決いただきました。フォルダを漁ってたら、当時の1枚企画書が出てきたので晒してみます。はずかしー。ブルーマン先生かっこいー。それっぽいこと書いてますが、「機能覚えるだけじゃなくてゲーム作ろうぜ!」って勢いだけでやった気がします。勢い大事。

UE4ぷちコン企画書

 

2014年8月~2014年12月 野良勉強会ラッシュ

これは自分がやったわけではありませんが、「関西 Unreal Engine4 ハンズオンセミナー」「背景アーティストUE4ビギナー勉強会」「沖縄UnrealEngine4講習会」「UE4 背景アーティスト勉強会in大阪」と、毎月のようにイベントがあった気がします。沖縄は社員旅行とくっつけて遊びに行った覚えが。今年は多忙で社員旅行いけなかったので、来年は春過ぎに行きたいなあ。

 

2014年10月13日 アンリアル・フェス2014

今まではアンリアル・サミットという名前でクローズドなイベントとして毎年開催されていたEpic Games Japan公式イベントが、オープン化してアンリアル・フェスとして開催されました。私もしょっぱな登壇させてもらいました。当時のスライドはこちら。ふわっとしたことしか言ってないですね(笑) 1人目だし、基本的なことと夢のある未来を語ろう!と考えてた覚えがあります。

動画もありました。

そういえば、いまコミュニティーマネージャーとしてバリバリ活躍されているEpic Games Japanの今井さんと一緒に初めてやったのは、このイベントだった気がします。

 

2015年3月3日 Unreal Engine 4のフリー版提供開始 & Kite Demo公開

今年のGDCにて、衝撃のフリー版の発表! 有料から無料になったという以外にも、今まではPayPalかクレジットカードがないと登録できなかったので、大幅にすそ野が広がったといえます。

また、無料化に対する想いがこもったKite Demoは、今まで技術的難易度が高かった分野(広大なフィールド、有機物、人肌表現)に対して「リアルタイムでここまでできるぞ!」と技術的な進化も見せてくれました。毎年GDCは興奮の連続です。

2015年3月8日 UE4攻略wiki公開

情報をまとめる場所として、オープンなwikiを作ってみました。最近はちょっと放置中ですが、だれでも編集できるのでぜひお気軽に。このポストを書きながら、イベント一覧ページはある程度更新しました。講演資料を漁るのに便利です。

 

2015年4月18日 アンリアル・フェス2015 大阪

横浜に続き大阪でも! ヒストリアも「UE4 × Project Morpheus ~“AKB0048”דアクエリオン”多次元スペシャルライブ開発事例~」というVRの事例を発表し、全員参加で行ってきました。2016年もあるそうなので、楽しみです。

 

2015年9月10日 Infinity Bladeのアセットが無料公開

300万ドル相当のアセットが無料公開! モバイル用のアセットということもあり、使い勝手のいいハイクオリティのアセットがコミュニティにプレゼントされました。太っ腹! これらを活かして何か作りたいですね~。

 

2015年9月17日 Infiltrator無料公開

GDC2013で初公開され、Elemental Demoと共に初期UE4の代名詞ともいえるデモ、Infiltratorのプロジェクトが一式公開されました。Kite Demoと共に家庭のPCでこのクオリティのリアルタイムデモが動かせるとは、時代の進化を感じます。

2015年9月~ 各地でUE4 Meetupが開催

もともとコミュニティが存在していた東京・大阪以外の各地で、UE4 Meetupの名で勉強会イベントが立ち上がりました。福岡、札幌、仙台がすでに開催されており、まだまだ企画されているようです。これまでは大体決まった人が主催してイベントを開いていましたが、フリー化を経てユーザー層が広がったのではないでしょうか。自分も何個か遊びに行ってますが、全国回れて楽しいです(笑)

 

2015年10月18日 アンリアル・フェス2015 横浜

今年は2トラック13セッションに大幅パワーアップして開催されました! ヒストリアも今回から実行委員に参加して、展示ブースでスポンサー展示とぷちコンを含むユーザー展示の仕切りを担当しました。大変だけど楽しかった! 来年もまた面白い企画やりたいです。

 

 

ざっと1年半のUE4コミュニティ事情をふりかえってみました。今年の夏あたりはもっといろいろあった気がするので、「これもあったよ!」と教えてもらえたら助かります。また、私も全部把握しているわけではないので、いろいろと教えてもらえたら嬉しいです。

一般公開後、着々と広がるUE4コミュニティ。2016年もどういった広がりを見せるのか楽しみです。個人的には制作事例が増えてきたので、それら技術記事が増え、イベントで事例発表が増えるといいなと。また、ノンゲームが盛り上がりに対して表に出てくる情報が少ないので、そのあたりが情報増えたらいいなーと思っています。ヒストリアとしても来年はイベント人員も強化していろんな面白いことをやっていこうと思っているので、こうご期待!(でもよく勘違いされますが、ヒストリアはイベント会社ではないよ! ゲーム制作会社ですよ!)

 

Unreal Engine 4 (UE4) 其の弐 Advent Calendar 2015、明日はhousakusleepingさんによるDCCツールのお話。お楽しみに!

[UE4] Pure関数とNonPure関数

$
0
0

今回は、少し上級者向けのブループリントのお話です。

 

Pure関数とNonPure関数

ブループリント関数には、Pureというオプションがあります。
今回は便宜上、コレがONの関数をPure関数、OFFの関数をNonPure関数と呼びたいと思います。

gettestvalue

 

このオプションを変更すると、関数の呼び出し方法が下図のように変化します。

pure_vs_nonpure

左がPure関数、右がNonPure関数です。実行ピンの有無が変化します。

今回は、これらの挙動の違いを解説してみたいと思います。

 

呼び出しタイミング

まず、Pure関数とNonPure関数では呼び出されるタイミングが異なります。

実行ピンを持つNonPure関数は、単純に処理順がそのノードに到達した際に実行されます。

それに対して実行ピンを持たないPure関数は、何らかのNonPure関数が実行された際に、そこから戻り値を参照されていた場合に実行されます。
ココで注意すべきなのは、参照が発生する際に毎回実行される、ということです。

 

例えば次のような場合、全く同じ値を参照するにも関わらず、GetTestValue関数は100回呼び出されることになります。

pure_call

Pure関数の組み合わせで複雑な計算を行う場合や、Pure関数内で複雑な計算を行っている場合などは、注意した方が良いでしょう。

 

戻り値の保存

もうひとつ重要な違いが、NonPure関数の戻り値は、次に同じノードが実行されるまで保存される、ということです。

Pure関数は、戻り値が参照される度に関数が実行され、値が再評価されます。
しかし、NonPure関数はExecピンから処理が流れてきた場合のみ実行され、その際に戻り値が保存され、後から参照されても最後に実行された際の戻り値が再利用されるのです。

そのため、処理的には連続していなくても、下記のようなことが可能になります。

blog_nonpure_ret

ただ、この書き方はC++等に慣れたプログラマにはとても気持ちの悪いものに感じるかと思いますが…

 

NonPure関数の戻り値を利用した最適化

この、戻り値が保存される、という動作を利用すると、場合によってはブループリントの処理を最適化することが出来ます。

用途的にはPure関数なのだけれど内部で複雑な計算を行っていて、その計算結果は何度も繰り返し利用される、というようなケースがあったとします。

この関数をあえてNonPure関数として実装し、繰り返し利用する前に1回だけ呼び出しておくことで、わざわざローカル変数等を用意せずに計算結果を再利用することが出来ます。

nonpure_for_opt

上図の例では、最初と同様の結果ながら、GetTestValue関数の呼び出しは1回だけで済みます。

これが有効に働くケースはそれほど多くはないかもしれませんが、時には、あえてNonPureでGet関数を実装する、という選択肢もあることを知って頂ければと思います。

 

本日発売! アミューズメントメディア総合学院のアンリアルチャレンジを監修しました

$
0
0

こんにちは、代表の佐々木です。

本日12月25日、私が3月から担当講師をしていたアミューズメントメディア総合学院の産学共同ゲーム開発プロジェクト、アンリアルチャレンジの3作品が販売されました!

リリース情報は公式サイトを見ていただくとして、ここでは担当講師をしての感想を書きたいと思います。

告知サイト:http://www.amgakuin.co.jp/amg-games/info-tgs.html

販売サイト 作品名
DMM.com 3本セット
DMM.com SUBLIGHT
DMM.com TOP QUAKE
DMM.com 風船と少年とイソギンチャク、空へ昇る。
PLAYISM SUBLIGHT

Sublight5TopQuake2Husen2

学生がUE4に、そして市場にチャレンジ

このプロジェクトはアミューズメントメディア総合学院(以下、AMG)の2年生が、1年生の冬に制作した作品(C言語)をUnreal Engine 4でリメイクして販売する、というプロジェクトで、私は担当講師&UE4技術指導という形で参加しました。AMGは毎年様々な形で産学共同プロジェクトを行っているのですが、自分がAMGの卒業生(ゲーム企画科卒業)でもあり、普段はプログラムの講師もしていることから、このプロジェクトが始まりました。

私は常々思っていました。ゲームエンジンが一般層に普及してきた今、物量を除けば、学生やホビーユーザーの作品が市場に通用する時代がやってきたのでは?と。もちろんゲームエンジン関係なくそのような作品は過去たくさんあったわけですが、明らかにそのハードルは下がりました。そのような流れの中、インディーとはまた違った若い学生の感性で作られた作品たちが市場にリリースされれば、それはきっとプロからは出てこないような新鮮な作品が生まれるのではないかと。そんな思いを抱えていた自分にとっては、今回のプロジェクトはとてもやりがいのあるものでした。

 

“UE4とは?”からのスタート

今回は3チーム(1チーム4~6人)がこのプロジェクトに参加し、期間は約5か月。まずは”UE4とは?”を教えるところからスタートしました。また、今回は1年生で制作した作品のセルフリメイクとはいえ、目標は”売れるもの”。ゲームの内容も1年次の内容からどう変えたら売り物になるのかという観点から企画を練り直しました。特に初期の時点で「SUBLIGHT」はゲームデザイン的に大きく手を入れることになり、リメイクとは言えない全くの別物になっていたので、タイトルも1年の冬の作品からは変わっています。

そして、企画を練りつつプロトタイプからのスタート。いちから「このタイプの作品を作るときはこうやって作るんだ」と教えたい気持ちもありましたが、自分も週に1回しか教えられない都合上、最初に簡単にUE4の使い方と各プロジェクトの設計の方向性だけ示し、あとは各自調べながら進めてもらいました。当時は極め本も発売されていなかったため、オフィシャルドキュメントが中心です。厳しいかなと思っていたのですが、そこは学生の頑張りとオフィシャルドキュメントの翻訳率の高さで、思った以上の習得スピードで物が出来上がっていきました。

現場では人に聞くことも含めて、やりたいことに対するアプローチを自分で能動的に調べるという習慣が非常に大切です。その点において参加メンバーは私の想像を超えた力を発揮してくれました。特に「TOP QUAKE」チームは破壊が技術的懸念点でしたが、初期の技術検証が驚くほど進みが良かったです。

 

全職種がUE4上で作業するワークフロー

今回学生はプランナー、プログラマー、デザイナーの3職種が参加したのですが、プログラマーだけでなくプランナー、デザイナーもUE4上でガシガシ実装していくという、非常にゲームエンジンを活かすワークフローで制作できました。仕様書をプランナーが、素材をデザイナーがつくり、そしてそれをプログラマーが組み込むというワークフローでなく、プログラマーがBPを(今回は3作品共にBPオンリーです)、プランナーがレベルデザインとカットシーンを、デザイナーがマテリアルやライティングをUE4上で行い、実装を進めていったからこそここまで出来たのだと思います。

今回の制作において、1人でもその分野のキーマンが居ると、良いクオリティでどんどん実装が進んでいくという姿を何度も見ました。私も技術サポートを行いましたが、あくまでサポート。個人個人の個性を発揮してどんどん進んでいく経過を毎週楽しみにしていました。中でも世界観重視の「風船と少年とイソギンチャク、空へ昇る。」チームは、毎週着々とどうユーザーに見せるかをプランニングし、それをデザイナー/プランナーがビルドで示していくという流れがスムーズでした。

 

技術面よりもゲームデザイン面の難しさ

制作を進めていく中で技術面での難しさもありましたが、「どうやったら売れる作品をその期間で作ることができるか」、「どうやったら面白いものになるのか」というゲームデザイン面での難しさの方が大きかったように思えます。今回の3作品はどれも一山も二山も超えた先に、リリースされたものです。このプロジェクトのゴールが”売る”というところにあったため、自分も「スケジュールやみんなの技術的にここで折り合いをつけたい……でもこのままじゃ売り物にならない」という難しい判断を迫られるシーンが多かったです。しかしそこはやはりプロジェクトのゴールが”学生作品の完成”ではなく”売る”というところにある以上、多くのケースで差し戻しました。

そして、その先に自分の予想を超えるアイディアでハードルを越えてくれた瞬間、その時が一番このプロジェクトをやっていてワクワクした瞬間です。これがゲーム作りの醍醐味であり、チーム制作の楽しみなんだなと。そのナイスなアイディアをこぼさずに実装するためのゲームエンジンなんだなと。そんな瞬間があるたびに、改めて思いました。

 

“発売”という重み

昨日改めてこのプロジェクトに参加したひとりと「明日発売だね」という話をして、ノートに「25日発売」と書いたところ、「そっか、発売なんだ。世に出て誰かが買って遊ぶんだ」と、改めて”発売”というものの重みを感じました。普段からいかに商品クオリティでユーザーのもとに届けるか、を考え続けているわけですが、自分も昔学んだ教室から生まれたものが”発売”されるというのは、また今まで感じたことがない新鮮さでした。

「SUBLIGHT」「TOP QUAKE」「風船と少年とイソギンチャク空に昇る」、3作品発売できたこと、とても嬉しく思います。本当にプロジェクトメンバーのみんなの頑張りだと思います。今回はDMM.comさん、PLAYISMさんともに、TGSでの出展や実際の作品を見てからサイトに置くことを決めて頂きました。もちろん、まだまだこれから大きく伸びていく彼らの作品ですが、今だからこそ創れる作品にもなっていると思います。

“売る”を目的に作ってきた学生作品。その結果がどうなったかは、ぜひみなさんの目で確かめてみてください。

 

[UE4] Actorの3D座標位置にWidgetを表示する方法

$
0
0

以前ブログでHUDからActorの3D座標を利用する方法を書きましたが今回はUMG(※1)で実装してみたいと思います。

HUDでは[Project]というノードで3D座標を2D座標に変換していましたが「Event Receive Draw HUD」の呼び出し中にしか使用出来ないという条件がありました。

今回は他の方法で実装したいと思います。

1:Widgetの作成(デザイン)

まずはじめに今回使用するWidgetを作成します。

キャプチャ30

デザインについては目をつむってもらうとして・・・

キャプチャ20

このような構成にしてます。

[Overlay]をルートとし、その下にテクスチャを表示するImg_BG(ImageのWidget)と文字表示用のText_Message(TextのWidget)をそれぞれ配置しています。

ここで注意なのですが、ルートのWidgetが[Canvas Panel]を使用した場合はその下のWidgetのAnchorsをサイズに合わせて指定するようなものにするとうまく表示されないことがあります。

無題

↑の画像で赤で囲んだものや手動で設定した場合などでは表示されませんでした。

なのでその場合はAnchorsはセンターのものを指定するようにします。

 

2:Widgetの作成(Blueprint)

次にグラフに移りBlueprintを書きたいと思います。

 

新しく作ったイベントは1つだけです。

Widgetの位置を取得するためPlayerControllerの[ConvertWoldLocationToScreenLocation]というノードを使用して2D座標を取得しそれを[Set Position in Viewport]を使用して位置を設定しています

キャプチャ

↑の画像の[TargetLocation]がActorの位置になります。

後はTickなどでこれを呼び出してあげればいいのですがWidgetのTickはWidget自体が画面外に出てしまうと呼ばれなくなってしまうため別の方法で呼び出す必要があります。

キャプチャ

[Construct]で[Set Alignment in Viewport]で表示位置をActorの上にくるようにずらします。何も設定しない状態だとWidgetの左上が基点になります。

今回はActorの少し上に表示されるように[0.5,2.0]にしてます。

以上で終了です。

 

 

後はこのWidgetにActorの位置をセットして表示してあげれば・・・

キャプチャ

このようなかんじに表示され、移動してもActorの上に表示されるようになります!

 

 

 

 

 

※1

UMG(Unreal Motion Graphics
UMGでの詳しい操作方法などはドキュメントを参照してください。
https://docs.unrealengine.com/latest/JPN/Engine/UMG/UserGuide/index.html

[UE4] SpriteStudioプラグインを更新しました

$
0
0

ヒストリアで開発を担当しているUE4用SpriteStudioプラグインですが、このたび SpriteStudio5.6.0 までの機能に対応した新バージョン「v1.1.0_UE4.10_SS5.6.0」がリリースされました。

 

本プラグインは、下記のGitHub上のページからダウンロード出来ます。
https://github.com/SpriteStudio/SS5PlayerForUnrealEngine4

プラグインのドキュメントはこちらです。

http://historia.co.jp/spritestudio

SpriteStudio本体については、下記のウェブテクノロジ様の公式ページをご参照下さい。
http://www.webtech.co.jp/spritestudio/

 

今回のバージョンアップでは、SpriteStudio5.5で実装されたエフェクト機能と、SpriteStudio5.6で実装されたカラーラベル機能への対応が実装されました。

 

エフェクト機能

SpriteStudio5.5で、パーティクルエフェクトを制作する機能が追加されました。
これまでのキーフレーム機能だけでは難しかった、大量のパーティクルをランダムに発生させる事が可能になっています。

particle_editor

UE4プラグインでも、今回のバージョンアップでエフェクト機能の再生が可能になりました。

e013 e014 e036

 

もちろんサンプルプロジェクトにも組み込み済みです。SsPlayerExamples プロジェクトを開いたら、下記のアセットを開いてみて下さい。

Content / SsAsset / particle / ParticleEffectSample

 

このサンプルアニメーションの元ファイルは、ウェブテクノロジ様のホームページからダウンロード可能です。

下記のページから、「エフェクト機能を使ったパーティクルエフェクトのサンプル」を参照して下さい。

http://www.webtech.co.jp/help/ja/spritestudio/download/tool_sample_download/#sample

 

これを参考に、是非新しい2Dエフェクトの制作にチャレンジしてみて下さい!

 

カラーラベル機能

カラーラベルはSpriteStudio5.6から追加された機能です。
エディタ上で、パーツ毎に任意のカラーラベルを指定しておき、ランタイムでこの値を使用して、様々な処理に利用することが出来ます。

colorlabel

今回のバージョンアップで、UE4のブループリントからもこの機能が利用出来るようになりました。

GetPartColorLabel

従来のユーザーデータに近いような形で、ゲームへの組み込みに利用することが出来ます。

 

本プラグインは、今後もUE4やSpriteStudioのバージョンアップに合わせての更新が予定されています。
是非一度ダウンロードして、サンプルプロジェクトを覗いてみて下さい。

[UE4] 名古屋 / 仙台 Meetup お疲れ様でした

$
0
0

名古屋 / 仙台 Meetup ご参加の皆様、お疲れ様でした。

 

第2回 UNREAL ENGINE4 名古屋 Meetup!! #ue4studynagoya
【UE4】Sendai Unreal Engine MeetUp! Vol.2

 

当日は両会場共に盛況だったようで、UE4の盛り上がりを感じています。
今年は昨年よりも更にこういったイベントが増えていくと思いますので、積極的に参加していけたらいいなと思います。

 

また、予告していました通り、名古屋での講演内容に関して公開致します。

 

 

スライド内にもありますが、こちらがデモ用の動画となります。
(当日は同内容をライブノーディングでご紹介しました)

 

開閉するドアを作るデモ動画

 

ぷちコンで使った各種機能の簡単なデモ動画

 

次回のぷちコンは春頃を予定しております。
興味のある方は是非ご参加下さい。

Viewing all 989 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>