lumino trail

ゲームエンジン作ってます。Github:https://github.com/lriki/Lumino Twitter:https://twitter.com/lriki8

水無月と作業報告の候

また1ヶ月コースの長いブランチを作ってしまいました。悪い癖です。

オフスクリーンレンダリングの実装中

作成中のゲーム、最初にその作品の中で一番の山場にあたりをつけてざっくりと作ってみています。 そこでどうしてもやりたい演出があるのですが、それに必要なので機能を追加しています。

鏡とかね。

f:id:lriki:20170630211918j:plain

空の作成中

この表現も重要なので作ってます。

f:id:lriki:20170630212106j:plain

プロファイラの実装中

テスト戦略成功の秘訣は日常的な実施である。

ということでパフォーマンステストしやすいように簡易的なプロファイラを作っています。

f:id:lriki:20170630212335j:plain

コーディングスタイルの変更(というか回帰)

関数名をすべて lower camel case に直しました。何年か前に upper に直してから、やっぱり lower に戻した感じです。

勤めている会社にまつわるいくつかの事情から upper にしていましたが、もうそういうのは気にしなくていいかなというのと、

  • WinAPI 関数名(マクロ)とのバッティングがとても、とてもつらい

  • C++11 の range based for とか STL とか、標準が lower 要求してるし

など諸々の理由で lower にしました。

upper にした時はある意味後ろめたい理由だったけど、今回はそうじゃ無いのでまるっきりバカな変更にはならない・・・はず。

今後

思うところあって7/7に一度リリースしようとインストーラやサンプルを準備していたのですが、多分無理そうです。 ちょっと仕事忙しくなっちゃった。

7月中には出したいですね。というか、もうブログ始めて半年だ。ダラダラしないようにしよう。

皐月と作業報告の候

新しくゲームを作り始めました

ゲームエンジンはゲームを作るためのものです。使ってみてはじめてわかる粗がたくさんあるわけです。その修正でまだAPIは安定しません・・・。

今後はゲームを作りながら、その都度 Lumino のほうも更新していきます。

f:id:lriki:20170531205554p:plain

ちなみにキャラ2D、マップ3Dなゲームが好きです。

シーングラフへの Entity Component System の導入

ゲームというのは仕様変更が頻発しがちな、ある種の特殊なシステムです。

変更に対して柔軟に対応するため一般的にはオブジェクト指向で作りますが、 is-a の関係を簡単に破壊するような変更よく起こります。

その解決策のひとつが Entity Component System です。Unity をはじめとして昨今のゲームエンジンはみんな備えているアレです。 機能を部品として分割し、オブジェクトは部品の集合体として定義する、そんな仕組みを入れています。

シリアライズ

Jsonファイルを簡単にシリアライズできる機能を作りました。 boost::serialization と同じようなものですが、Object を継承したオブジェクトをシリアライズする場合、型情報を利用した高度なシリアライズが可能になっています。

class EventData : public Object
{
public:
    String          name;
    float          x;
    float          y;
    float          z;
    String          textureName;
    List<Variant> commands;

    void Serialize(Archive& ar, int version)
    {
        ar & LN_NVP(name);
        ar & LN_NVP(x);
        ar & LN_NVP(y);
        ar & LN_NVP(z);
        ar & LN_NVP(textureName);
        ar & LN_NVP(commands);
    }
};

struct MapData
{
    String                  displayName;
    String                  meshName;
    List<RefPtr<EventData>> events;

    void Serialize(Archive& ar, int version)
    {
        ar & LN_NVP(displayName);
        ar & LN_NVP(meshName);
        ar & LN_NVP(events);
    }
};
{
    "displayName": "map1",
    "meshName": "MapMesh1.mqo",
    "events": [
        {
            "name": "EV000",
            "x": 1.0, "y": 1.0, "z": 1.0,
            "textureName": "chara_1.png",
            "commands" : [100, 101, 102]
        },
        {
            "name": "EV001",
            "x": -1.0, "y": 1.0, "z": 1.0,
            "textureName": "chara_1.png",
            "commands" : [100, 101, 102]
        }
    ]
}
MapData data = JsonSerializer::Load<MapData>(Assets::LoadText("MapData.json"));
data.events[1]->name;   // => "EV001"

