UnrealLIFE!

UE4やプログラミング言語に関するブログです。

Nim言語入門(インストール)

追記

新しいNimのバージョンに合わせた記事を別ブログに書きました。 nim-memo.hatenablog.com

本文

最近、新しく使っていく言語を決めようと思って色々試している中で、今のところ一番良さそうなNimという言語の簡単な導入記事です。
Nim、とてもいい言語なのですが、日本では知名度が低いみたいなので記事も少ないです。なので、入門記事でも役に立つと思って書いてみました。

一応簡単なNimのサンプルを公式から引用させていただきます。 (引用元: http://nim-lang.org/ )

type Person = object
  name: string
  age: int

proc greet(p: Person) =
  echo "Hi, I'm ", p.name, "."
  echo "I am ", p.age, " years old."

let p = Person(name:"Jon", age:18)
p.greet() # or greet(p)

見たところPython風構文のOCamlって感じですね。

導入

Linux、特にUbuntu系を想定します。(筆者の環境はLubuntuです)
UbuntuリポジトリにはNimのパッケージがあるのですが、Nimで使うnimbleというパッケージマネージャー&ビルドツールがインストールされないみたいなので、ソースからビルドします。
必要なツールは、

だけです。
あとはシェルで

cd ~/
git clone https://github.com/nim-lang/Nim.git
cd Nim
git clone --depth 1 https://github.com/nim-lang/csources
cd csources && sh build.sh
export PATH=~/Nim/bin:$PATH
cd ..
nim e install_nimble.nims

とやればインストールできます。
あとは~/.profileあたりにexport PATH=~/Nim/bin:$PATH を追記してパスを通しておけばいいでしょう。
これで nim や nimble コマンドが使えるようになってるはずです。

nimbleを使ったプロジェクト

nimbleはパッケージのインストールだけでなく、パッケージのビルドにも使えるシステムです。(RustでいうCargoのようなものです)
なので、試しにnimbleを使ったプロジェクトを作ってみます。
最初にプロジェクト用のフォルダを作り、 mkdir nimble_test
nimble用のファイルをコマンドで作ります。 nimble init (対話処理で進みますが入力必須以外は大体デフォルトでいいと思います)
そこでできた*.nimbleに

srcDir = "src"
binDir = "bin"
bin = @["main"]

を追記します。
余談ですが、nimbleで使う設定ファイルはNimScriptというNimのサブセットになっており、DSLで設定が書けるようになっています。ここはRubyのRakeに似ていますね。(構文はPython風ですが)

あとはsrcディレクトリとbinディレクトリ、src/main.nimファイルを作り、src/main.nimに

echo "Hello World!" # echo("Hello World!")でもいい

を書いてからnimble buildでHelloWorldプログラムが生成されます。
生成されたbin/mainプログラムはネイティブバイナリなのでそのまま実行できます。

所感

NimはどうやらメタプログラミングDSLをかなり重視しているようで、TemplateやMacro(AST操作可能)などの言語機能や、NimScriptなどのDSL向きのサブセットなどがあります。(RubySinatraっぽいルーティングDSLもありました)
マクロやDSLがNimの構文によく馴染む形になるのもプラスポイントです。
個人的にはRubyDSLLispなどのマクロは大好きなので、Nimの方向性はかなりいいと思います。速度がC並みに速いのもいいですね。
ソフトリアルタイムなども想定しているようなので、これでゲームエンジンなど作ってみたいですね。
引き続きNimを色々試していこうと思います。

以上、Nim言語導入でした。

UE4用mrubyプラグインを作ってみる。

UE4AdventCalender其ノ弐の11日目の記事です。

小ネタのつもりで書いたらめっちゃ長くなっちゃいました。

今回の記事は、UE4でmrubyを使えるようにするプラグインを一通り作ってみたという記事です。
UE4ではブループリントとC++の2つの言語を使ってプログラミングをしていきますが、ブループリントはビジュアルプログラミング言語で、C++は低レイヤーのプログラミング言語なので、その中間のテキストのスクリプト言語が無いかなぁと思ったので、即興で作ってみました。(正直言ってしまうとLispにしようかとも思ったんですが、ブループリントでC++な皆さんに全力で逃げられそうだったので断念しました)

先に断っておきますと、ブループリントやC++の置き換えではなく、設定ファイルとして利用するという一発ネタに近い感じの記事です。完全なスクリプトとして実装するためにはAPIリファレンス読み込まなければいけないのでめんどくさかった。 ただ、Ruby自体はVagrantやChef、Metasploit Frameworkなどのソフトウェアで設定ファイルや拡張言語として利用されているので、しっかり実装すれば有用なものになると思います。

今回の主な目的は、組み込み向けRubyの実装であるmrubyをUE4に組み込んで設定ファイルとして利用するというものです。
当初はプラグインの利用についてのみの解説だったのですが、少し有用性に欠けていたと思ったので、UE4にCやC++で書かれた外部ライブラリ(今回は.lib形式の静的ライブラリ)を組み込んでみるという記事にしました。
その際に必要となるCとUnrealC++の連携やライブラリのリンクなどを主に解説して、mrubyの細かい部分は省きながらいきたいと思います。(mrubyに関しては後日別記事を書くかもしれません)
一応実装の全てを見れるようにgithubにコードを上げています。詳細はそちらをご確認ください。
https://github.com/hsssnow23/MRubySetting

UE4のバージョンは4.10を、mrubyは1.2.0を使っていきます。

mrubyのビルド

mrubyはバイナリが配布されておらず、自力でgithubからソースコードを取得してコンパイルする必要があります。
ここでの注意は、UE4のエディタは64bit版しかないので基本的に64bit版を使用するようにすることです。 32bit版に切り替える際も自動で行えるようにしています。(たぶん) 今回は64bitのみをビルドして使います。
ちなみにUE4とmrubyでVisualStudioのバージョンをあわせないとシンボル未解決のエラーが出たりするのでそれも注意ですね。(3時間ぐらいハマった)

今回使った依存ソフトウェア

mrubyをgithubからダウンロードして展開したフォルダ中にbuild_config.rbがあるので、8行目付近の

toolchain :gcc

toolchain :visualcpp

に書き直し、 "VS2015 x64 NativeTools コマンドプロンプト"をスタートメニューから起動し、

cd mruby-[version]/
ruby ./minirake

を実行します。
すると、"mruby-[version]/build/host/"の中にlibmruby.libができます。
このlibmruby.libとmrubyのルートディレクトリにあるincludeディレクトリを、UE4用のC++プラグインを実装するモジュールのフォルダのThirdParty/MRubyディレクトリに、libmruby.libはLibraries/libmruby.x64.lib、includeディレクトリはIncludesにリネームして置きます。

これでmrubyの準備は完了です

mrubyをUE4のモジュールにリンクする

.libのリンクにはモジュールの設定ファイルである*.Build.csを改変する必要があります。 追記したコードを載せます。

public class MRubySetting : ModuleRules
{
    private string ModulePath
    {
        get { return Path.GetDirectoryName(RulesCompiler.GetModuleFilename(this.GetType().Name)); }
    }

    private string ThirdPartyPath
    {
        get { return Path.GetFullPath(Path.Combine(ModulePath, "./ThirdParty/")); }
    }

    public MRubySetting(TargetInfo Target)
    {
        // 省略

        LoadMRubyLIB(Target);
    }

    public bool LoadMRubyLIB(TargetInfo Target)
    {
        bool isLibrarySupported = false;

        if ((Target.Platform == UnrealTargetPlatform.Win64) || (Target.Platform == UnrealTargetPlatform.Win32))
        {
            isLibrarySupported = true;

            string PlatformString = (Target.Platform == UnrealTargetPlatform.Win64) ? "x64" : "x86";
            string LibrariesPath = Path.Combine(ThirdPartyPath, "MRuby", "Libraries");

            PublicAdditionalLibraries.Add(Path.Combine(LibrariesPath, "libmruby." + PlatformString + ".lib"));
        }

        if (isLibrarySupported)
        {
            PublicIncludePaths.Add(Path.Combine(ThirdPartyPath, "MRuby", "Includes"));
        }

        Definitions.Add(string.Format("WITH_MRUBY_BINDING={0}", isLibrarySupported ? 1 : 0));

        return isLibrarySupported;
    }

モジュールパスとサードパーティパスの取得関数を定義し、それらを利用してlibmruby.x64.libをリンクする関数を定義して、それをコンストラクタで呼び出しています。
重要なのは、PublicAdditionalLibraries.AddとPublicIncludePaths.Addです。名前の通り.libファイルのリンクとインクルードパスの追加を担当します。 地味に肝なのが、string PlatformStringで32bitと64bitの判別をし、ビルド時にリンクするライブラリを切り替えている部分です。こうすることにより32bitでも64bitでも動くアプリケーションをUE4と連携して作ることができます。

実装

実装についてはかなりざっくりいきます。 主にヘッダファイルを載せます。

まずmrubyのヘッダファイルをインクルードし、(C言語なので念の為にextern "C" {}で囲む)
MRubySettingBPL.h

extern "C" {
   #include "mruby.h"
   #include "mruby/variable.h"
   #include "mruby/hash.h"
   #include "mruby/compile.h"
   #include "mruby/string.h"
}

mrubyファイルをインポートできるようにアセットを実装し、
MRubySettingBPL.h

UCLASS()
class UMRubyAsset : public UObject
{
    GENERATED_UCLASS_BODY()

public:
    UPROPERTY(EditAnywhere)
    FString Source;
};

UCLASS()
class UMRubyAssetFactory : public UFactory
{
    GENERATED_UCLASS_BODY()

    virtual bool DoesSupportClass(UClass* Class) override;
    virtual UClass* ResolveSupportedClass() override;
    virtual UObject* FactoryCreateText(
        UClass* InClass,
        UObject* InParent,
        FName InName,
        EObjectFlags Flags,
        UObject* Context,
        const TCHAR* Type,
        const TCHAR*& Buffer,
        const TCHAR* BufferEnd,
        FFeedbackContext* Warn
        ) override;
};

設定ファイルを表現するクラスとmrubyの値のラッパーの実装をして完成です!
MRubySettingBPL.h

USTRUCT()
struct FMRubyState {
    GENERATED_USTRUCT_BODY()

public:
    mrb_state* MRB;

    FMRubyState();
    ~FMRubyState();
};

UCLASS(Blueprintable)
class UMRubyValue : public UObject
{
    GENERATED_BODY()

public:
    mrb_value Value;

    UFUNCTION(BlueprintPure, Category = "MRuby")
    bool ToBool();
    UFUNCTION(BlueprintPure, Category = "MRuby")
    int32 ToInt();
    UFUNCTION(BlueprintPure, Category = "MRuby")
    float ToFloat();
    UFUNCTION(BlueprintPure, Category = "MRuby")
    FString ToString();
};


UCLASS(Blueprintable)
class UMRubySetting : public UObject
{
    GENERATED_UCLASS_BODY()

public:
    TSharedPtr<FMRubyState> MRubyState;

    UFUNCTION(BlueprintCallable, Category = "MRuby")
    void SetConfigString(FString Key, FString Val);

    UFUNCTION(BlueprintCallable, Category = "MRuby")
    UMRubyValue* GetConfig(FString Key);

    UFUNCTION(BlueprintCallable, Category = "MRuby")
    bool ExecuteSource(UMRubyAsset* MRubyAsset);
};

雑すぎんだろ!( ‘д‘⊂彡☆))Д´) パーン

