【UE4】OculusTouchでHapticEffectが使えないとき
PlayHapticEffectが動かない
Touchのコントローラの振動にはPlayHapticEffectを使えとあります。
要約すると、ここからHaptic用アセットを作って適用できるよと。
- Haptic feedback Effect Buffer 数値指定
- Haptic feedback Effect Curve カーブで指定
- Haptic feedback Effect Sound Wave 音声ファイルから生成
と3つあります。
今回は適当にカーブを作りました。
でHapticEffectに入れて終わり。あとは好きなところで。
これで使えるようになります。
しかし、手を抜きたいあまり、Riftのセンサーを片手で塞ぎながら空いてる手でデバッグすると反応しませんでした。
どうやらTouchを持った時に生じる揺れ(人の手は完全に静止しないので)をTriggerにしてるようです(勘)。
なので両方持つか、ちゃんと被るか、足にでもつけるかしないと振動しません。
【UE4】簡単な音声認識をしてみた
はじめに
裏 Unreal Engine 4 (UE4) Advent Calendar 2016 10日目です。
本記事の主役とも言えるほげたつさんのアセットのレビューがされている こちらの記事も合わせてお読みください(何ももらってませんよ)
今回はちょーお手軽に音声認識をしてみようと思います。
というのもmikulusを見てるとUnityは標準で音声認識が出来たり、プラグインを探すと大体英語対応のものばかりで辛い目に遭いました。
なので作りました。
現在作成中のVR内で使えるキーボードに音声認識で入力アシストするのに使っています。
イメージ
音声認識で入力アシストできるようにした #UE4 #VR_keyboard https://t.co/NYLHmjmO1H
— てんちょー (@shop_0761) 2016年11月20日
こちらテスターを募集しているのでOculusTouchがある方はぜひお願いします。@shop_0761にご連絡ください。(圧倒的テスター不足)
方針
色々遠回りしたお話は、あとに書くとして端的に実装方針を説明します。
http通信を用いて実装します。
しかもこれ、ほげたつさんがすでにプラグインを作成して公開しています!!
まぁWebApiプラグインはUE4を触り始めて1週間くらいで作り始めたものだから直すところ沢山なのは仕方ない
— ほげたつ (@HogeTatu) 2016年11月18日
つよい。
ちなみに
@shop_0761 たぶん今後は要望があればバージョンアップ対応だけやって非推奨になると思いますー
— ほげたつ (@HogeTatu) 2016年11月18日
らしいので先日のこちらの記事を参照してゆくゆくは移行しましょう。
http通信については適当にググるか、頭を空っぽにしてそういうものがあるんだなくらいでも動きます。
参考サイトを貼っておくので自作しても構いません。先人は偉大!
参考:
面倒な方は僕がすでに作成したものでも使ってください。実行するだけです。おわり。
ダウンロードするだけで環境が整ってしまいました。
音声認識の精度について
精度的なお話だと、
こちらのほうがいいかもしれません。ただなぜか手元の環境だと、
await contSpeechRecognizer.ContinuousRecognitionSession.StartAsync();
のところで The speech privacy policy was not accepted prior to attempting a speech recognition. と言われてしまい実行できず…
音声認識のプライバシーポリシーってどこで設定するんだ…となってしまったので詳しい方がいればぜひお教えいただけると嬉しいです。
このように認識は外部のツール依存なので、自作してチューニングすればよりよいものになるかと思います。
実装する
と言っても、大したことはしません。
先程のほげたつさんのプラグインにはサンプルプロジェクトWebApiDemoがあるので、それを起動します。
その中にあるWebApiDemo/WebApi/Api 内にあるBP_DemoApiBaseを開きましょう。
そして、継承元の変数を表示するために赤線のところにチェックを入れます。
すると
Domain変数が見えます。
今回はアプリのポートの都合上ここを http://127.0.0.1:50001/ に書き換えます。
あとは、さっきの音声認識のツールを実行後にUE4側で実行しましょう。
SimpleGetを押せば終了!!
音声認識の結果が得られます。
ちなみにこのStringはBP_01_SimpleGet内のResponsebodyに入ってるので後はご自由に。
VRKeyboardの方ではTickで適当にポーリングするBPComponentにしてます。
応用例
普通に音声認識してそのまま使ってもいいのですが、ちょっとあちこち手を加えてこんなことをしてみました。
声が先に聞こえるよ(いっこく堂風) #UE4 https://t.co/r7Po9pnMTD
— てんちょー (@shop_0761) 2016年12月5日
音声認識した結果を使ってLipSync的なことをしています。これをLipSyncさせても面白いかもですね。
[UE4] Grayちゃんと会話 https://t.co/pbFJ5YjaLq
— ほげたつ (@HogeTatu) 2016年11月27日
Docomo雑談APIを使ってGrayちゃんと会話してみた。APIリクエストには開発中のNekoNekoOnlineプラグインを使用。 #UE4
色々できるので遊んでみてください。
辛い目にあったお話
これ以降は今回の記事に至るまでに迷子になった余談なので、気が向いたら読んで下さい。
きっかけはこの辺
ぬああああ!やっとできたww簡単すぎるところで躓きすぎ( ;∀;)ww
— gsk (@gsk39) 2016年10月20日
ヒストリアさんブログみたら一発だったありがとうでした( ;∀;)ww pic.twitter.com/pRW1ecCu39
こんなのあるんだ と思って調べてみたら Pocketsphinxってのがベースらしく
pocketsphinx 多分日本語に対応していないのでsphinxプラグインつらそう
— てんちょー (@shop_0761) 2016年10月20日
と、以外にも日本語の音声認識プラグインが無いことを知りました。
しばらく経って、先程のwin10で動く音声認識のサンプルがあるのを知ったので、これUDP通信で実装するできそうと思ってました。
よく聞くJuliusとかでプラグインでも作ったほうがよさそうかなと思いましたが、あれはデフォのままだとかなりアレな感じなのでガツガツやるにはいいのかもしれません…(DNNベースでもHMMベースでもできます)
じゃあさっそくH社のUDPのサンプルを読みつつ進めていくと、デシリアライズの方法で引っかかりました。
UDP Socket Sender Receiver From One UE4 Instance To Another - Epic Wiki
こことか見てたんですが、日本語stringの場合文字コード周りで死にたくなりましたね…。C++から文字コードを日本語にする方法が見当たらなかったので、 お友達に聞くとpythonをかますか、http通信にしたほうがいいよと言われたので、今回の方法に落ち着きました。
文字コード周りで辛い目に遭っている
— てんちょー (@shop_0761) 2016年11月13日
UnrealC++を読む機会が出来たので、副産物的に前よりは抵抗感がなくなりまし た。
ほげたつさんのコードを読んでUnrealC++を覚えていこうな
— てんちょー (@shop_0761) 2016年11月18日
まとめ
そろそろデフォで音声認識ができてもいいんじゃないかなとは思います。
ただVR_keyboardで日本語の変換周りを扱っていると日本語難しいなと思ったので、当分ないだろうなと思ってしまいました。
明日はprince_ue4さんの「エフェクトネタやります」です。
【UE4】水彩風マテリアルを作ってみた
はじめに
裏 Unreal Engine 4 (UE4) Advent Calendar 2016 8日目です。
我らがおぎまふさんやcom04さんなど、いろいろな方がトゥーン表現に挑戦されています。
最近ではMarketPlaceでもトゥーンなアセットが出てきたりしています。
いろいろ参考サイトをまとめていたのですが、com04さんの記事を見たほうが早いですね…
ですがやっぱり自分好みのものを作りたい…と思って探していたら参考になりそうなところを見つけました。
こちらのサイトではsoftImageですが、なんとかそれっぽくなりました。
マテリアルダメダメなので、練習ついでに作ってみました。
できたもの
水彩風マテリアルがだいたい出来た#UE4Study pic.twitter.com/yUPdarH34r
— てんちょー (@shop_0761) October 18, 2016
ズームアップした pic.twitter.com/pl1rVDxyAh
— てんちょー (@shop_0761) October 20, 2016
つくりかた
ここから先は、先程の参考サイトを見た前提でお話を進めていきますね。(さらっとでおkです)
考え方、手順は参考サイトと同じなので それをどうやって再現したか という感じで進めます。
とりあえず今回は
- BaseColor
- Specular
- Shadow
に当たる部分のマスクを愚直に作って色指定、その境目にNoiseを入れてなじませていきます。コンパイルが重いかも。
ついでに、各色をテクスチャではなく直で色指定しています。モデルによっては細かくマテリアルが分けてる 方が相性がいいです。処理負荷は気にしないことにします。
BaseColorのマスクを作る
BaseTextureを指定できますが、結局Desaturationしてしまうのでそのままでもいいかも。
Blurをかけます。
Blurノード
float4 output = 0; float weightSum = 0; float currentWeight = 1.0; for (int i = 0; i < 10; ++i) { output += Texture2DSample(Material.Texture2D_0, Material.Texture2D_0Sampler, texCoord + i * stride) * currentWeight; weightSum += currentWeight; } output /= weightSum; return output;
参考:
そして、ちょっとNoiseをのせたり。 もちろん、すでにあるNoiseノードでもいいかと思いますが今回はstylized RenderingのT_TexturedPaperを使用しています。
Specularのマスクを作る
こちらはおぎまふさんを見習ってLightVectorを擬似的に用意して、Specularの計算をします。
こうして出来た2つをDesaturationしてそれぞれマスクとしました。
ここまででとりあえず2色分塗ることが出来ました。
ShadowとEdgeのマスクを作る
ここで影色も塗らなきゃなりませんが、このままでは塗れないのでゴリ押しで解決します。
影色になる部分を一旦求めて、今の絵に黒で塗り直します。(いい方法が知りたい)
こうすると後からaddして色を乗せられます。
こちらはおぎまふさんのRimLightの実装をそのまま使ってますので関数の中身は割愛します。
水彩の参考サイトの完成絵を見るとエッジが2段あるように見える(実際は歪めたり、ノイズかけたり)のでRimLight_Rangeにちょっとaddして作ります。
NoiseありとなしのEdgeがあったほうがよかったので、分けて作ってます。
ついでにさっきのshadowマスクを使って、明るい方のEdgeと暗い方のEdgeを作り分けてます。
ここで影色も作ってます。
まとめる
最後にshadow側とBaseColor側をaddして完成です。 お好みでNormalMapも使えるようにswitchをつけたりしました。
こうすることでGraymanの胸のUnrealロゴが出せます。
余談
最初はこのマテリアルをPostProcessで実装していたのですが、マスクできるようになったけど色指定できない…ってなったのでやめました。
その時、最初に作っていた影色を少し淡くするPostProcessが出来たのでついでに載せておきます。
そしてさらにalweiさんのお手軽トゥーンと比較
— てんちょー (@shop_0761) October 8, 2016
影がキツくなくなった感 pic.twitter.com/C4bixB3Ii2
alweiさんのお手軽Toonのマテリアルにちょっと足すだけです。
全体
追加した部分
CustomDepthでマスクを作った結果を使ってなんかごにょごにょしました。
でこの結果を最後にaddして出力してます。
余談2
blurのコードを書いてるときに思いついたのですが、blurベースでedgeが取れそうだったのでやってみました。
このぐらいいけます。
ノードはこれだけ 面倒なSobelフィルタを作らなくても使えるかも。
Blurのコード
float3 output = 0; float ScreenMult_X = GetPostProcessInputSize(0).zw.x; float ScreenMult_Y = GetPostProcessInputSize(0).zw.y; output += 1.0 * SceneTextureLookup(TexCoord + float2(ScreenMult_X * BlurAmount * -1.0, 0.0), SceneTextureID, true); output += 1.0 * SceneTextureLookup(TexCoord + float2(ScreenMult_X * BlurAmount * 1.0, 0.0), SceneTextureID, true); output += 1.0 * SceneTextureLookup(TexCoord + float2(ScreenMult_X * BlurAmount, 0.0), SceneTextureID, true); output += 1.0 * SceneTextureLookup(TexCoord + float2(0.0, ScreenMult_Y * BlurAmount), SceneTextureID, true); output += 1.0 * SceneTextureLookup(TexCoord + float2(0.0, ScreenMult_Y * BlurAmount * -1.0), SceneTextureID, true); output += 1.0 * SceneTextureLookup(TexCoord + float2(0.0, ScreenMult_Y * BlurAmount * 1.0), SceneTextureID, true); output /= 6.0; return output;
SceneTextureIDを引数にすることで、任意のSceneTextureを指定できます。 outputは加算した分だけ割ります。
多分この順番通りでIDが振られてると思います 分からなかったらコードを読めばいいんだよ うん。
まとめ
なんとなくMaterialの演算が分かった気がします。もろもろの計算は試行錯誤の結果なので、「なんでこうなるの?」というより「色々やったらこれがよかった」って感じです。
作りたい絵を目指して、実際に手を動かしてみるとこの意味がわかるかと思います。すごい人になれば頭のなかで完成形をイメージしながらノードを組めるかと思いますが()
まずは作りたい絵を探しましょう!!
わからないこととかあれば、可能な限りお答えするのでTwitter(@shop_0761)等でご連絡ください。
明日は Epic社のおかずさん(@pafuhana1213)の「帰ってきたUE4のマーケットプレイスで購入したアセットを片っ端からレビューするマン」です。
鹿のレビューが懐かしい…
頭の悪いHMDのレビューをしてみる
はじめに
Oculus Rift Advent Calendar 2016の2日目です。
昨日は桜花一門さんのAllumetteが素敵な理由でした。
N回目のVR元年ということで、いろいろHMD(Head Mount Display)が出てきました。
気付いたらうちにもたくさん生えてきました。
ファッションVR勢です pic.twitter.com/KvhdHh6K33
— てんちょー (@shop_0761) October 22, 2016
ということで、これらのHMDのレビューでもしようかと思います。
と言ってもだいたいのレビューはあちこちですでになされているので、別の観点からします。
題して
一番寝心地のいいHMDはどれだ選手権!!
すでに皆さんも一度は被って寝落ちしているかと思いますし、僕自身も手元にあるHMDを被ったまま(意図して)寝たことがあります。 ですが改めて記事になっているものはなかったので、チャンスだと思いました。
注)別に各HMD、コンテンツを批判したいわけではありません。どれもいいものですし、お世話になってます。
事前知識
VRについて全くわからない方はこちらのリンクとかを見てみるといいかもです。一つ目の動画は割りとイメージつかみやすいかも。
評価
評価方法
実際に被って寝(落ち)ます。メガネかけてます。
Rift以外はヘッドホンを使います。
実験時間は寝てから起きるまで。最近あまり長く寝れないことが多いのでだいたい2-3時間です。
(そもそもほんとに眠いときは準備する気になれない)
評価基準
- 起きたときの没入感
- 寝返りのうちやすさ
- コンテンツの寝やすさ
- 被った状態での後頭部へのやさしさ
- 総合評価
をそれぞれ5段階で評価します。かなりアレなやつは☆になります。
結果
Rift
Mikulusの場合
mikulusについてはこちら。
膝枕をしてもらうかのように調整してみました。 他にもたくさんやっている方がいるので、写真は割愛します。
この辺でも覗いてみてください。
起きたときの没入感 ★★★★
目を開けたら目があった。 びっくりした。
寝返りのうちやすさ ★★★★
ヘッドホンが付いているので便利。 しかも軽いのがいいですね。
あまりゴロゴロするとヘッドホンの部分が壊れてしまいそうなので、おとなしくしてました。
コンテンツの寝やすさ ★★★★★
あの これ 呼吸音と心拍音がいい感じに落ち着く感じ非常に良い…
こう看取られてる気分ですね…(看取られたい
被った状態での後頭部へのやさしさ ★★★★★
Riftの後頭部のつくりは以外にもクッションが効いてて、あまり違和感がありませんでした。
後頭部にもセンサーがあるそうなので、これを使ってなにか作れないかなとか思ったり。
総合評価 ★★★★
最初は寝るのにちょうどいいコンテンツを思いつかなくてVirtualDesktopにでもしようかと思ったらMikulusがよさそうだったので選びました。
参考:
目をつぶってしまえば現段階での呼吸音の違和感もなにもなく快適でした。
というか呼吸音と心拍音が聞こえるだけの音声を聞けばいいのではという意見は受け付けません。
目を開けたらそこにいると思えるほうがいいのです。はい。
Riftだとメガネが厳しいHMDなので、そこさえなんとかなれば(というか被ってしまえば)意外とお布団でも使えることがわかりました。
HTC Vive(Pre)
The lab 丘の場合
The labについてはこちら。
丘で横になったときはこんな感じ。鳥が飛んでます。
起きたときの没入感 ★★★
起きて外した時にあれ部屋が狭い…?っと軽く思うくらいには没入していたようです。
まあ曇り空なんですが。
寝返りのうちやすさ ★★
ちょっと重いので頭が持って行かれます。出来なくはないですね。イヤホンにしたらもっといいかも。
コンテンツの寝やすさ ☆
Thelabの丘で寝ると上で鳥が飛んでいます。いい感じに環境音があって快適…かと思いきやタイミングやら位置やらが悪かったのか例のロボット犬が周囲で暴れまくっています。
いやあの、ほんと戯れる分にはいいんですよ… 寝るのにはうるs…(めっちゃギュインギュインしてた
ロボット犬 On/Offできると嬉しい。
被った状態での後頭部へのやさしさ ★★★★
Viveだと後頭部からまっすぐコードが伸びてます。
このためコードのことを思うと頭の下にそのままコードがくることになると思います。このためちょっと違和感があるかなという感じでした。
工夫次第でよくなるかもって感じです。
総合評価 ★★★
ぱっと思いついたコンテンツがコレだったのですが、犬が…犬が…という思いで完全に失敗しました。
といっても、他にこういうのが思いつかなかったのでオススメが知りたいです。
Viveは以前VirtualDesktopをしながら寝ていたこともあるのですが、如何せん重い…首が疲れます。
寝ているとそれは多少軽減されるのですが、ちょっとでも動くとぐいっと持っていかれる感じがします。
PSVR
サマーレッスンのひかりちゃんの部屋のベッドの場合
サマーレッスンについてはこちら。
ベッドの上に座った感じ
ベッドに寝っ転がった
起きたときの没入感 ★
そもそも寝れなかった 頭痛すぎ。
寝返りのうちやすさ ★
プレイエリアの外に出ると目を開けた時に気が散ってしまうので、なかなか動けません。 ヘッドホンをしているとさらに動けないです。イヤホンにしたらいいかも。
しかも全体的に大きめなので壊しそうであまり動けない。
コンテンツの寝やすさ ★★
まあ確かに誰かがいる部屋で寝てる感は出てるっぽいので、ぼちぼち。
どのタイミングで寝るかに迷うかも。(教えてる時、ハンコを押す前、押した後)
今回はハンコを押した後にしましたが、突然話しかけてきたりしてビビります。
教えてるときはBGMがかかってしまうのでやめました。ので、ハンコを押す前がいいかも。
場所を調整するのがかなり難しかったです。 なかなかベッドの位置に移動出来ないし(当たり前)、低すぎると暗転してしまいます。
被った状態での後頭部へのやさしさ ☆
良くない これは頭が痛い 後頭部のパーツが痛すぎる。
他のは薄めのバンドだったのにこれだけやたらしっかりしてるせいで、 寝っ転がった時にこの後頭部のパーツにばかり力がかかって痛い。
そのせいではずした後に頭が痛くなってしまった。
総合評価 ★
PSVRは寝るのに挑戦したのは3回ほどあります。一度だけ座椅子にもたれかかって寝ることに成功しましたが、 今回のように布団や床で寝ることを想定すると後頭部の出来のよさが仇になってしまっています。
後1回はAmazonPrimeVideoでアニメを見ていました。結局後頭部の痛みが気になり寝るにねれない状況でした。 よっぽど疲れている、眠い状況であれば寝付けるかもしれませんが、数時間でこのダメージなので ちょっと怖いですね…
番外編 GearVR + GalaxyS6 edge
niconicoVRの場合
起きたときの没入感 ★★★
まあ単に動画を見ていただけなので、特にやべえコレ みたいなことにはなりませんでした。
寝返りのうちやすさ ★★★★★
これは超いい ほんとにいい モバイルの良さを思い知らされました。
なにより軽い。余計なコードがない。寝返りがうてる。こんなに違うものかと…
思わずゴロゴロ転がってしまいました。
コンテンツの寝やすさ ★★★
眠くなる動画とか見ればいいのでは 終了。
被った状態での後頭部へのやさしさ ★★★★★
こいつもやわらかバンドなので違和感は無いに等しい。 頭が痛くないのはいいことですね。
総合評価 ★★★
手元にあってすぐ試せるniconicoVRにしてみましたが、割りとパフォーマンスがいいのか 3時間くらい持ちそうです。一回も落ちてないっぽい。2時間半くらいで100%→28%だった。
正直GearVRなので寝ろと言わんばかりに熱くなっておしまいかと思ったら 長持ちしてびっくりしました。
余談ですが、起きてからGear VR Cooler!(物理)と題して、雪を詰めた袋をくっつけるだけの 記事を書こうかと思いましたが5秒でやめました。
考察
ということで、今回はRiftが一番良さそうという結果になりました。 ヘッドホンよりイヤホンがいいかもと思ったので完全に選択ミスですね。動けない。
一応どのHMDでも(AmazonPrimeVideoのような)映像を見るだけのものが出来ますが、せっかくなら いいところでお昼寝したいです。欲を言えば寝ても覚めても被って生活したいです。
UE4で言えばKiteDemoのマップは寝心地よさそうな気がします。
参考: www.youtube.com
また今回は扱わなかったハコスコなどのモバイル系は一晩も使えないのがネックですね。
ダンボールだと固定できないので、どうしてもという場合のみアロンアルファで顔にくっつけてください。
参考
http://www.hashiyansoft.com/contents/dc/death.html
これからはVR Nevangelist(寝バンジェリスト)とでも名乗ってみようかな!!
ぜひこの記事を参考に寝心地のいいHMDを購入してみてはいかがでしょうか。
明日はnoshipuさんの「VRアプリ作って思った事とか書きます」です。
おまけ
ちなみにSAOのキリトくんが被っているナーブギア
ヘルメット系は詳しくないのですが、これとても首を痛めそうな気がします。
調べてみると、やっぱり何人かの方がヘルメットを被って寝たことがある模様。特に寝心地レビューみたいのが見当たらず残念。
アミュスフィアはこんな感じ
HoloLensに近い気がしますが、やはりPSVRの例から後頭部に優しくないのではと思ってしまいます。
なのでメディキュボイド型が最強なのでは!?!?
点滴してもらえそうだし。
やっぱりこっちのメガネも捨てきれませんね(もはやVRではない
こちらは確か作中では寝る時に外していたような気もします。
おまけ2
ちょっと調べてみるとこんなものがあったのを思い出しました。
確かに寝る分にはいいかもしれませんが、VR空間で寝たいのでうーんという思い。
【UE4】SaveとLoad時のUserIndexについて
はじめに
時々SaveとLoadに苦戦するのでよくお世話になっているほげたつさんのサイトです。
ここには
セーブデータにはスロット名とユーザーインデックスが指定できます。 スロット名はセーブデータの名前、ユーザーインデックスはその中でのIDみたいなものですね。 ドラクエの冒険の書1とか冒険の書2とかそんなイメージです。
とあります。
もちろんこちらにも。
For some platforms, master user index to identify the user doing the saving.
ただ実際に実装してみると、なにやら動作がおかしいので調べてみました。
ソースコード
ということで、Engineのソースコードを追います。UE4.13です。 ctrl+FとかでUserIndexをハイライトしておくと、見やすいかもしれません。
Runtime/Engine/Private/GameplayStatics.cpp
USaveGame* UGameplayStatics::LoadGameFromSlot(const FString& SlotName, const int32 UserIndex) { USaveGame* OutSaveGameObject = NULL; ISaveGameSystem* SaveSystem = IPlatformFeaturesModule::Get().GetSaveGameSystem(); // If we have a save system and a valid name.. if(SaveSystem && (SlotName.Len() > 0)) { // Load raw data from slot TArray<uint8> ObjectBytes; bool bSuccess = SaveSystem->LoadGame(false, *SlotName, UserIndex, ObjectBytes); if(bSuccess) { (省略)
bool UGameplayStatics::SaveGameToSlot(USaveGame* SaveGameObject, const FString& SlotName, const int32 UserIndex) { ISaveGameSystem* SaveSystem = IPlatformFeaturesModule::Get().GetSaveGameSystem(); // If we have a system and an object to save and a save name... if(SaveSystem && SaveGameObject && (SlotName.Len() > 0)) { (省略) // Stuff that data into the save system with the desired file name return SaveSystem->SaveGame(false, *SlotName, UserIndex, ObjectBytes); } return false; }
とSaveもLoadもUserIndexに関わる処理はSaveGame、LoadGameを呼び出しているのでそちらへ。
Runtime/Engine/Public/SaveGameSystem.h
virtual bool SaveGame(bool bAttemptToUseUI, const TCHAR* Name, const int32 UserIndex, const TArray<uint8>& Data) override { #if PLATFORM_HTML5_BROWSER return UE_SaveGame(TCHAR_TO_ANSI(Name),UserIndex,(char*)Data.GetData(),Data.Num()); #elif PLATFORM_HTML5_WIN32 FILE *fp; fp=fopen("c:\\test.sav", "wb"); fwrite((char*)Data.GetData(), sizeof(char), Data.Num(), fp); fclose(fp); return true; #else return FFileHelper::SaveArrayToFile(Data, *GetSaveGamePath(Name)); #endif }
virtual bool LoadGame(bool bAttemptToUseUI, const TCHAR* Name, const int32 UserIndex, TArray<uint8>& Data) override { #if PLATFORM_HTML5_BROWSER char* OutData; int Size; bool Result = UE_LoadGame(TCHAR_TO_ANSI(Name),UserIndex,&OutData,&Size); if (!Result) return false; Data.Append((uint8*)OutData,Size); ::free (OutData); return true; #elif PLATFORM_HTML5_WIN32 FILE *fp; fp=fopen("c:\\test.sav","rb"); if (!fp) return false; // obtain file size: fseek (fp, 0 , SEEK_END); int size = ftell (fp); fseek (fp, 0 , SEEK_SET); Data.AddUninitialized(size); int result = fread (Data.GetData(),1,size,fp); fclose(fp); return true; #else return FFileHelper::LoadFileToArray(Data, *GetSaveGamePath(Name)); #endif }
えーっとPlatformがHTML5のブラウザだけUserIndexを使っているように思います。
そしてこのUE_SaveGameとUE_LoadGameはこちらに。
Runtime/HTML5/HTML5JS/Private/HTML5JavaScriptFx.js
UE_SaveGame: function (name, userIndex, indata, insize) { // user index is not used. var _name = Pointer_stringify(name); var gamedata = Module.HEAPU8.subarray(indata, indata + insize); // local storage only takes strings, we need to convert string to base64 before storing. var b64encoded = base64EncArr(gamedata); $.jStorage.set(_name, b64encoded); return true; }, UE_LoadGame: function (name, userIndex, outdataptr, outsizeptr) { var _name = Pointer_stringify(name); // local storage only takes strings, we need to convert string to base64 before storing. var b64encoded = $.jStorage.get(_name); if (b64encoded === null) return false; var decodedArray = base64DecToArr(b64encoded); // copy back the decoded array. var outdata = Module._malloc(decodedArray.length); // view the allocated data as a HEAP8. var dest = Module.HEAPU8.subarray(outdata, outdata + decodedArray.length); // copy back. for (var i = 0; i < decodedArray.length; ++i) { dest[i] = decodedArray[i]; } Module.HEAP32[outsizeptr >> 2] = decodedArray.length; Module.HEAP32[outdataptr >> 2] = outdata; return true; },
いやあのこれ、UserIndex出番なくないですか…(引数として呼ばれているだけで未使用)
まとめ
現状だとUserIndexは出番がないようです。ちゃんと(?)設定していないはずのUserIndexでLoadしてもエラーが出ませんでした。
Forum等も漁りましたが、良さげな解答が見つからなかったので追ってみました。
どこか間違ってたり、新たに何か分かった時にはぜひ@shop_0761までお知らせください(更新します)
【UE4】自作アクションゲームにAutoPlayを仕込んでみた
はじめに
ぷちコンで作ったもろもろ紹介編2です。
元記事
今回はCEDECに行った際に聞いたAutoPlayのお話をもとに作ってみました。
講演はこちら。
資料はCEDiLにあります。
実際にAutoPlayしたものがこちら。
考え方
講演の内容をもとに(正確な内容はあやふやですが)AutoPlayの実装方法を簡単に説明します。
-------事前準備-------
マップにConeを置く
そのConeにはいくつかActionを登録しておく
-------実行時---------
ConeのActionを実行する
次のConeについたら上へ戻る
これだけです。AIは使わなくても実装できます。
Actionというのは絵に描いてあるような移動だったり、ジャンプだったりと なんでもいいわけです。
管理、保守など調整しやすいように難しいことはしません。
実装
実際に実装してみると、このConeのBPだけで大部分はまかなえてしまいました。
(あとはループ処理のためのConeManagerを作ったりしたくらい)
方針
ポイントを整理しておきます。
今回は予めConeを配置したあとにNextConeとして次のConeを登録しておく
今ActionをしているConeとNextConeのみCollisionを有効にする(例外あり)
くらいだと思います。
一つ目はConeと同じ型の変数を公開しておき、レベル上で設定していきます。
こんな感じ。
二つ目はそのほうが安全だよねってくらいです。
そして"例外あり"とは、スタート地点はCollisionを有効にしておかなきゃそもそも動かないのでという意味だったり、不具合対処の意味が大きいです。
この辺はまたあとで触れます。(※)
実際のBPはこちら(1-2日で作ったので一応整理したけど散らかっている感)
ConstractionScript
一応確実にジャンプして欲しい場面(大きな穴とか)があるので
全体像
BeginPlay
ここでCollisionの設定をしています。
BeginOverlap
NextConeのCollisionをEnableにしたあと、Wait秒待ちます。
落ち着いてジャンプしてもらうためです。
Debug用にPrintStringしてるだけです。Duration0は便利ですね。
到着判定
NextConeにたどり着いたかどうかをチェックします。
今回はVecLengthでやりましたが、2点間の距離でもいいと思います。
そして、先程の※のお話(ポイントの2つ目)がここで出てきます。
後述するRandomJumpを使っていたりすると、偶然Coneをスキップされてしまい、 Gateが開いたまま移動処理が残ったりする場面が多々ありました。
実装方法に問題があるのは分かっていますが、今回は一定距離が離れたりした場合でも到達したとみなして、処理を打ち切ることにしました。
Move
一応Axisの向きも内積でなんとなく求めておきます。
このMoveActionはキー入力の移動と同じ処理をするだけです。そのために追加したイベントです。
Jump
こちらのJumpは一度だけ確実にジャンプする必要があるときに使います。
JumpActionも先程のMoveActionと同じです。
RandomJump
適当なタイミングで適当に長押し(したフリをして)ジャンプします。
Reset
今回はループさせたかったので、Reset処理を追加しました。
ループさせる必要がないならDestroyしてしまってもいいかもしれません。
(そうするとさっきの※のお話はなくなるはず)
AutoPlayのメリット
最初は なんかすげぇでも作れそう とおもったので勉強がてら作りました。
何がいいかというとテストプレイ(周回系)が楽です。
- 実装に疲れたから、眺めて確認したい
- どうせちゃんと動くだろうから、操作したくない
- Twitterを見たい
などAutoPlayにしておけばこんなに便利です。
あとプレイ動画を撮るのも楽ですね。コミケ等の展示時に流していると面白いかもです。
まとめ
このくらいのスピード感で作ってました。
オートプレイ UE4なら作れるかもしれない
— てんちょー@9/30-10/2東京 (@shop_0761) 2016年8月26日
暇というわけではないけど、ぷちコンのオートプレイを作ってみよう
— てんちょー@9/30-10/2東京 (@shop_0761) 2016年8月28日
AutoPlayできたー
— てんちょー@9/30-10/2東京 (@shop_0761) 2016年8月28日
意外にあっさりできましたね。
ちなみに途中で操作介入できます。(意図的にジャンプさせたり)
なにかわからないことがあれば@shop_0761までお願いします。
【UE4】ParallaxOcclusionMappingで背景アセットを作ったお話【小ネタ】
はじめに
ぷちコンで作ったもろもろ紹介編1です。
元記事
HeightMapの描き方が分かりません。
けど、いい感じの背景アセットを手抜きで作りたい。
そんなこんなで出来たのが、これ。
意外に良くないですか 手抜きの割には。
作り方
Heightmapは描きました。手描き出来ます。
引っ込ませたい方を黒で塗ればいいんです。おわり。
あとはMaterialですが、ContentExampleにあったものを拝借してきただけです。
超お手軽ですね!!
深いことを考えなければ、パラメータはSampleそのまま使っていい気がします。
最初はalphaで抜こうか、なんて思ってた時期もありました。
ですが、シェーダ複雑度が真っ赤になったのでやめました。
これなら実質箱だけなので、描画負荷はalpha抜きに比べると軽いです。(上手くやれば赤いとこもなくなるはず)
試しにalpha抜きしてみました。(見栄えはおいておくとして
そりゃそうですねって感じです。
まとめ
ぱららっくすおくるーじょんまっぴんぐ ってカッコよかったので使いたかっただけです。
ContentExampleのは出来が良すぎて意味が分からなかったですが こんな手抜きの白黒絵を描いても使えるよ ということが言いたいだけです。
賞味20分くらい?だった気がします。
誰かもうちょっとまともな使い方の記事を書いて欲しいところですね。