とりあえずマップデータとかをツクールMVライクな JSON で用意していく方向で。

ビルド方法

Lumino のビルド方法を簡単にまとめました。 https://github.com/lriki/Lumino/wiki/BuildingWindows

卯月と作業報告の候

もうちょっとレンダリングフローの見直し中

前月に引き続き。

2D/3Dシーンと UI モジュールの関係がかなり変わってきています。

前はシーンのほうが偉くて、それの後描画的な立ち位置で UI の描画が入り込んでました。 今は UI のほうが偉くて、UIFrameWindow クラスをルートとした、ちょうど WPF や HTML のような UI要素の階層構造の一部(Canvas とか)としてシーンが描画されるようになりました。

なんでこんなふうになったの?

  • Lumino で作るゲームのエディタも Lumino で作りたい。
  • 本業ではネイティブアプリの GUI とか作ってるんだけど、そういうフレームワーク作ってみたくなっちゃった。

ウリというかコンセプトもちょっと変わってくると思います。 今までは「ゲームエンジン」だったけど、それプラス、「リアルタイムグラフィックスアプリケーション」。 なんかかっこいい。字面が。

あと、描画はコールバックの外側でもできるようになりました。 コードはこんなイメージ。

void Main()
{
    // コードをコンパイルして Shader オブジェクトを作る
    auto shader = Shader::Create("test.fx");
    
    while (Engine::UpdateFrame())
    {
        // Lumino 自体の描画処理
        Engine::RenderFrame();
        
        // 画面全体への、シェーダを用いた直接描画
        auto* r = Engine::GetSceneGraph3D()->GetRenderer();
        r->SetShader(shader);
        r->DrawScreenRectangle();

        // 描画結果をウィンドウに表示する
        Engine::PresentFrame();
    }
}

ピクセルシェーダをゴリゴリ書くとこんなのが出たりします。 f:id:lriki:20170430220705p:plain

ドキュメントとホームページ

ドキュメント書きたい。でもデザインめんどい。

こんなことがしたい。

  • Markdown で書ける
  • カテゴリやtocが柔軟に管理できる
  • 全文検索できる
  • 拡張しやすい
  • ソース管理しやすい

サイトジェネレータとか今時は星の数ほどあるけど、 がっつりとしたマニュアルやAPIドキュメント作りたいときはイマイチ力不足を感じます。

で、自作とかしてた最中に DocFX を見つけました。 この子はねぇ・・・つよいよ。

自作とかするまえにかなりたくさん調べたつもりだったけど、なんでヒットしなかったのか・・・。無能なだけか。

日本語情報もまだ少ないみたいだからそのうち使い方まとめとこう。

お試し版

ごめんね

弥生と作業報告の候

忙しい仕事が、今日、ようやく終わった。予定。

レンダリングフローの見直し中

今は OnRender コールバックの中とか、決められた範囲でしか描画できないけど それだとデバッグ描画するときにやり辛いので、制限無くどこでも描画できるように調整してます。

ハードウェアでアウトラインフォント書きたい

今の Lumino の文字描画は全部ビットマップベースです。一度テクスチャに書いてからスプライトとして描画しています。 シェーダをサポートしようと決めたときから気になってたのが、シェーダで文字を装飾するときに 普通のメッシュと同じシェーダを再利用しづらいことでした。

もう何年も前からやりたいやりたいで後回しになってましたが、レンダリングアーキテクチャとかパイプラインとかを整理する中で 「やっぱり実現性の検討くらいはやっておいた方がいいよね」と思いました。まる。

f:id:lriki:20170331235108p:plain

そこそこキレイに書けてるんじゃないかな?

テッセレーションは glues を使いました。 後で poly2tri も試してみたいけど、多分 gluTess のほうが FreeType とは相性良さそう?