というだけではあんまりなので、.cppの方でよく使った関数やマクロについて少し書こうと思います。
こういったC言語C++のライブラリは独自の文字列型や値型を持っていることが多いので、そこの連携を調べるのが少し面倒かと思います。
mrubyは値をmrb_valueという型で持っており、Cの値に変換する関数がありますが、一番めんどうなのが文字列です。
まずmrubyの文字列をUE4の文字列に変換するにはmrb_value->char*->TCHAR*->FStringというステップを踏みます。
mrb_valueからcharを取り出すにはRSTRING_PTRを使いました。(が、どうにもこれを使うのはまずいらしいのですがとりあえず簡単なこれでいきます) charをTCHAR*に変換するのにANSI_TO_TCHARを使い、あとはFStringのコンストラクタに渡すだけです。

FString(ANSI_TO_TCHAR(RSTRING_PTR(Value))); // こんな感じです。

では逆にUE4の文字列をmrubyに渡す方法は、単に逆のプロセスを踏むだけです。 FStringは*を付けることによって内部のTCHAR*文字列が取れるので、ANSI_TO_TCHARの逆のTCHAR_TO_ANSIでchar*に変換したらmrubyに渡せます。

TCHAR_TO_ANSI(*Str);

使ってみる

ではこのプラグインの使用例を載せてみます。 例えばゲームらしくドラゴンの設定ファイルを題材にすると、
blueprint f:id:shsnow23:20151211000212p:plain unreal_dragon.rb

