main() blog

プログラムやゲーム、旅、愛する家族について綴っていきます。

【UE4】UnrealEngine4をソースからビルドする

UnrealEngine4のソースを取得してビルドする方法についてまとめました。

以下の項目については事前に準備しておく必要があります。

ビルド手順

UnrealEngineのアカウントとGitHubのアカウントを関連付ける

UnrealEngineのアカウント管理画面でGitHubのアカウントを入力します。 これでUEのアカウントとGitHubのアカウントが関連付けられました。

ue4_000.png

GitHubからソースを取得する

GitHub内のEpicのページにアクセスしてUnrealEngieのページが開くことができれば関連付けが正しく行われたことになります。

ue4_001.png

zipファイルでダウンロードすることもできますが、今回はTortoiseGtiで取得してみます。 右上の緑のボタンのChose or downloadを選択してください。 そこに表示されているURLをコピーしておきます。

ue4_002.png

TortoiseGitでリポジトリをクローンします。 URLに先ほどのURLを入力して、ディレクトリは任意のディレクトリを指定しておきます。

ue4_003.png

ue4_004.png

これでGitHubからソースを取得することができました。

追加の依存ファイルをダウンロード

取得したフォルダのルートにSetup.batというバッチファイルがあるので実行します。 これで必要な追加ファイルがダウンロードされます。

ue4_005.png

ソリューションファイルの生成

GenerateProjectFiles.batを実行します。 UE4.slnというVisualStudioのソリューションファイルが生成されます。

詳しくはオフィシャルのドキュメントも参考にしてください。

ビルド

UE4.slnをクリックしてVisualStudioを起動してビルドしてください。

これでUnrealEngineをソースからビルドすることができました。

参考



【おでかけ】カフェノタビ

f:id:takezoh_1127:20170501092228j:image

カフェノタビに行って来ました!

 

カフェや古本、手作り雑貨などが集まるイベントです。

 

かずさアカデミアパークの近くの公園が会場です。

ここでの開催は2回目とのことです。

 

妻が好きな花屋さんも出店しています。

イベントを知ったのも花屋さんから教えてもらいました。

 

会場に入るとまずは古本のコーナー。

本だけを扱うお店や雑貨も一緒に置いてあるお店など10店ほど出ていました。

 

f:id:takezoh_1127:20170501092304j:image

f:id:takezoh_1127:20170501092429j:image 

下の子はフェーブ(フランスでお菓子に入れる陶器の小さな人形)に夢中に。

ずーっと選んでいましが、最終的に選んだのがタマゴ。

ネコとか女の子とか勧めたけど「マゴ!マゴ!」と言ってタマゴを嬉しそうに選んでいました。

 

昼ごはんはSmallAxeというお店のハンバーガー。

ボリュームもあってジューシーでメチャクチャ美味しいです。

こういうイベントには良く出店されているので他も食べたいけどやっぱり選んでしまいます。

 

それからPIZZA FORNOのピザ。

石窯焼きのピザでとても美味しいです。

このお店も別のイベントで食べたことがあります。

 

f:id:takezoh_1127:20170501092328j:image

 

f:id:takezoh_1127:20170501092345j:image

 

他にも手作り雑貨やカフェや食べるお店など沢山あって、ステージで音楽のイベントがあったりしてましたが下の子が楽しそうに走り回っていたのでゆっくりと見れません出した。

 

f:id:takezoh_1127:20170501092503j:image

 

f:id:takezoh_1127:20170501092531j:image

 

f:id:takezoh_1127:20170501092551j:image

 

 

帰りにhanahacoというカフェにも寄りました。

料理が美味しくてサラダバーも美味しいお店です。

会場からすぐ近くなのでこちらもオススメです。

f:id:takezoh_1127:20170501093326j:image

f:id:takezoh_1127:20170501093339j:image 

 

 

家からも近いし、来年も開催してくれると嬉しいです。

