N煎ログブログ

n番煎じと言っても過言ではない今更な、でも個人的に躓いたUnityやUE4等での開発についての云々を書いていきます

【Unity2018】WebGLで外部ブラウザを新規ウィンドウで開く方法

目次

はじめに

いつからか、UnityC#でjavaScriptの関数を呼び出す「Application.ExternalCall」と「Application.ExternalEval」関数が廃止になりました。

これにより、上記関数を用いたjavaScriptの関数が呼び出せなくなり、別の方法でjavaScript関数を呼び出す必要が出てきました。

私の場合、WebGLでリンクをクリックすると新規ウィンドウでそのリンク先を開いてほしかったのですが、「OpneURL」だと同一ウィンドウ内で開いてしまうし、調べても解決方法が見当たらなかったので、先輩の助言のもと、javaScriptを直接呼び出す方法で実装することにしました。

 

途中の諸々を飛ばしたい方は、目次の「Unity(C#)でjavaScriptの関数を呼ぶ方法」からどうぞ

今まではどう実装できたか?

「Application.External~」が使用できていた時は、

Application.ExternalEval(string.Format("window.open('{0}','_blank')", url));

等といった記述で、新規ウィンドウを開いてリンクを開くことが出来ました。

【参考】

belhb.hateblo.jp

EvalとCallの違いは、リファレンスを読む限り、呼び出す関数に引数を含むか含まないかだと思われます。

docs.unity3d.com

docs.unity3d.com

そもそも引数に記述されている内容の意味は?

Application.ExternalEval(string.Format("window.open('{0}','_blank')", url));

 の引数に、

string.Format("window.open('{0}','_blank')", url)

とあるのですが、「string.Format」は「どのような文字列にするか」を指定できる関数になります。そしてこのプログラムの場合、「{0}」には「url」変数の値が入ります。

他にもいろいろ出来るのですが、詳しくはこちらなどを参考に。

www.sejuku.net

 さて、では「string.Format」に渡された引数をもとに出力した文字列に直すと、

 window.open('url','_blank')

 となります(「url」部分は実際にはURLの文字列が入ります)。

では、この「window.open」とは何なのでしょうか。

「window.open」って?

ずばりこれがjavaScriptです!

「Application.ExternalEval」はこの関数を呼び出していたのです!

具体的には、関数名通りウィンドウを開く関数になります。

引数には、「URL」「開くウィンドウの名前」「ウィンドウオプション」が指定できます。

developer.mozilla.org

プロパティについてはこの記事では省略しますが、こちらのサイトに情報が載っています。

amenti.usamimi.info

この時、open関数の第二引数に

nullもしくは_blank

を渡すと、新規ウィンドウが生成されます!

つまりまとめると、

 window.open('url','_blank')

 は「新規ウィンドウでURLのリンク先を開いてね」という関数だということです。

じゃあ、このプログラムをUnityC#で呼べれば新規ウィンドウを開くことが出来ますね!

Unity(C#)でjavaScriptの関数を呼ぶ方法

この方法に至っては、リファレンスの方で誘導がなされていました。

docs.unity3d.com

このページに記述されているURLに飛ぶと、

docs.unity3d.com

このリファレンスに飛びます。

内容をまとめると、

  1. Plugins サブフォルダーの下に .jslib拡張子のファイルを作成
  2. プラグインファイルの中に構文(プログラム)を記述
  3. C#で呼び出す

になります。 

この「2.」で書くプログラムで「window.open」関数を呼び出せば出来そうですね!

書いてみる

まずは、jslibの方を記述していきます。 

※jslibは必ず「Plugins」フォルダ内に作成しましょう!

gist.github.com

書き方はリファレンスを参考に、

独自に追加したのは

OpenToBlankWindow: function (_url) {
    window.open(Pointer_stringify(_url),'_blank')
}, 

 です。

Pointer_stringify

は受け取った文字列をjavaScript文字列に変換します。

次に呼び出すC#側を記述していきます。

gist.github.com

[DllImport("__Internal")]

は、「この関数は外で定義されていますよ」と知らせるものです。

 これで、C#javaScriptを呼び出すことが出来るようになり、window.openを呼び出して新規ウィンドウでリンクを開くことが出来るようになりました。

まとめ

javaScriptC#で直接呼び出してリンクを新規ウィンドウで開けるようになった!

【Unity2017】フェードイン / アウトをUI含めて1スクリプトで制御する

目次

はじめに

フェードイン / アウトはゲームを制作していく過程で導入するのは少なくないと思います。

しかしゲームを制作するたびにこれらの処理やフラグ管理などを作るのは流石に面倒ですし使いまわすにしてもUI周りで扱いづらくなりそうだなと感じました。

そのため、UI周りやフラグも含めて全ての処理を1スクリプトで制御するスクリプトを制作しました。

以下がそのスクリプトです。

delegate, Action共に初めて使うので2つ実装してみました。

 

[追記(2018/03/13)]

  • 一部変数を削除しました。
  • スクリプトをシングルトンにしました。
  • オブジェクトにアタッチしなくても呼び出すだけで自動でUIだけでなくオブジェクト周りも生成するようになりました。

[追記(2018/04/20)]

  • static変数がメンバ変数コード規約「m_」になっていたのを「s_」に修正しました。
  • よくよく見直してみたらプロパティ内で色々生成なりAddComponentなりしちゃってるので、修正案ができ次第修正します。
  • 修正しました(2018/4/21)。が、なんだかややこしいコードになってしまい可読性や管理性がやや低下してしまっているので引き続き代替案が出次第更新します。

スクリプト(delegate版)

gist.github.com

スクリプト(Action版)

gist.github.com

スクリプト(修正版)

github.com

使い方

任意のオブジェクトにスクリプトを貼っつけて、

[追記(2018/03/13)]

  • 不要になりました

[追記(2018/04/22)]

  • アセット処理の初回実行時の前に「CreateInstance」を呼ばなければならないようになりました。
  1. FadeController.CreateInstance(this)でインスタンスを生成しよう(1度だけでOK)
  2. FadeController.Instanceで取得しましょう
  3. FadeIn, Out(フェード時間)で動作します。
  4. FadeIn, Out(フェード時間, 終了後に呼び出す関数)でフェード後に任意の関数を呼び出すことが出来ます。
  5. IsCompleteプロパティからでも終了フラグを取得することが出来ます。
  6. SetColor(色)でフェード色を変えることも出来ます。
  7. 必要なくなったらFadeController.DestroyInstance()で削除しよう

AssetStoreにリリースしました!

AssetStoreに申請し、無事リリースされました!

是非AssetStoreページの方でご評価、コメント等よろしくおねがいします!

【UE4】C++でExecuteConsoleCommand実行関数ライブラリBP作ってみた

目次

C++で書こうと思ったきっかけ

 つまるところ↑ツイートの通りなのですが、おおまかな処理等を構築する時にはBPは非常に有効です。しかし計算やループ(forではなく値の)処理を構築する場合はBPだと時間がかかる上に、見た目ブロック状のものが画面いっぱいになりそれらを繋ぐ線で埋まるので管理が難しくなってしまいます。

そして今回の場合はExecuteConsoleCommandをC++で実行させたいと思いました。

というのも、ExecuteConsoleCommandでゲームのクオリティ設定を行いたかったのですが、設定分ExecuteConsoleCommandノードがどんどん伸びていき、線も増えていき、コマンドの管理も時間がかかり始めていた、という背景があります。

というわけで書いてみました。ちなみにUE4でのC++は初です。

C++(UE4)は友達! 怖くないよ!

ガッ

f:id:isemito:20171215225635j:plain

というわけでいきなり噛みつかれました。

まず、当たり前といえば当たり前ですが初見でUE4での書き方が全くわからない。

とりあえず関数ライブラリを作り、Blueprint Function Libraryを親としてC++ファイルを作成

f:id:isemito:20171215230104p:plain

開いてみると...

[SystemFunctionLibrary.h] 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "SystemFunctionLibrary.generated.h"

/**
 * 
 */
UCLASS()
class TIMEROBBER_API USystemFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()




};

 

[CalcFunctionLibrary.cpp]

// Fill out your copyright notice in the Description page of Project Settings.

#include "SystemFunctionLibrary.h"

 

ギリギリギリ...

f:id:isemito:20171215225635j:plain

f:id:isemito:20171215233555j:plain

色々調べてみてこちらの記事が見つかりましたので参考に進めていきます。

unrealengine.hatenablog.com

staticでselfピンを無くす

 静的になるので当然ですが、staticにするとselfピンがなくなり、オブジェクトを渡す必要がなくなります。

例えば、

UFUNCTION(BlueprintCallable, Category = "System")
void TestFunction(int _test);

こう記述すれば...

f:id:isemito:20171215234043p:plain

◯- ターゲット[self]」というピンが表示されますが、

UFUNCTION(BlueprintCallable, Category = "System")
static void TestFunction(int _test);

こう記述すれば、

f:id:isemito:20171215234354p:plain

 selfピンが消えます。

おっじゃあstatic付けてExecuteConsoleCommand実行するC++コードを書けば良いんだなと思い検索かけてみた所、 

forums.unrealengine.com

 このポストにて

#include "Engine/World.h"
GetWorld()->Exec(GetWorld(), TEXT("MyAwesomeConsoleCommand X Y Z"));

 こう記述すれば実行できるよ! という情報がありました。

これでC++で ExecuteConsoleCommandを実行しましょう。

 

怖くない...怖くない...

f:id:isemito:20171215235043j:plain

ガッ

f:id:isemito:20171215225635j:plain

まだ駄目です。 

なぜなら、

f:id:isemito:20171215235428p:plain

 「GetWorld()」はconst修飾であり、sttaic関数内で使用できません。

「 じゃあconst付ければ使える..?」

と思いきやそうするともちろんstaticは外さなければならず故にselfピンが表示されてしまいます。

ExecuteConsoleCommandはどこにある?

ここで、そもそもExecuteConsoleCommandはどのソースコードにあるのか?

それを参考に書けば良いのではないか?

自力で探してみたものの見つからなかったのでAnswerHubにて質問した所、

ExecuteConsoleCommandと同等の機能をC++で実装したい - UE4 AnswerHub

#include "Kismet/KismetSystemLibrary.h"
void ExecuteConsoleCommand(UObject* WorldContextObject, const FString& Command, APlayerController* Player)

「KismetSystemLibrary.h」にありました。

 

...あれ?

 

f:id:isemito:20171216000220p:plain

書いてあったああああああああああ!!!!

なるほど。

ともあれ、これでExecuteConsoleCommandを直接呼び出すことが可能になりました。

WorldContextObjectとは

ExecuteConsoleCommandを直接呼び出す方法はわかりましたが、今度は見覚えのない言葉が出てきました。

f:id:isemito:20171216000606p:plain

WorldContextObjectとは一体?

まず、WorldContextObjectの型はUObjectであり、これはUE4内全てのオブジェクトの親クラスになるものです。そして、

[C++] WorldContextObject Parameter - UE4 AnswerHub

にて回答者様がおっしゃっているとおり、WorldContectObjectはつまり「GetWorld()」を持つオブジェクトを指す。つまりWorldContectObject(UObject)経由で「GetWorld()」を呼び出しているということになるようです。

そしてExecuteConsoleCommandは渡されたWorldContextObjectからPlayerControllerを取得し、ConsoleCommandを実行しています。

[ExecuteConsoleCommand関数 中身]

void UKismetSystemLibrary::ExecuteConsoleCommand(UObject* WorldContextObject, const FString& Command, APlayerController* Player)
{
    // First, try routing through the primary player
    APlayerController* TargetPC = Player ? Player : UGameplayStatics::GetPlayerController(WorldContextObject, 0);
    if( TargetPC )
    {
        TargetPC->ConsoleCommand(Command, true);
    }
}

メタデータ指定子

では、肝心のWorldContextObjectにはどうやって値を渡せば良いのか?

それは、「メタデータ指定子(meta)」と呼ばれるものを使用します。

docs.unrealengine.com

 上サイト内の「関数メタデータ指定子」に

WorldContext="Parameter"

BlueprintCallable 関数によって使用され、オペレーションが起こるワールドをどのパラメータが決めるかを示します。

 という説明があります。

つまりこの場合「どのパラメータが決めるか」というのは「WorldContextObject」であり、故に「"Parameter"」の箇所には「WorldContextObject」を割り当てます。そうすることで、オペレーションが起こるワールドはWorldContextObjectが持つことを示すことになるのかもしれません(勉強不足でまだ完全には理解できていません)。

出来た!

ともあれ、これでstaticしつつExecuteConsoleCommandを呼び出すことが出来るようになりました。

最終的なコードは以下になります。

最初に述べたとおりゲームのクオリティ設定が元々の目的だったのでクオリティ設定のコードになっています。

[SystemFunctionLibrary.h]

UCLASS()
class TIMEROBBER_API USystemFunctionLibrary : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()
    //クオリティ設定反映
    UFUNCTION(BlueprintCallable, Category = "System", meta = (WorldContext = "_worldContextObject"))
    static void ApplyGameQuality(
        UObject* _worldContextObject,
        int _resolutionScale,
        float _viewDistance,
        int _antiAlias,
        int _postProcessQuality,
        int _shadowQuality,
        int _textureQuality,
        int _effectQuality
    );
};