config do |info|
    hp = 100
    if info["difficulty"] == "normal"
        hp = 200
    end
    {
        "name" => "Salamander",
        "hp" => hp,
        "mp" => 50
    }
end

これを実行すると、画面に
Salamander
200
50
と表示されます。
Rubyを設定ファイルとして利用する場合、情報を与えて設定ファイル側でその情報を元に分岐ができたりするのが強みです。(ここではhpを難易度によって変えています)

というようなネタでした。

お疲れ様でした。

明日はdabura6さんの「TobiiEyeXとUe4」です!よろしくお願いします!

Blenderのマスク変形アドオンを作りました。

今回はBlenderの記事です。 UE4はそれだけではゲーム開発を完結させることはできません。UE4はあくまでゲームエンジンなので3Dモデル自体を作る機能は無いからですね。(簡単なものならできますが)
なので、Blenderを使ってスカルプトモデリングを練習していたのですが、マスク部分を変形させる機能が見当たらなかったんですよね。(見落としているだけかもしれません)
ということでタイトルにあるとおり自作しました。 GithubにてMITLicenseで公開しています。

github.com

インストールは、GithubからZIPでダウンロードして、
ユーザー設定 -> アドオン -> ファイルからインストール でダウンロードしたZIPの中のBlenderDeformTools.pyを指定すればインストールできます。