天気も良くて楽しい一日でした。

 

※下の子の「マゴ」

f:id:takezoh_1127:20170501093507j:image

 

※購入した古本

f:id:takezoh_1127:20170502085329j:image

 

※花屋さんで購入

f:id:takezoh_1127:20170502151146j:image

 

※イベントのチラシ

f:id:takezoh_1127:20170501093537j:image

f:id:takezoh_1127:20170501093550j:image 

【バグ】マスターまで残り4日!1バイトのメモリ破壊を追え!

はじめに

過去のプロジェクトでマスター直前に出たメモリ破壊のバグを調査した時の方法を公開します。

報告内容

プラットフォーム:コンシューマ
言語:C++

症状:
メモリアロケート時に門番の情報が破壊されているらしく、不正チェックに引っかかり、ASSERTで停止。
タイミングは不定、アドレスも不定な為ハードウェアブレイクでも追うのが困難な状況。
再現頻度はかなり低い。

今ところはデバッグ版のみで発生。
リリース版はエラーチェックに引っかからないので発覚していないだけで、メモリは破壊されている可能性はある。

調査開始

かなりやっかいなメモリ破壊系の不具合報告が来ました。

破壊している箇所がたまたま門番の場所だったのでその領域を解放した時のチェックで発覚しました。
メモリアロケート時に不正チェックに引っかかっているということは、それ以前のどこかのタイミングで破壊されていると思われます。
もしかしたら他のデータ領域を壊している可能性もありますし、その場合壊されたことに気が付かず動作し続けている可能性もあります。

色々と追っているがとっかかりがない状況です。

他のメンバーはマスター直前で他のチケットにかかりきりで誰も調べられていません。

可能性を考えてみます。

頻度は高くないが壊されたメモリを確認してみると、必ず1バイトが書き換わっている様で前後の門番のフィルの状態は壊されていませんでした。

バッファオーバーランの可能性もありますが、仮にそのオブジェクトのインスタンスでメモリを破壊してそのタイミングで門番を破壊しているようであれば
そのインスタンスが破棄されたタイミングなどでもエラーが出てもよさそうなものです。

まずは変数の書き込みを疑ってみることにします。

ということで以下の様なことをやって書き込みのタイミングで拾うようにしてみました。

調査方法

プロジェクトでは固有の型が定義されています。
簡潔に書くために便宜上以下の様にしています。

typedef char s8;
typedef unsigned char u8;
typedef short s16;
typedef unsigned short s16;
  :
  :

変数の書き込みのタイミングでチェックを行うために以下の様なclassを定義します。
力技ですがboolやs8,u8という型をGrepでBool,S8,U8に置換します。
これで今までと同じ挙動で変数の型を置き換えることができました。

class check_bool
{
public:
    check_bool() : val( false ){}
    check_bool( bool b ) : val( b ){}
    
    operator bool() const { return val; }
    operator int() const { return val; }
    
    check_bool& operator=( const bool &src )
    {
        // 書き込みに行く前にヒープ領域のチェック.
        if( Allocator::CheckAddressIsFreeBlock( &val ) )
        {
            ASSERT( " ### Error : free memory access.\n" );
        }
        
        val = src;
        return *this;
    }
    
    check_bool& operator=( const check_bool &src )
    {
        // 書き込みに行く前にヒープ領域のチェック.
        if( Allocator::CheckAddressIsFreeBlock( &val ) )
        {
            ASSERT( " ### Error : free memory access.\n" );
        }
        
        val = src.val;
        return *this;
    }
    
    bool operator==( const bool &src ) const
    {
        return val == src;
    }
    
    bool operator==( const check_bool &src ) const
    {
        return val == src.val;
    }
    
    bool operator!=( const bool &src ) const
    {
        return val != src;
    }
    
    bool operator!=( const check_bool &src ) const
    {
        return val != src.val;
    }
    
    bool operator!( void ) const
    {
        return !val;
    }
    