[SystemFunctionLibrary.cpp]

void USystemFunctionLibrary::ApplyGameQuality(
    UObject* _worldContextObject,
    int _resolutionScale,
    float _viewDistance,
    int _antiAlias,
    int _postProcessQuality,
    int _shadowQuality,
    int _textureQuality,
    int _effectQuality) {
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.ResolutionQuality " + FString::FromInt(_resolutionScale));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.ViewDistanceQuality " + FString::SanitizeFloat(_viewDistance));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.AntialiasingQuality " + FString::FromInt(_antiAlias));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.PostProcessQuality " + FString::FromInt(_postProcessQuality));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.ShadowQuality " + FString::FromInt(_shadowQuality));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.TextureQuality " + FString::FromInt(_textureQuality));
    UKismetSystemLibrary::ExecuteConsoleCommand(_worldContextObject, "sg.EffectsQuality " + FString::FromInt(_effectQuality));
}

【UE4】4.18以降での被破壊メッシュの作成方法

目次

4.18から仕様が少し変わったようです

「被破壊メッシュ作りたいなー」と思い、メッシュを右クリックして「被破壊メッシュを作成」を探していた所...

 

...ない

 

無いんです。どこにも。そこで検索かけてみました。しかし...

こことか

物理-Destructible Mesh - UE4 Document@com04