あと、それだけだとアンチエイリアス無くてとても見れたものじゃなくなるので、 辺の押し出しでアンチエイリアスを表現してます。nanovg と同じやりかた。

で、とりあえず何とか使い物になる品質まで持っていけたと思うんだけど、やっぱり最終的な描画結果はグラボ依存です。 「このピクセルGeForce よりも Radeon の方が濃く見えます」とかすごく細かいところで微妙に差があります。 多分もうこの辺が限界だと思うので、安定して描けるビットマップとは互いに長所短所を補いながら両立していく方向で調整。

最後に

Mac の文字キレイ(羨望)

如月と作業報告の候

今月はお仕事忙しかったのでちょっと進み鈍いです。

.mqo ファイルを読み込めるようにしてます

3Dゲーム企画中。多分モデラーメタセコをメインで使うので。 でもまだスムージングが怪しいかも。 f:id:lriki:20170228230642p:plain

デバッグ描画を設計中です

長い間放置してたので。 使い方は UE4 みたいになるかな。 f:id:lriki:20170228230650p:plain

今後の予定

お試し版はまだです。来月もまだ仕事忙しそう。

睦月と作業報告の候

今月は黙々と作ってました。

ティーポット作れるようにしました

姿勢制御系のテストに使いたかったので。 f:id:lriki:20170131211525p:plain

Gizmo 作ってます

3Dゲームの設計始めた途端シーンエディタが欲しくなったので、その取っ掛かりです。 f:id:lriki:20170131211531p:plain

リポジトリ構成を大きく変えました

いままでサブモジュールとしてたものをすべて Lumino リポジトリに取り込みました。 最近 Issues を絡めたフローを意識して開発進めてますが、各リポジトリに頻繁に変更が入るのでかえって管理が面倒。 安定してきたらまた分けるかも。(何年先になるだろうか・・・)

C++ から言語バインダの自動生成できるようにしてます

UE4 の UCLASS マクロみたいに、

LN_CLASS()
class Sprite
    : public Object
{
    ...
};

と書いてコマンドラインツールに突っ込むと、ほかのプログラム言語用のラッパーを自動生成します。

今までは C 言語用の公開インターフェイスから変換してたけど、Lumino の機能が増えるにつれて限界になってきたので直接 C++ から変換できるようにしてます。

今は C# のみ。

SWIG 使わないの?

  • プロパティシステムやリフレクション、なんちゃってGC、イベントのブロードキャストなど、Lumino の機能が大きくなってきたので SWIG じゃカバーしきれない。
  • HSP その他、いずれは対応したいと思っているプログラム言語に SWIG が対応していない。将来的にも和製のマイナー言語なんて対応される見込みも無く。

今後の予定

これから毎月報告していく予定ですが、そのたびにお試し版をリリースできるようにしたいです。

ゲームエンジン「Lumino」の紹介