とてもシンプルなアドオンです。わずか135行しかありません。 動作としてはマスクをVertexGroupに変換してラティスを生成&設定しています。

使い方はGIFを見てもらうのが一番はやいと思います。

BlenderDeformToolsGuide

"ツールバー -> スカルプト -> DeformTools"に全ての機能がまとめてあります。 変形をさせたい場合は、変形させたい部分をマスクしてから"Create DeformBox"ボタンをクリックすれば自動でラティスが生成されます。 VertexGroup化だけをしたい場合もあると思うので(マテリアル設定時など)、そういった場合は"Mask to VertexGroup"ボタンを使いましょう。

注意点としては、生成されたラティスの端の辺を変形させないということです。 端の辺を変形させると、変形がなめらかに行われず、ギザギザになったりするからです。 対策は、変形させたい部分のマスクを大きめにして、内側の頂点を変形させることです。こうすればなめらかに変形ができると思います。 ラティスの分割数を増やすのもいいと思います。

FIX:  
ギザギザになるのは、ラティスの端を操作したからではなく、変形させる形状によるということがわかりました。  
なので、ギザギザになった場合は手直しするか、スムースモディファイアをかけるなりして対処してください。  
訂正しておきます。

さぁ、みんなでBlender沼にはまっていきましょう!(

C++でBlueprintから使えるInterfaceを作る

FIX: ソースコードのclass宣言で末尾にセミコロンが抜けてたので修正。
FIX: TArrayの型パラメータが特殊記法と判断されて隠れてたので修正。
FIX: はてブロの色んな記法を試してる途中で記事を非公開にしてしまっていたので修正。
FIX: 上の記法を間違えて記事にしていたので修正。

初っ端からUnrealC++の記事です。我ながら大胆。

タイトル通り、C++でBlueprintから使えるInterfaceに関する記事です。

C++でBlueprintから使えるInterfaceを作る際に、日本語情報は見当たらず、英語情報も少なめで苦労したので記事にしました。ただし、コンパイルエラーが出まくって、エラーメッセージを頼りに実装したので、マクロなどの動作までは説明できないので悪しからず。

実装:

まずはInterfaceを書きます。 漠然と書くのは厳しいので、Debug用の架空のInterfaceを想定します。

// IDebugPrintable.h

class IDebugPrintable
{
    GENERATED_IINTERFACE_BODY()
    
public:
    UFUNCTION(BlueprintImplementableEvent)
    void DebugPrint(UDebugData* DebugData, UDebugStream DebugStream);
};

インターフェースクラス名の頭にIを付けるのはC++プログラマの方にはおなじみかと思います。

GENERATED_IINTERFACE_BODY()でInterface用の情報を生成しているのはUnrealC++のGENERATED_BODY()やGENERATED_UCLASS_BODY()に似ていますね。ここは特に弄る必要は無いでしょう。

重要なのは次のUFUNCTIONとその対象の関数、そしてその関数の引数です。 UFUNCTIONではBlueprintImplementableEventを指定しています。少しBlueprintNativeEventと似ていますが一番の違いは恐らくデフォルト実装を提供するかどうかだと思います。(自信少なめ) BlueprintNativeEventを指定した場合は[関数名]_Implementationを実装する必要がありますが、BlueprintImplementableEventの場合はデフォルト実装は要求されませんでした。(Interface関数なので当たり前ですが) ここもInterfaceでは弄る必要は無いでしょう。

そして関数定義をするわけですが、関数定義の注意点として引数はどのようなタイプかによってノードの引数と戻り値に別れます。結構分かりにくい所だと思います。 具体的には、型をそのまま(int32など)やポインタ型(UObject*など)やconst(const TArray<uint8>&など)を指定した場合は引数、参照を指定した場合は戻り値になります。 このような仕様になっているのは一つの値しか返せないC++の関数の特性を考慮してだと思います。

次に、GENERATED_IINTERFACE_BODY()によって生成されたコードがUDebugPrintableを要求するので、UInterfaceを継承したクラスを作成します。

// IDebugPrintable.h

class UDebugPrintable : public UInterface
{
    GENERATED_UINTERFACE_BODY()
};

これだけです。ただ、GENERATED_UINTERFACE_BODY()はコンストラクタは生成してくれないみたいなので、.cppファイルにコンストラクタの空実装を書きます。

// IDebugPrintable.cpp

UDebugPrintable::UDebugPrintable(const class FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
}

これで実装完了です。後はUE4Editor側で適当なブループリントを作ってInterfaceが実装できるかどうか試して終わりです。

ただ、私がまだインターフェースを使った関数呼び出しなどをやっていないので、そこまでは記事にできませんでした。一応する予定はあるので、やった場合はできれば記事にしたいと思います。

最後に全ソースを載せておきます。ちなみにヘッダーのインクルードはEngine.hと自動生成されたヘッダーと.cpp側でIDebugPrintable.hとプロジェクトのヘッダーをインクルードしておけば大丈夫だと思います。

// IDebugPrintable.h

// #include "[ModuleName].h"
#include "Engine.h"
#include "IDebugPrintable.generated.h"

class UDebugPrintable : public UInterface
{
    GENERATED_UINTERFACE_BODY()
};

class IDebugPrintable
{
    GENERATED_IINTERFACE_BODY()
    
public:
    UFUNCTION(BlueprintImplementableEvent)
    void DebugPrint(UDebugData* DebugData, UDebugStream DebugStream);
};
// IDebugPrintable.cpp

// #include "[ModuleName]PrivatePCH.h"
#include "IDebugPrintable.h"

UDebugPrintable::UDebugPrintable(const class FObjectInitializer& ObjectInitializer)
    : Super(ObjectInitializer)
{
}

初めまして。

初めまして。シェルスノウ(shsnow23)と申します。

 

最近、UnrealEngine4でゲームを作ろうとしているのですが、その過程で書いたプログラムなどについて、時間がたったらどんな感じで作ったか忘れてしまうんですよね。

なので、ブログにそれらを書いてしまえば、後で見直せる上に他の方の参考になるんじゃないかと思ってブログを始めてみました。

 

私は、VRゲームなどの開発を行いたいと思っているのでそれらの記事が多めになるかもしれません。

 

よろしくお願いします。