    bool operator<( const bool &src ) const
    {
        return val < src;
    }
    
    bool operator<( const check_bool &src ) const
    {
        return val < src.val;
    }
    
    bool operator>( const bool &src ) const
    {
        return val > src;
    }
    
    bool operator>( const check_bool &src ) const
    {
        return val > src.val;
    }

private:
    bool   val;

private:
    bool operator<( const bool &src );
    bool operator<( const check_bool &src );
    bool operator<=( const bool &src );
    bool operator<=( const check_bool &src );
    bool operator>( const bool &src );
    bool operator>( const check_bool &src );
    bool operator>=( const bool &src );
    bool operator>=( const check_bool &src );
};

template< typename Tp_ > class check_integer
{
public:
    check_integer(){}
    check_integer( Tp_ i ) : val( i ){}
    
    operator Tp_() const { return val; }
    
    check_integer& operator=( const Tp_ &src )
    {
        // 書き込みに行く前にヒープ領域のチェック.
        if( Allocator::CheckAddressIsFreeBlock( &val ) )
        {
            ASSERT( " ### Error : free memory access.\n" );
        }
        
        val = src;
        return *this;
    }
    
    check_integer& operator=( const check_integer &src )
    {
        // 書き込みに行く前にヒープ領域のチェック.
        if( Allocator::CheckAddressIsFreeBlock( &val ) )
        {
            ASSERT( " ### Error : free memory access.\n" );
        }
        
        val = src.val;
        return *this;
    }
    
    /** 前置インクリメント.
       
       r = ++i;
       
       int は前置と後置の判別のためのダミー
   */
    check_integer& operator ++ (int)
    {
        // 書き込みに行く前にヒープ領域のチェック.
        if( Allocator::CheckAddressIsFreeBlock( &val ) )
        {
            ASSERT( " ### Error : free memory access.\n" );
        }
        
        ++val;
        return *this;
    }
    
    /** 前置デクリメント
       r = --i;
   */
    check_integer& operator -- (int)
    {
        // 書き込みに行く前にヒープ領域のチェック.
        if( Allocator::CheckAddressIsFreeBlock( &val ) )
        {
            ASSERT( " ### Error : free memory access.\n" );
        }
        
        --val;
        return *this;
    }
    
    /** 後置インクリメント
       r = i++;
   */
    check_integer operator ++ ()
    {
        // 書き込みに行く前にヒープ領域のチェック.
        if( Allocator::CheckAddressIsFreeBlock( &val ) )
        {
            ASSERT( " ### Error : free memory access.\n" );
        }
        
        check_integer   r( val );   // 壊す前の値を返さなければならない.
        
        ++val;
        return r;
    }
    
    /** 後置デクリメント
       r = i--;
   */
    check_integer operator -- ()
    {
        // 書き込みに行く前にヒープ領域のチェック.
        if( Allocator::CheckAddressIsFreeBlock( &val ) )
        {
            ASSERT( " ### Error : free memory access.\n" );
        }
        
        check_integer   r( val );   // 壊す前の値を返さなければならない.
        
        --val;
        return r;
    }
    
    /** 単項マイナス
       r = -i;
   */
    check_integer operator - () const
    {
        return check_integer( -val );
    }
    
    /** 加算
       r = i0 + i1;
   */
    check_integer operator + ( const check_integer &src ) const
    {
        return check_integer( val + src.val );
    }
    
    check_integer operator + ( const int &src ) const
    {
        return check_integer( val + src );
    }
    
    /** 減算
       r = i0 - i1;
   */
    check_integer operator - ( const check_integer &src ) const
    {
        return check_integer( val - src.val );
    }
    
    check_integer operator - ( const int &src ) const
    {
        return check_integer( val - src );
    }
    
    /** 乗算
       r = i0 * i1;
   */
    check_integer operator * ( const check_integer &src ) const
    {
        return check_integer( val * src.val );
    }
    
    check_integer operator * ( const int &src ) const
    {
        return check_integer( val * src );
    }
    
    /** 除算
       r = i0 / i1;
   */
    check_integer operator / ( const check_integer &src ) const
    {
        return check_integer( val / src.val );
    }
    
    check_integer operator / ( const int &src ) const
    {
        return check_integer( val / src );
    }
    
    /** 剰余
       r = obj % o;
   */
    check_integer operator % ( const check_integer &src ) const
    {
        return check_integer( val % src.val );
    }
    
    check_integer operator % ( const int &src ) const
    {
        return check_integer( val % src );
    }
    
    /** 加算代入
       i0 += i1;
   */
    check_integer& operator += ( const check_integer &src )
    {
        val += src.val;
        return *this;
    }
    
    /** 減算代入
       i0 -= i1;
   */
    check_integer& operator -= ( const check_integer &src )
    {
        val -= src.val;
        return *this;
    }
    
    /** 乗算代入
       i0 *= i1;
   */
    check_integer& operator *= ( const check_integer &src )
    {
        val *= src.val;
        return *this;
    }
    
    /** 除算代入
       i0 /= i1;
   */
    check_integer& operator /= ( const check_integer &src )
    {
        val /= src.val;
        return *this;
    }
    
    /** 剰余代入
       obj %= o;
   */
    check_integer& operator %= ( const check_integer &src )
    {
        val %= src.val;
        return *this;
    }
    
    bool operator==( const s8 &src ) const
    {
        return val == src;
    }
    
    bool operator==( const u8 &src ) const
    {
        return val == src;
    }
    
    bool operator==( const s16 &src ) const
    {
        return val == src;
    }
    
    bool operator==( const u16 &src ) const
    {
        return val == src;
    }
    
    bool operator==( const s32 &src ) const
    {
        return val == src;
    }
    
    bool operator==( const u32 &src ) const
    {
        return val == src;
    }
    
    bool operator==( const int &src ) const
    {
        return val == src;
    }
    
    bool operator==( const check_integer &src ) const
    {
        return val == src.val;
    }
    
    bool operator!=( const s8 &src ) const
    {
        return val != src;
    }
    
    bool operator!=( const u8 &src ) const
    {
        return val != src;
    }
    
    bool operator!=( const s16 &src ) const
    {
        return val != src;
    }
    
    bool operator!=( const u16 &src ) const
    {
        return val != src;
    }
    
    bool operator!=( const s32 &src ) const
    {
        return val != src;
    }
    
    bool operator!=( const u32 &src ) const
    {
        return val != src;
    }
    
    bool operator!=( const int &src ) const
    {
        return val != src;
    }
    
    bool operator!=( const check_integer &src ) const
    {
        return val != src.val;
    }
    
    bool operator<( const s8 &src ) const
    {
        return val < src;
    }
    
    bool operator<( const u8 &src ) const
    {
        return val < src;
    }
    
    bool operator<( const s16 &src ) const
    {
        return val < src;
    }
    
    bool operator<( const u16 &src ) const
    {
        return val < src;
    }
    
    bool operator<( const s32 &src ) const
    {
        return val < src;
    }
    
    bool operator<( const u32 &src ) const
    {
        return val < src;
    }
    
    bool operator<( const int &src ) const
    {
        return val < src;
    }
    
    bool operator<( const check_integer &src ) const
    {
        return val < src.val;
    }
    
    bool operator<=( const s8 &src ) const
    {
        return val <= src;
    }
    
    bool operator<=( const u8 &src ) const
    {
        return val <= src;
    }
    
    bool operator<=( const s16 &src ) const
    {
        return val <= src;
    }
    
    bool operator<=( const u16 &src ) const
    {
        return val <= src;
    }
    
    bool operator<=( const s32 &src ) const
    {
        return val <= src;
    }
    
    bool operator<=( const u32 &src ) const
    {
        return val <= src;
    }
    
    bool operator<=( const int &src ) const
    {
        return val <= src;
    }
    
    bool operator<=( const check_integer &src ) const
    {
        return val <= src.val;
    }
    
    bool operator>( const s8 &src ) const
    {
        return val > src;
    }
    
    bool operator>( const u8 &src ) const
    {
        return val > src;
    }
    
    bool operator>( const s16 &src ) const
    {
        return val > src;
    }
    
    bool operator>( const u16 &src ) const
    {
        return val > src;
    }
    
    bool operator>( const s32 &src ) const
    {
        return val > src;
    }
    
    bool operator>( const u32 &src ) const
    {
        return val > src;
    }
    
    bool operator>( const int &src ) const
    {
        return val > src;
    }
    
    bool operator>( const check_integer &src ) const
    {
        return val > src.val;
    }
    
    bool operator>=( const s8 &src ) const
    {
        return val >= src;
    }
    
    bool operator>=( const u8 &src ) const
    {
        return val >= src;
    }
    
    bool operator>=( const s16 &src ) const
    {
        return val >= src;
    }
    
    bool operator>=( const u16 &src ) const
    {
        return val >= src;
    }
    
    bool operator>=( const s32 &src ) const
    {
        return val >= src;
    }
    
    bool operator>=( const u32 &src ) const
    {
        return val >= src;
    }
    
    bool operator>=( const int &src ) const
    {
        return val >= src;
    }
    
    bool operator>=( const check_integer &src )
    {
        return val >= src.val;
    }

private:
    Tp_     val;

private:
    bool operator!( void ) const;
};

