【UE4】LevelSequenceでTrackとして追加されているActorのインスタンスを取得してイベントを呼び出す【UnrealEngine4】
はじめに
開発をしていてLevelSequence経由で発生させたいイベントがあり、LevelSequenceにBindされているActorの関数を呼び出そうと思ったら、SequenceDirector内でどうやってインスタンスを取得するのか調べてもわかりませんでした
Additional Event Receiversを使いたいのに、どこのサイトも「実際にレベルに配置する」のが前提で書かれており、Additional Event Receiversはレベルに配置しないと設定できないように思えたので動的に再生するLSを切り替えるケースではどうすればいいのかわかりませんでした
色々試していたらできたのでその手順をまとめておきます
もっといいスマートな方法があったら教えてください
環境
UE4.23.1
VisualStudioCommunity2017
Windows10
事前準備するもの
LevelSequenceを用意する
呼び出したい自作のBP公開されている関数を持つC++クラスを継承したBPクラス
今回用意したクラスはこちら
// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "MyActor.generated.h" UCLASS() class IMGUI_PRJ_API AMyActor : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AMyActor(); public: UFUNCTION(BlueprintCallable, Category = "Test Actor") void TestFunction(); UFUNCTION(BlueprintCallable, Category = "Test Actor") void BoolFunction(bool hoge); };
#include "MyActor.h" void AMyActor::TestFunction() { GLog->Log("TestFunction"); } void AMyActor::BoolFunction(bool hoge) { GLog->Log("BoolFunction"); }
手順
1. LevelSequenceを開く
※レベルに配置されてないのでDetailウィンドウにはなにも表示されません
2. 「Track > Actor to Sequence」からBP_MyActorを追加する
3. BP_MyActorの下にEventトラックを追加する
4. 追加したEventトラックを右クリックしてプロパティからBP_MyActorをEvent Receiversに登録する
5. キーを作成し、キーを右クリックからCreateQuickBindingからBP公開されているイベントを呼ぶ
5-2. BP_MyActorの下につけてないEventトラックではEvent Receiversに登録してもCreateQuickBindingが出てこないので注意
6. 追加すると確認LevelSequenceDirectorが開き、BP_MyActorのインスタンスを引数で持ってこれているのが確認できる(完)
6-2. LevelSequenceから呼ぶイベントにtarget以外の引数を増やしたらイベントそのものが無効になるので注意
さいごに
ざっくりまとめました
これややこしくない?これ以外にスマートな方法がある気がしてならないんですが、誰か教えてくれませんか?
気軽にコメントよろです
【UE4】「え?それで取得できるの?」と思ったAssetやクラスのGet周りのまとめ【C++】
この記事はUnreal Engine 4(UE4) Advent Calendar 2019の4日目の投稿記事です
はじめに
元々UE4C++でのゲームAIについて書いていましたが、諸事情により完成しませんでした・・・
なので大したネタじゃないです、すみません
変わりにUE4をC++で開発していて「え?これでインスタンス取得できるの?」と思ったGet系をまとめてみました
初心者向けでよろしくお願いします
公式ドキュメントのどこかに書かれているかもしれないですけど、まぁご容赦ください
Asset系取得
FObjectIterator
Module | include |
---|---|
CoreUObject | #include "UObject/UObjectIterator.h" |
ディレクトリにあるAssetを全検索して名前で引っ張ってこれるので「大丈夫なのかこれ?正しいのか?」と不安になるコード
void ASampleActor::BeginPlay() { UBlackboardData* bbData; for (FObjectIterator It(UBlackboardData::StaticClass()); It; ++It) { UBlackboardData* obj = (UBlackboardData*)*It; if (obj->GetFName().IsEqual("Sample_BB")) { bbData = obj; break; } } }
クラスの取得・生成
MakeShareable
ぶっちゃけ公式ドキュメントに普通に書かれているんですけど、全然存在に気づかなかったです
MakeShareableを覚えてからはこっちのほうが楽なので「ふ~ん、便利じゃん」となりました
void ASampleActor::BeginPlay() { Super::BeginPlay(); TSharedPtr<OriginalClass> hoge = TSharedPtr<OriginalClass>(new OriginalClass());//これと同じ TSharedPtr<OriginalClass> hoge = MakeShareable(new OriginalClass()); }
GetComponentsByClass
そりゃこういうAPIはあるよねって感じだけど、複数形の名前じゃないからてっきり1個しか取得できないと思っていたのにさりげなく複数取得できたのでちょっと混乱した
空目していただけで元々複数形だった。俺の目は節穴か
void ASampleActor::BeginPlay() { Super::BeginPlay(); TArray<UActorComponent*> compArray = GetComponentsByClass(USampleComponent::StaticClass());//複数 UActorComponent* comp = GetComponentsByClass(USampleComponent::StaticClass());//先頭1個 }
CastChecked< T >
UObjectを継承しているdynamic_cast相当の型変換はCastを使用しますが、CastCheckedはもしnullptrが入る場合にはエラーログを吐いたうえで停止します
「こいつがCastできないわけないが、もしnullptrが返ってきてたらわかるようにしておきたい」という場合に便利だなぁと思いました
void ASampleActor::Hoge(AActor* actor) { AFooActor* fooActor = CastChecked<AFooActor>(actor); }
スマホ向けゲームアプリにおける基本的なBattle(インゲーム)設計
はじめに
スマホゲーム業界ではデッキ編成やガチャなどのバトルではない部分を「アウトゲーム」Battleなどのゲーム性がある部分を「インゲーム」などと呼ぶ
※明確な定義はない
※この記事では単語を明確にするためBattleをインゲームと呼称する
どんなジャンルのゲームだろうと共通の抑えておくべき要点がある
ゲーム業界では仕様変更がかなり頻繁に行われる傾向にあり、それの影響か大人数で開発を行っているとクラスが肥大化しまくっていたり明らかに関係のないクラスや関数内に予想外の処理が行われてしまっているソースを読むことになるのは珍しくない
スマホゲームでは長期間の運営が想定された設計を行う必要があり「運営〇年目」となっても変更に耐えうる形にするには最初の設計が最も重要である
ここに書いてあることは人によっては「そんなこと・・・?」と思ってしまうものであるが、ここ最近スマホゲームを作りなれていない人にとっては当たり前ではないということがわかってきた
一人でも多くの人にインゲームの設計について最低限の必要な要点を理解してもらい皆幸せになってもらうために記しておく
要点は以下の通り
インゲーム設計で抑えるべき要点
1-1.インゲームだけで起動できるようにする(アウトゲームとの疎結合)
インゲームはアウトゲームとは完全に独立させておくのが最も重要である
理由としては
などが挙げられる
逆にこれができていないとこれから紹介する要点がすべて無理になるといっても過言ではない
Unityを使用しているなら、インゲームのSceneからPlayして直接最低限の通信のみで起動できるようにしておくべきであり、UE4を使用しているなら同じようにインゲームのLevelから再生すればバトルを実行できるようにするべき
「タイトル画面を通ってマスターデータを取得し、Topページへ遷移後クエスト選択画面で特定のデバッグクエストを選択」などという狂ったことを毎度繰り返さなくてはインゲームの動作確認ができないなんてことは断じてあってはならないのである
1-2.任意のステージ(クエスト)を任意のキャラクター編成で実行できるようにする
ただ実行できるだけでは意味がないのでUnityやUE4で実行していない状態でエディタ上乃至外部エディタで任意のステージ構成できるようにしたうえで実行することが重要である
これがないと新規のキャラクターや新規のスキルを実装しようとした際に都度開発サーバ上のマスターデータを弄らなくてはならなくなる(そんな面倒なことやってられないでしょ?)
具体的にどんな方法で読み込むかは人それぞれであるが、一般的なのはローカルディレクトリに「インゲーム開始のためのなんらかのデータ」を置いておき、SceneやLevelの実行時に読み込むことでサーバとの通信で持ってきたマスターデータと読み込んだデータでバトルを開始する手法ではないだろうか
UnityならJson、UE4ならCSV辺りがベター
※もちろんこれらの処理を行うためのコードはすべてリリース時には含まれないようにすること
2-1.ゲームデザインやレベルデザインのイテレーションを速く回せるようにする
ゲーム開発の難しいところは一度完成したと思っても容赦なくちゃぶ台がひっくり返るところである
「作ってみたけど思っていたほど面白くない」
「もっと要素を追加したい」
「面白いこと思いついちゃった!」
など日常茶飯事である
ゲームは面白くなくては意味がなく、「面白さ」などという不確かなモノのクオリティを担保する上でゲームデザイナーのトライアンドエラーのイテレーションを速くすることがかなり重要であることは言うまでもない
また、エンジニア的視点でみてもゲームデザイナー側で出来ることを増やすことでエンジニアに過度なタスクが降ってこないようにすることができる(メンテナンスは必須になるが)
2-2.ゲームステージの変更をエディタ上で行えるようにする(可能ならばホットリロード)
1-2で述べた通り、任意のステージを任意の編成で行えるようにしたところで、非エンジニア職に「さあJsonを編集するのだ」などと言ってもそれを実行できるのは極わずかである
JsonやCSVを直接触るのはバグの元なので、理想はそれらの編集エディタを用意してあげるべきだろう
可能であれば、ゲーム実行中にローカルのマスターデータを編集して動作を変更させたりする環境を構築すると楽(仕様変更がたくさんくるであろう開発序盤でこれを実装するのは時期尚早だと思うが考慮はしておいたほうが良い)
以上を踏まえたバトルの開始時の簡単なシーケンス図(慣れてないのでUML的に正しい書き方かは保証しない)
2-2-2.Nodeベースのビジュアルスクリプトを使うことを選択肢に入れよう
エンジニアにありがちなのだが「すべての処理が同じソリューション内のコードで見ることができればそれが一番わかりやすい」といった幻想に取りつかれがちである
エンジニアだけで完結するタスクならばそれは正しいが、ゲームデザイナーやレベルデザイナーと密に連携を取らなくてはならない場合には誤りである
Unityには標準ではないが有名な有料AssetでPlaymakerやArbor、UE4には標準でBluePrintやBehaviorTreeが用意されている
これらはうまく活用すれば非常に強力なToolであり、ゲームを実行しながら値を変更したりNodeを繋ぎ変えたりすることが可能である
そうすることでレベルデザイン班などが自由にエディタ上で試すことが可能になり、イテレーションが高速になる(もちろん学習コストはかかるが)
その際注意しなくてはならないのは、非エンジニアが触るビジュアルスクリプト部分はゲーム処理の高レイヤー部分に限定するように注意して設計する必要がある
例えば、AIのターゲッティング処理にビジュアルスクリプトを採用する場合、「敵AIのターゲット判定の処理」のみを弄ることができるようにPlaymakerなりBehaviorTreeなりを使用しておくべきでターゲッティングを行った次の処理などとは蜜結合にならないようにするべきである(あくまで例でありターゲッティングはビジュアルスクリプトを用いるべきという話ではない)
つまりは非エンジニアがうっかり間違った編集をしてもゲームが止まったり致命的なバグが発生しないような状態にしておく
※便利になる代わりにエンジニアの知らないところで想定していたより複雑且つ大規模なNodeが組まれていたりすることになるので注意
※これらはゲーム性により使うべきかそうでないかは大きく異なってくるので必ず使うべきという話ではない
2-2-3.演出部分はプログラマーではなくても編集して動作確認できるようにする
演出部分は必ず予想外の変更や微調整が入る
プログラマーが演出部分まで担当していた場合によくあるケースだと、当初はアニメーションシーケンスを流すだけだったのが時間がたつにつれて、やれQTE要素入れたいだの特定の条件下では一部のエフェクトを変えたいだのやっぱりやめただのアニメーションの開始時と終了時に特別処理を特定のタイミングの時だけ行ってほしいだの仕様変更が入りコードの保守がとても難しくなる
プログラマが都度対応する必要がある設計になっていると手がいくらあっても足りなくなってしまうのでアーティスト側だけで「編集→動作確認」が完結するように設計する必要がある
UnityならTimeLineなどを使って細かな編集ができるようにしておくべきだし、同様にシーケンサを用いるべきである
また、PlaymakerやBlueprintなどのビジュアルスクリプトと併用して使うことでより広い範囲をアーティスト側だけで実装することが可能になる
演出部分に関してはエンジニア側が行うことは細かな実装ではなく、ビジュアルスクリプトで使う便利な機能をコマンド(Node)単位で作成していく程度にとどめておくのがちょうどよいだろう
以下は演出部分だけをビジュアルスクリプトにした場合のざっくりとしたスクリプト処理イメージ
UnityならPlaymakerやArbor、UE4ならBlueprintなどで脳内保管してほしい
※プログラマが実装するべき青いコマンドをゲームデザイナー側で処理させてしまうとNode全体が煩雑化してしまってバグの元なので注意。モックアップで仮に実装してしまうのもありだが必ずC#乃至C++で書き直すことをお勧めする
このような形にしておけば、ゲームデザイン側でコマンドに渡す引数と条件式の作り方次第で色々なことが試すことができる
3-1.なんでもやるクラスは作らない(singletonは一つまで)
これは別にインゲームに限った話ではないが、ゲーム内のあらゆる処理をなんでも行ってしまうクラスがあるとコードの保守が一気に苦しくなる
そのクラスがsingletonでさらに他にもsingletonがある場合にはもう悲惨である
インゲームは他のシーンと比較して複雑化しやすい傾向にあるため一番最初の設計が特に重要である
3-2.クラスの名前をできる限り具体的にする
そのようなクラスの誕生を防ぐ簡単な方法としてはクラスの名前を具体的にすることである
保守期間がそんなに長くないのにも関わらずスパゲッティ化したコードの多くは曖昧な名前のインゲーム統括クラスが存在するケースが多い
例えば以下のような名前のクラスファイルを見たことはないだろうか
- Battle.cs(cpp)
- Game.cs(cpp)
- BattleManager.cs(cpp)
- GameManager.cs(cpp)
- RPGManager.cs(cpp)
- ...
最初に名言しておくとこれらのクラスの存在意義を否定しているわけではない
おそらくは開発初期ではManagerクラスの上位クラスとして全体の管理や橋渡しだけを行うクラスを想定していたのだと考えられる
しかし、開発も進み関わる人員が増加したり納期に追われたりしていくにつれて知らず知らずのうちにこれらのクラスに具体的な処理が追加されていき、気がついた時には何でもこなす便利クラスが爆誕していたのだろう
例えば「BattleGeneralManager」といった名前にすれば初見で「managerクラスの上位クラス」と明示することができるのでスパゲッティ化する可能性は低くなる(それでもなるときはなるものではある。結局は運用体制とメンバー次第なところは否めない)
同じようにBattleGeneralManagerのメンバで保持する子Managerクラスも具体的な名前をつけていくとでいくらか保守性は高まる
例えばコマンド選択式RPGゲームの場合には具体的には以下のようなイメージである
どれくらいの粒度で具体的に名前をつけるかとかそもそも「~Manager」といったクラス名はつけるなという意見もあるとは思うが、とりあえずは〇〇Managerのような名前では責任が曖昧になりがちなので、もう一単語増やして「〇〇△△Manager」といった名前にするように心がけるだけでだいぶ事故は減るはずである
あとがき
こういう記事を書くと色々なところからマサカリが飛んできそうなので書くべきか悩んだけど書いてみた
同じジャンルのゲームを設計しても人によって設計手法は違うしなにが正解なのかという答えはないものだと思うので、マサカリを投げたくなった人は
「こういうことを気にしている人がいる」
程度でとらえてほしい
でも個人的にはこれらは絶対に抑えておいてほしいしそのほうがみんな幸せになると思っている
そうそうふいつ
【UE4】Unityで慣れたゲームPGがUE4で覚えるべきTips〜その2〜
はじめに
前回の記事(Unityで慣れたゲームPGがUE4で覚えるべきTips)を書いてからだいぶ時間がたったので個人的にまとめておきたいとおもったことをつらつらと書いていく
また、UnityからUE4に移住してきた人やUE4が初めてのゲームエンジンという人も参考になると思います(多分)
間違っていたらご指摘お願いします
本記事の対象になる人
headerファイルで無邪気にIncludeするな
前回の記事で「Includeとはなにかということ」という項目を書きました
開発をしていると実感できますが、むやみにheaderファイルでIncludeしまくってるheaderがあったときにそのheaderをイジってしまうと
「1/325...」「2/325...」「3/325...」
といった具合にVisualStudioのビルドを無心で眺めるという虚無時間が頻発します
前方宣言を使おう
前回の記事で書いたようにIncludeとはコンパイル時にそのheaderをその場所にコピペされることで記述したファイル内で参照できるようにするものです
しかし、header内では基本的には関数や変数の宣言のみで関数の中身はcppファイル内に記述されます
ということはheaderでは外部のクラス型は参照する必要がありますが関数や変数は参照できなくてお構わないということになりますので、その場合は前方宣言を使うべきです
具体的には下記のような感じ
#include "CoreMinimal.h" //#include "Hoge.h" ←Hoge.hをincludeする必要はないのでコメント化 class Hoge;//クラス宣言だけ必要な場合はこれだけで良い class Foo { public: Foo(); ~Foo(); Hoge *hoge;//クラス型として認識される };
どういう仕組かというとC++のコンパイラはコンパイル時にファイル内にひとまず「これはクラスである」という宣言さえあれば実際にリンクさせるのは最後に同名のクラスを探してリンクさせてくれるので前方宣言さえやっていればコンパイルが通り、さらにheaderをincludeしていないのでそのheaderファイルで差分が発生しても変更差分として対象にならないのでコンパイルの虚無時間をへらすことに繋がります
これはとても基本的なことである一方でチームで開発しているとincludeしておけば楽なのでついつい知らずしらずのうちに無駄なincludeが増えてしまうことが多いので注意が必要です
Includeの""と<>
includeするときに下記のようにかかれているのを見ることがよくあると思うのですが、この違いを意識していますでしょうか
#include <stdio.h>//これと #include "Hoge.h"//これの違い
これらの違いはincludeするファイルのディレクトリの検索するスコープの違いです
<>で囲まれたファイルはコンパイラが認識しているディレクトリ(システムで設定されているディレクトリや環境変数で指定されているディレクトリなど)のみから探し出してincludeしますが、""で囲まれたファイルは記述したファイルのカレントディレクトリを検索した後に<>と同じdディレクトリを検索します
知らなくても絶対マズイというものでもない気がしますが、C++ではheaderのinclude順によってエラーがでることがあるので知識として知っておきましょう
検証してないので知りませんが<>で足りるものを""で書いているとビルド速度に影響でるかも(ほんまか?)
Engine.hをIncludeするのをやめろ
色々な記事でEngine.hをincludeしているところが多いのですが、これってビルド時間が長くなる原因になるのでやめましょう
Engine.hをincludeするとEngineが持つ全てのheaderをincludeすることになるのでたしかに書いておけばEngineのクラスや関数やマクロを呼び出すことができてしまうので楽なのですが、これを色々なファイル内で書いているとコンパイル時に毎回巨大なheaderを読み込むことになるので虚無時間が増えます
「とはいってもこのAPIはなにをincludeすればいいのかわからない!」
という人はUE4C++の公式リファレンスの該当するクラスのページを見てもらえばどうincludeすればいいか書いてあります
公式リファレンス
個人的な肌感ですが、たいていの場合はEngine/Engine.hをincludeしていれば事足りるケースが多いです
Includeの順番(IWYUの記述ルール)
C++ではincludeする順番が大事です
むちゃくちゃにincludeしているとふとした時に不可解なエラーが発生します
可読性という観点から見てもめちゃくちゃにincludeしているよりも一定のルールによってincludeされていることが望ましいです
UE4の場合はIWYUの記述ルールに沿うのが最も良いと考えられます
UnityBuildの場合の無名名前空間の罠
UE4にはオプションとしてUnityBuildというものがある
断じて競合ゲームエンジンとは関係ない
UnityBuildについてはこちら-株式会社ヒストリアさまより-
かんたんに言ってしまえばUnityBuildとはビルド時に全てのファイルを一つのファイルにまとめてしまってからビルドしてしまえば早いでしょ?という仕組みです
その場合に問題になってしまうのはグローバルスコープにある変数や関数が同名な場合エラーが起きることです
無名名前空間は用法用量を守って正しく扱いましょう
前回の記事で「無名名前空間」を紹介したがこれを多用しているとこの問題にぶつかる
無名名前空間はそのファイル内のグローバルスコープに宣言されてしまうため、UnityBuildを使うと全てのファイルと同一スコープになってしまうためうっかり同名の関数や変数があるとガンガンエラーが出てしまうので無名名前空間は外部から完全に隠蔽したい変数や関数を扱いたい場合には便利ですが、むやみに使うと問題を引き起こしてしまいます
使う場合は本当に無名名前空間で扱うべきものなのか?を考えてから使いましょう
C++にはリフレクションがない
題名の通りです。ありません。終了。
Unityでエディタ拡張をしようとした場合、エディタ拡張で様々なことがかんたんにできます
しかしそれはC#にリフレクションがあるから機能することです
UE4でもそこらへんはカバーされてはいるのですが、それはUCLASSやUFUNCTIONなどのマクロで宣言されているもののみがリフレクションのように取得できるだけであり、例えば独自に作ったローカルのマスターデータクラスを参照して拡張したエディタから操作したいといったケースではリフレクションがないのでかなり大変です
UE4.22以前はエディタ拡張をしようとするとプラグインで作る必要がありました
確認していないのでわかりませんが、今はEditor Utility Widgetがあるので多少作りやすくなっているかもしれません
あとがき?
三連休で暇だったので書きました
C++で書いていると「C#ではあんなにかんたんにできたことがなぜできないんだ意味わかんねえやはり人類にC++は早すぎた」という気持ちに支配されます全てを支配できた喜びに打ちひしがれるので大変楽しいです
とはいっても純粋なC++よりもUE4C++ではかなり書きやすくなっているようで今後はより扱いやすくなっていくのではないでしょうか
そこらへんよろしくお願いしますepic games様
【UE4】スマートフォン(iOS,Android)のスリープ機能を無効にする
はじめに
スマートフォン向けに開発していると少し重めのDLCをDLさせる必要があったり、長めのムービーを見せたいことがよくあと思います
その場合、スリープされてしまうとDLが中断したりムービーが途中で終わってしまったりして意図しない結果になってしまいますよね
なのでスリープ機能をOFFにしてしまえば解決するのですがUE4だとどうやればいいのかまとまっている記事が見当たらなかったので備忘録として残りしておきます
環境
Windows10
UE4.21.2
実装
- 〇〇.Build.csのPublicDependencyModuleNamesに「ApplicationCore」モジュールを追加
- 任意のcppファイルで「HAL/PlatformApplicationMisc.h」をinclude
- ifdefでiOSとAndroidで処理を分ける
- プラットフォーム別のControlScreensaver関数でONとOFFを切り替える
例
// Fill out your copyright notice in the Description page of Project Settings. #include "MyActor.h" #if PLATFORM_ANDROID || PLATFORM_IOS #include "HAL/PlatformApplicationMisc.h" #endif void AMyActor::SetEnableScreensaver(bool enabled) { #if PLATFORM_ANDROID FAndroidApplicationMisc::ControlScreensaver( enabled ? FAndroidApplicationMisc::EScreenSaverAction::Enable : FAndroidApplicationMisc::EScreenSaverAction::Disable); #elif PLATFORM_IOS FIOSPlatformApplicationMisc::ControlScreensaver( enabled ? FIOSPlatformApplicationMisc::EScreenSaverAction::Enable : FIOSPlatformApplicationMisc::EScreenSaverAction::Disable); #endif }
備考っぽいもの
今回Includeしている「HAL/PlatformApplicationMisc.h」では単純に必要なヘッダーファイルをプラットフォーム別に読み込めるようにifdefで区切っている
なので、どのプラットフォームだろうがこのヘッダーを読み込んでおけばOK
また、ControlScreensaver関数も共通のクラスから呼び出せば大丈夫になっているかと思いきやVerの問題なのかコンパイルではじかれたので素直にプラットフォーム別に処理をわけたほうが間違いないかなと思いました
あとiOSだけっぽいのですが、無効にする処理をBeginPlayで行っても正常動作しなかったのでTickなどで1秒以上delayさせてから実行するようにすると正常動作しました
Anacondaからjupyterを開いてブラウザが立ちあがったがクリックしても動かない場合
環境
WIndows10
python3.6.9
Anaconda3 5.0.0
jupyter Notebook 6.0.0
現象
新しいPCになったのでopenAIgymの環境をセットアップしており、jupyterNotebookをインストールし起動させるとブラウザが立ち上がった
しかし、フォルダをクリックしても新しいフォルダを作成しようとしてもなにも反応がない
なんだったらURLから直接pathをたたいても開かない
なぜだ?となって軽くggってもブラウザが立ち上がらないケースばかりヒットしてブラウザが立ち上がっても動かないケースは解決法が見当たらなかったので備忘録として書き残す
解決法
jupyterをインストールした環境で以下のコマンドを実行
conda install --force html5lib
【UE4】アウトプットログに出力される文字列を取得する
開発環境
UE4.21.2 VisualStudio 2017
手順
- FOutputDeviceを継承するクラスを作成する
- Serialize関数を作成
- 渡された文字列をGLogからデバイス登録をする
- アウトプットログに出力される処理を実行する(ログ、コンソールコマンドなど)
- 出力し終わったら再びGLogからデバイス登録を解除
実際のコード
※専用のUMGを用意するのがめんどくさいので出力方法をImGUIライブラリを使っている.実際に使う際は適当なクラスなり変数なり用意してほしい
#include "ConsoleActor.h" #include <imgui.h> namespace { TArray<FString> strArray; } class CONSOLETEST_API FConsoleDevice : public FOutputDevice//手順1 { public: FConsoleDevice() { } //手順2(consoleに吐き出されるlog情報が1行ごとに渡される) virtual void Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category, const double Time) override { strArray.Insert(Data, 0); } //手順2 virtual void Serialize(const TCHAR* Data, ELogVerbosity::Type Verbosity, const class FName& Category) override { strArray.Insert(Data, 0); } }; void AConsoleActor::OutputStr(const FString& str) { FConsoleDevice device; GLog->AddOutputDevice(&device);//手順3 UE_LOG(LogClass, Log, TEXT("output -> %s"), *str);//手順4 GLog->RemoveOutputDevice(&device);//手順5 } // Called when the game starts or when spawned void AConsoleActor::BeginPlay() { Super::BeginPlay(); OutputStr("AAAAAAAAAAAAAAAAAA"); OutputStr("BBBBBBBBBBBBBBBBBB"); OutputStr("CCCCCCCCCCCCCCCCCC"); } // Called every frame void AConsoleActor::Tick(float DeltaTime) { Super::Tick(DeltaTime); //取得したログのゲーム内への出力方法は各々で考えてください ImGui::NewFrame(); Super::Tick(DeltaTime); //setting ImGui::SetNextWindowPos(ImVec2(50, 20), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(550, 300), ImGuiCond_Always); ImGui::SetNextWindowBgAlpha(0.5f); bool isVisiableCloseBtn = true; //Draw if (ImGui::Begin("Console"), isVisiableCloseBtn) { for (int i = 0; i < strArray.Num(); i++) { ImGui::Text("%s", TCHAR_TO_ANSI(*mBuffer[i])); } } ImGui::End(); }