この記事は「ゲームエンジン・ライブラリ・ツールの開発 Advent Calendar 2016(http://qiita.com/advent-calendar/2016/gameengine)」の21日目です。 それなりのゲームエンジンを目指しつつも未だぴよぴよですが、せっかくなので公開したい。ということで、こんなことやっています、という紹介です。

Lumino とは

Lumino はゲームやグラフィックスソフトウェア を簡単に開発できるよう支援するための C++ ライブラリです。 ゲーム開発をメインのユースケースとしていますが、グラフィックスを中心とした様々な機能を、既存のアプリケーションに組み込んで使用することもできます。

特徴

さて、紹介、という体で改めて特徴に向き合うのもなかなか難しいです。

というのも、今の Lumino は単なるリアルタイムレンダリングエンジンにちょっとゲーム寄りの機能が付いた、という感じです。 それがどんなに効率よく綺麗な絵を描くことができるとしても、今できることはレガシーなマテリアルシステムで簡単な絵を描くだけです。 これまで作りこんできたシステムでどこまで行けるかは、今後の課題です。

そんな感じでまだまだ開発中ですが、今は以下のような方向を向いて進んでいます。

広く使われている API デザイン

Lumino はできるだけ Lumino らしさを求めないようにしています。 もしかしたら「ライブラリの色が薄い」と言うとピンと来る人もいるかもしれません。 Lumino で得た知識は他の開発ツールで利用できますし、逆に他の開発ツールで得た知識も Lumino で利用しやすい、ということを常に意識しています。

高パフォーマンス

Lumino はマルチコア環境に最適化されています。 昨今はPCに限らずモバイルでもマルチコアは当たり前になってきました。 そういったデバイスの性能を活かしきれるような設計にしています。

組み込みやすさ

Lumino に研究開発やプロトタイピングでも使いやすいように、という要求を持たせたのは、 私がエンジニアの仕事に就いてしばらく経ってからでした。 以来、既にある別のツールキットに組み込んで使うようなケースも考えています。

使い方

チュートリアルを書き始めています。よろしければ参考にしてみてください。

以下、いくつかの例を紹介します。 Lumino の雰囲気を見ていただければと思います。

Lumino で Hello world

#include <Lumino.h>
using namespace ln;

void Main()
{
    // Lumino の初期化処理
    Engine::Initialize();
    
    // 2D テキストを作成する
    auto text = TextBlock2D::Create("Hello, world!");
    
    // Lumino の更新処理 (ウィンドウへの描画などを行い、ウィンドウが閉じられていれば false を返す)
    while (Engine::Update()) {}
}

f:id:lriki:20161220233352p:plain

スプライトを表示する

#include <Lumino.h>
using namespace ln;

void Main()
{
    Engine::Initialize();

    // 画像を読み込み、2Dスプライトを作成する
    auto sprite = Sprite2D::Create("Assets/icon256.png");

    // 2Dスプライトの位置を設定する
    sprite->SetPosition(200, 100);

    while (Engine::Update())
    {
    }
}

f:id:lriki:20161220233436p:plain

ユーザー入力を受け取る

void Main()
{
    Engine::Initialize();
    
    // アクションマッピング (キーコンフィグ)
    // "left" と "right" に キーボードの左右キーと、ゲームパッドの1軸を割り当てる
    Input::AddButtonBinding("left", KeyboardBinding::Create(Keys::Left));
    Input::AddButtonBinding("left", GamepadBinding::Create(GamepadElement::Axis1Minus));
    Input::AddButtonBinding("right", KeyboardBinding::Create(Keys::Right));
    Input::AddButtonBinding("right", GamepadBinding::Create(GamepadElement::Axis1Plus));
    
    auto text = TextBlock2D::Create("A");
    
    while (Engine::Update())
    {
        Vector3 pos = text->GetPosition();
        
        if (Input.IsPressed("left"))   // "left" が押されているか?
            pos.x -= 5;
        if (Input.IsPressed("right"))  // "right" が押されているか?
            pos.x += 5;
        
        text->SetPosition(pos);
    }
}

音を鳴らす

void Main()
{
    Engine::Initialize();
    
    // BGM を再生する
    GameAudio::PlayBGM("bgm.ogg");
    
    int count = 0;
    while (Engine::Update())
    {
        // およそ2秒ごとに効果音を鳴らす
        if (count % 120 == 0)
        {
            GameAudio::PlaySE("se.wav");
        }
        count++;
    }
}

活用事例

開発中であったり、身内公開のままプロジェクトが終わったものが多いので、いくつかスクリーンショットでのご紹介です。 f:id:lriki:20161220234550p:plain

今後のロードマップ

一応、2D/3Dゲームを作る力はあります。が、それは私自身が開発に携わっていてヘルプが出せるからであって、第三者に Lumino だけ渡しても多分無理です。 なので、まずはなによりもドキュメントの整備と、開発中のゲームで必要な機能を優先的に実装したため生まれてしまった 滅茶苦茶汚いコード 負債の返済です。

そんなことと、開発中のゲームの完成を経て ver1.0 でしょうか。 そのあとはマルチプラットフォームとか、IDE とか、物理ベースレンダリングとかやりたいですね。

もし何かご意見などありましたら、GitHub の Issues に投げていただけると幸いです。