typedef    check_bool          Bool;
typedef    check_integer< s8 >   S8;
typedef    check_integer< u8 >   U8;
bool Allocator::CheckAddressIsFreeBlock( const void* p )
{
    // 独自アロケータで実装されているので書き込みを行ったアドレスが
    // ヒープのフリー領域や門番等のアドレスかどうかをチェックします
}

この仕組みを施してテストに回したところ、テストプレイで見事にチェックに引っかかりました。

担当者に報告してデバッガ上で再現してもらい確認してもらったところ、デバッグ機能側で解放されたオブジェクトに対して操作を行っていたらしいです。

デバッグ機能というところまで絞り込めたので、そこまで分かれば再現手順も絞り込めます。
担当者に修正してもらい、テストプレイでも再現しないことが確認できました。

最後に

今回は本当に1バイトの変数の書き込みのタイミングだったのでバグが特定できましが、違う不具合だったらどう対処してたんでしょうか…

他にももっとスマートな解決方法があったもしれません。

そもそも論で言えばスマートポインタのような仕組みで設計、実装されていればというのもありますが、起きてしまったバグはなんとか潰さなければなりません。

手法はどうあれ問題を解決することが重要です。

同じやり方でというケースはないかもしれませんが何かの参考にでもなればと思います。

みなさんもやっかい系のバグに出くわして、こういう風に解決しましたという事例があったら教えてください。