このサイトを見ても、右クリックから生成していました。

pafuhana1213.hatenablog.com

そこで、ご存知UE4AnswerHubにて質問してみた所...

4.18からプラグインになった

被破壊メッシュを作ることが出来ない - UE4 AnswerHub

回答者様の情報によると、どうやら4.18から被破壊メッシュは「ApexDestructablePlugin」なるプラグインとなり、デフォルトでOFFになっているそうです。

 

そこで、エディタの「編集 > Plugins」にて検索かけてみた所...

f:id:isemito:20171215195605p:plain

ありました。これです。

これにチェックを入れると従来通り非破壊メッシュを右クリックから生成することが出来るようになりました!

まとめ

非破壊メッシュは4.18から「ApexDestructablePluginプラグイン化。

エディタの「編集 > Plugins」にて「APex」と検索で出て来るのでチェックを入れる。

で使用可能。

【UE4】大量のフラグが存在する場合の管理方法

目次

大量のフラグ変数は死の予兆

『フラグ』:ゲーム制作には無くてはならない存在

ですよね?

しかしゲームの規模が大きくなるに従って使用したいフラグも増えていき、管理する量が多くなっていき...

こんなことに...(ヒィッ

f:id:isemito:20171215193113p:plain

まだ少ないので良い(良くない)ですが、これが20,30と仮に増えていった場合のことは考えたくありません...。

UE4AnswerHub神

UE4ならではの解決策がないか質問してみました。

大量のフラグが存在する場合の管理方法 - UE4 AnswerHub

『Set』!? なんぞ!!!

回答者様に教えていただいた以下サイトによると、

docs.unrealengine.com

というものがあり、

データの中に何があって何がないかというのを管理するために使う物

だそうです。

Addで追加。Unionで合成。Contains Itemで存在確認。 等々なかなか便利そうです。

要は、

ある型の複数の値を1つの変数として扱う物

ざっくりしたイメージでこんな感じだと思います。

使ってみました

実際に使用してみた所(enum値を用意して使用しています)、

f:id:isemito:20171215194001p:plain

 ア...アァ...(神を拝む姿勢

これ↓が、

f:id:isemito:20171215193113p:plain

こう↓

f:id:isemito:20171215194001p:plain

すっごいスッキリ!!

しかもアニメーション遷移の情報を今まで取ってた(依存関係強すぎ)のが不要になったおかげで依存関係を弱くすることが出来ました。

皆も使おう「Set」!

ちなみに

UE4にはBitmaskという仕組みがあり、Bit演算でフラグ管理することも出来ます。

今回私はまだ使用したことなかったSetを用いました。

Bit演算に関しては過去記事にて公開していますので参考までに。

isemito.hatenablog.com

まとめ

「Set」と呼ばれる変数で一括管理できる。

余談

UE4 Set」と検索しても出てこないので苦労する

【UE4】PlayerController削除の仕方と注意すべきこと

目次

削除したくなった理由

マルチプレイ対応ゲームを実際に制作してみて、「CreatePlayer」にて生成したプレイヤーをあの世に葬り去りたいという欲が生まれてきました。

調べても情報が出てこなかったので質問してみました。

PlayerControllerを削除したい - UE4 AnswerHub

上記回答によると「RemovePlayer」を使用することでプレイヤーをコントローラ毎削除することが出来るようです。

生成したPlayerControllerは確保しろ!

しかし、単にRemovePlayerを使用しても、何故かPlayerControllerが削除されないという事案が発生しました。

「取得に時間がかかっているのか?」「-1指定でCreatePlayerしているのが悪いのか?」等の方法を試しても解決に至らなかったので、再び質問してみた所...

RemovePlayerでPlayerControllerが削除されない - UE4 AnswerHub

回答によると、

PlayerControllerのNetRoleが"ROLE_Authority"が以外だった場合はPlayerControllerが削除されない可能性があります。

別レベルで作成されたPlayerControllerはNetRoleが違うものが設定されている可能性もあります。特にOpenLevelでパーシスタントレベルを変更した際にはご注意ください。

という事で、 どうやらPlayerControllerの持つ情報により取得が意図する状態で行えていないことが原因のようでした。

しかし!

回答者様の検証BPでも行っていますが、CreatePlayerで生成したPlayerControllerを配列内に格納し、RemovePlayer時に取り出しています。

私も同じように予め配列内に確保しておき、RemovePlayerをしてみた所、何の問題もなく正常に全てのPlayerControllerを削除することが出来ました!

つまり、

「GetPlayerControllerで取得できないのなら初めから確保しておけば無問題」

ということでした。

 よくよく考えてみれば、後で削除などの操作を行うものを逐一再取得とか非効率といいますか、無駄と言いますか。初めから手元に持っておけばよかったんですね。

まとめ

CreatePlayerで生成したPlayerControllerは確保しておき、RemovePlayerで削除しよう!

【UE4】スキル使用時等の複数のアニメーションを単一のステートで管理する方法

目次

はじめに

UE4にはアニメーションをステートマシンで遷移させる仕組みがありますが、この機能をそのまま使用して複数のアニメーションを管理しようとした場合、あっちこっちに遷移の線が伸びてしまって管理しにくくなります。

f:id:isemito:20171215175351p:plain

複数アニメーションを使用する場面は多々ありますが、これらをたった1つのステートで管理できる方法があります。

以下ではgyazoによる動画を貼っていますがデフォルトでループ再生にならないため、右クリックから「コントロールを表示」で手動で再び再生させる必要があります(ループにチェックを入れればなお良し)。

アニメーションシーケンスに渡すアニメーションを変数化

通常、ステート内にアニメーションステートを追加する場合、以下のようにアニメーションアセットをD&Dして追加すると思いますが(検索かけたり等とか)、

gyazo.com

この時、ステート内の「再生◯◯」というノードに渡すアニメーションは変数化することが出来ます。

その方法は、まずステート内に移動し、「再生◯◯」ノードの詳細にある「(As pin) Sequence」にチェックを入れるだけです。

gyazo.com

こうすることで再生させるアニメーションを直接指定させることが出来るようになりました。

あとはこのピンから線を伸ばして変数を作成します。

gyazo.com

後は、プレイヤー等の外部から「何のアニメーションを再生させるか」を引数として持つ関数を作成して呼び出せるようにしておけば、ステートを複数作成しなくても単一のステートアニメーションとして動作させることが出来ます(型はAnimSequenceBase)。

f:id:isemito:20171215182209p:plain

どう使うか

私の場合は、事前にデータベース化しておいた各Perk(スキル)情報にPerk使用時に再生させたいアニメーションを格納しておき、使用時にアニメーションBPに情報を渡す、という手法をとっています。

f:id:isemito:20171215183833p:plain

f:id:isemito:20171215183846p:plain

これを、解釈が間違っていなければ「アフォーダンス指向」と呼ぶそうです。

以下のサイトの情報を参考にしました。

www.4gamer.net

まとめ

以下のような複数のアニメーションを...

f:id:isemito:20171215175351p:plain

アフォーダンス指向でこんなにスッキリさせることが出来た!

f:id:isemito:20171215184223p:plain