そもそもバグに悩まされるのがいやなので、こちらの関連記事も読んでみてください。

www.main-function.com

【Unity】C#のタブがスペースに置き換えらてしまう

UnityでC#を触っていますが、VisualStudioで編集しているとタブが勝手にスペースに置き換えられてしまいます。

デフォルトの設定でそうなっているので何か理由があるのかもしれませんが、 自分はどうもそれに馴染めないので設定を変更してみます。

[ツール]→[オプション]でオプション設定ウィンドウを開いて、 [テキストエディター]→[C#]→[タブ]の設定でタブの保持にチェックを入れます。

f:id:takezoh_1127:20170424104133p:plain

みなさんはどうしているのでしょうか? それからチームで開発するときはどう統一しているのでしょうか?

【UE4】ファイルスコープで宣言した変数、関数でビルドエラー

ファイルスコープや無名名前空間で変数や関数を宣言した場合、
異なるファイルで同名の変数や関数を宣言すると場合よってはビルドエラーになります。

  • Hoge1.cpp
static const FText VFXSocktNamePrefix = FText("VFX_");

namespace
{
    const float EnemyCheckRange = 1000.0f;
}
  • Hoge2.cpp
static const FText VFXSocktNamePrefix = FText("VFX_");

namespace
{
    const float EnemyCheckRange = 1000.0f;
}

UE4のunityビルドで同じファイルにまとめられた場合に重複定義としてビルドエラーになってしまいます。

そもそも同じ定義を使うのであればキチンと共通の定義すべきだし、もしくはクラスの定数として定義すべきです。

ここでの問題は後になってビルドエラーになってしまう点です。

実際、自分の環境ではビルドエラーにならず、コミットした後で自動ビルドでエラーになりました。

Module.HogeGame.1_of_14.cpp等のファイルを開いてみると以下の様なファイルになっています。

#include "HogeGame.h"

#include "Hoge1.cpp"
#include "Hoge2.cpp"

ファイルはアルファベット順になっていて、ファイル数なのか実際に展開されたファイルサイズなのかは分かりませんが 一定数ごとにファイルが分かれていきます。

ここでHoge1.cppとHoge2.cppが同じファイルにまとめられてしまうと重複定義となってビルドエラーになっていしまいます。


【記憶のゴミ箱】ダンジョンエクスプローラー

f:id:takezoh_1127:20170425234639j:image

 

中学生の時好きだったゲーム。

 

僕がと言うよりは僕たちが好きなゲーム。

中学の時の仲良し組みで集まっては良く遊んだゲームだ。

何回クリアしたことか。

 

PCエンジンのゲームで、最大5人で遊べるアクションRPG

その当時は次どこに行ったら良いか、

ダンジョンの最短のルートを覚えるまでやり込んでいた。

 

I本はファイター、Y山はシーフかドワーフ、M本はエルフ、

そして自分はビショップだ。

各々キャラクターも決まっていてほとんど同じパティー構成になっていた。

 

幾度と無く同じ道を通り、幾度と無くボスを倒す。

その繰り返しだったが楽しかった。

デスが出てきたときはみんなであっちでもない、こっちでもないと

ギャーギャー騒ぎながら遊んでいた。

 

時には闘技場で殺し合いもした。

当然ビショップでは勝てるわけも無く、いつも悔しい思いをしていた。

 

それでも今日も集まってはみんなで遊んでいた。

 

そういえばPCエンジンのゲームって集まって遊べるゲームが多かったんだよね。

フォーメンションサッカーやファイヤープロレスリングなんかはその代表で、

そのファイヤープロレスリングではこんなこともあった。

 

いつものように集まって4人でファイヤープロレスリングで遊んでいた。

初代ファイプロはハメ技があった。

リング外に一度落ちると、リングインする際にそこを狙われ打撃系の大技が決まってしまい、

再びリング外に落ちてしまうというものだ。

暗黙の了解があって、ある程度体力を削る分には多少のハメは許されていた。

しかし、勝ち負けにこだわり試合に没頭するあまり、

ちょっと調子に乗りすぎたあまりにこれでもかいうほどにハメ技を食らわしていた。

その刹那、M本が叫んだ。

 

「ハメるなよ!」

 

まじ切れだったと思う。

ケンカになった。

 

「ゲームくらいで怒るなよ!」

Y山が言ってケンカは収まった。

 

その後は険悪な雰囲気の中でゲームを続けた。

当然、ハメは封印された。

ゲームくらいで怒るなよ。

確かにゲームくらいでと言うが、それほどうちらが純粋だったのか、バカだったのか...

ちなみにファイプロ2ではそのあたりが少し改善され、起き上がりのタイミングがずらせるようになっていた。

 

こういうのも良い思い出で、またみんなで集まって遊びたいと時々思ったりもする。