mori

専門学校HAL×Aiming 産学連携プロジェクト(後編)


こんにちは、人事の森です。

前編に続き、「HAL×Aiming産学連携プロジェクト」後編をお送りします。

今回は、栄えある優秀賞を受賞した2作品を紹介させて下さい。優秀賞は当初、1作品のみを選出する予定でした。ところが、どちらの作品も系統が異なりとても面白い!吟味した結果、2作品共に優秀賞を授与する形となりました。

優秀賞受賞作品は、Aiming名義で、App StoreとGoogle Playでリリースします。

それでは、2つの作品を紹介させて頂きます。

 

まずは1つ目、こちらはHAL大阪チームの作品です。

優秀賞:クレイジーヘッドボーイズ(HAL大阪:it OKASHI!チーム)

 

キュートでポップな二人のキャラクターが頭をぶつけながら疾走する爽快感あふれるラン&ジャンプアクションゲームです。

  • タップでジャンプというシンプルな操作だが、画面の上下にプレイヤーを置くことで、良い意味での複雑なゲームデザインが構築されている
  • ゲーム開始までのタップ数が少なく、迷うことなくプレイ開始に誘導している
  • チュートリアルから体感型レベルデザインが組み込まれていて、失敗を通じて正解を模索させるような作りになっている
  • 骨太なアクションゲームに仕上がっていて、しっかりと遊べる
  • ビジュアルにも力入れている

 

カメラアングルも独自性があり、この効果によりスピード感が倍増しているように感じます。こちらの操作方法で、上手くいった時、墜落しちゃった時を実感できるので、「もう1回!」と何度もプレイしたくなりました。キャラクターが可愛いのも良い感じ。Sランクを取るのは非常に難しく、テクニックが必要です。私は未だそこに到達していません。どなたか、タイムアタックで勝負しませんか?

ダウンロードはこちらから。

App Store からダウンロードGoogle Play で手に入れよう

 

 

 

 

続いてHAL東京チームの作品

 

優秀賞:挟撃のインターポーザー(HAL東京:雨霧工房チーム)

個性的なキャラクターが多数登場し、敵を挟んで倒すというやりごたえのある戦略性がおすすめポイントです。

  • はさみ将棋に、独自の世界観とキャラクター性を上手に融合させている
  • ターンベースの詰め将棋といったプレイフィール
  • 見た目とは裏腹に、手応えのある難易度に調整してあり、しっかりと考えないと攻略できないというのが秀逸
  • リプレイ性が高い
  • ステージボリュームが豊富で倍速機能もついている(気遣いが良い感じ!)

 

アプリ内課金の要素はないものの、商用ゲームさながらのまとまりがあり、とても完成度の高いゲームです。クラシカルな内容に、演出とイラストが見事に彩りを与えています。ステージごとに満点をとるのは難しく、満点をとるために熱中してプレイしてしまいました。「挟撃のインターポーザー」というネーミングも素敵です。ゲーム内に漂うラノベ的な雰囲気も、このタイトルに上手く表現されていると思います。

ダウンロードはこちらから。

App Store からダウンロードGoogle Play で手に入れよう

 

 

 

いかがでしょうか・・・?2作品共に短期間の開発ながら非常に完成度が高く、楽しく遊べるゲームに仕上がっています。それぞれ配信期間は、2018年3月31日までを予定していますが、落し切りのゲームなので、DLしてしまえば配信期間後もプレイすることが出来ます。皆さま、是非手に取って、遊んで見て下さい!

(まとめ)

さて、今回の産学連携プロジェクト。4月のオリエンテーションから始まり、中間企画発表、総合作品審査、受賞式、リリースと経て幕を閉じました。とても濃密な内容でした。優秀賞受賞チームの皆さんには、リリース準備の際に、+αの対応事を様々依頼しましたが、そのレスポンスの速さと的確な内容から、非常にスムーズに進めることが出来、感謝しております。

また、普段開発側の人間でない私にとっては、開発したゲームをリリースまで持っていくこと、そのために必要なもの、時間、やるべきこと、注意点、落とし穴、等々・・・通して体感することが出来たことはとても貴重な体験でした。こんな作業、手間をかけてゲームはリリースされお客様に届くのだと改めて認識したことと、ゲームを作り、世に生み出す開発者の方のお仕事を少しでも体験できたことも良かったです。

 

最後に・・・

このプロジェクトは、HAL学生さんの「ゲーム制作」や「チーム開発」への熱意、工夫、技術、拘り、愛、頑張りなしには成立しないプロジェクトです。あわせて、これらの企画~実施、運営までのサポートを下さったHALの先生方のお力添えやお気遣い・・・こちらも学びが多く、このような機会を頂けたことに感謝しています。関わって頂いた皆様、ありがとうございました。

 

(おまけ)

クレイジーヘッドボーイズ、どうしてもRANK Sがとれませんでした。28秒の分厚い壁、私はいつ越えられるのでしょうか。

 

私の中の最高記録のRANK Aで、「産学連携プロジェクト」(前編/後編)を締めたいと思います。(完)


mori

専門学校HAL×Aiming 産学連携プロジェクト(前編)


こんにちは。人事の森です。

今日は、学校法人・専門学校HALとAimingが産学連携の1つとして行った「産学連携プロジェクト」について紹介したいと思います。(前編)
「産学連携」とは、教育機関である学校法人・専門学校HALと、民間企業である株式会社Aimingが連携し、学生がスマートフォン用のゲーム開発に挑戦するというプロジェクトです。

このプロジェクトは、「今日は皆さんに、ちょっとゲームを作ってもらおうと思います」という映画バトルロワイヤル風に始まりました。(古いですか)

内容はこうです。

  1. Aimingが出したテーマに沿ってHALの学生がゲームを制作する
  2. 提出されたゲームを「企画段階」と「完成品」の2段階で審査する
  3. 優秀なゲームには、Aimingが用意した幾つかの賞を授与する

特に優秀なゲームは、Aiming名義で、App StoreとGoogle Playからリリースします。

 

制作は、企画期間1ヵ月/開発期間1ヵ月という、とても限られたスケジュールです。この内容に対し、HALの400名を超える学生が、チームを組んで挑戦してくれました。

Aimingがお題としたテーマは以下の3つです。

  1. 最初の3分のおもしろさ
  2. 新規性
  3. 遊びやすさ

この3つを軸に、作品審査を行いました。

iOSの開発は初めてという方が多い中、たくさんのチャレンジがあり、工夫があり、熱意があり、審査であるにも関わらず、ついつい楽しくプレイさせて頂きました。

企画段階のものから内容を大きく変更し、素晴らしくブラッシュアップされた作品もありました。ネットワークに挑戦し、オンライン要素を取入れた作品もありました。チームワークや頑張りの跡が見られる作品は、審査にも熱が入ります。

選びきることに苦悩はありましたが、Aimingが用意した賞は全部で4つ。
「優秀賞」、「ゲームデザイン賞」、「技術賞」、「審査員特別賞」です。

前編では、優秀賞を除く5つの受賞作品について紹介したいと思います。

 

 

まずは、ゲームデザイン賞:「Pechantet」(HAL大阪)

指でなぞった地面が切り取られ折り曲がり、そこに敵を挟んで倒すという、操作感と見た目も面白いタワーディフェンスです。ゲームデザインに優れているポイントは、リソース管理と操作が上手に融合している点です。大きくなぞると簡単に敵を挟めますが、次の敵を攻撃することが出来なくなってしまう。敵の動きを予想して、最小範囲で切り取り挟む。更に角度も考え遊ぶというテクニカルな要素がとても面白い作品です。

 

 

続いて、技術賞:「双璧のマジカライズ」(HAL名古屋)

画面いっぱいの大量のスライムを魔法の壁でプチプチ挟んで倒していく、小気味良く簡単な操作で楽しめるゲームです。
ゲームとしては荒削りであるものの、コアメカニクスである指でなぞったルートのスライムを巻き込んでいくというのが、”わかりやすいゲームデザイン + 面白いビジュアル + 簡単操作のユーザビリティ” として、バランス良くまとまっていました。
ベースとしてテクニカルなアプローチがあり、この美点を技術賞として評しました。

 

 

そして、審査員特別賞は個性豊かな3作品がノミネート。

「ヤンキーキャット」(HAL名古屋)

ヤンキーを操作して相手のヤンキーを倒す「喧嘩上等」のシミューレーションに、ネコの要素が加わることで癒やしの効果が得られるネコゲーです。
ベースは挟み将棋で、相手ヤンキーを挟んで倒していくというものです。そしてそこにネコ登場!
通常は倒すしかないヤンキーも、ネコを間に入れる(相手とネコを挟む)ことで場が和み、和解すれば相手を仲間にすることができます。
ネコ好きに悪い奴ぁいねぇ理論ですね。(私は犬派です。)
ヤンキーというクラシカルな題材ではあるものの、細かな動きや演出面など細部までこだわりが見え、ゲーム全体を通してクオリティの高さがうかがえるゲームです。
センスが光るテキストに、にやにやしながら審査しました。

 

 

「ぽより~」(HAL名古屋)

風船を思わせる妖精”ポヨリー”を、上手く操作し上昇させて故郷に帰してあげる、とても可愛いビジュアルのゲーム。
ポヨリーを指で挟んで空気を押し出し、その反動でどんどん上昇させてゴールに導いてあげます。
直感的な操作と、瞬時にルールを理解できるゲームデザインが秀逸です。
操作は簡単ですが、挟み方によってはポヨリーが右に左に飛んでいき、ゆっくり操作すれば落下していくので、慎重かつ大胆なプレイが求められます。
キュートな世界観も魅力のゲームです。

 

 

「日狭茶飯事」(HAL東京)

ふりそそぐ食品をトングで挟んで食べまくる。シンプルながら瞬間的に熱くなれるゲームです。
本能に訴えかける説明不要のゲームルールとポップなビジュアルが上手く組み合わさり、高い中毒性と、1人プレイながらも場を盛り上げることのできるパーティーゲームに仕上がっています。
ゲーム開始までのタイトルやイントロのカットシーンなどもポップな演出で彩られ、細部まで抜かりがありません。
丁寧な作りを感じます。プレイ動画は、YouTube投稿にも向いてるかもしれません。

 

さて、栄えある優秀賞は・・・!?(後編に続く)

 

(おまけ)HAL大阪での授賞式の1コマ

素晴らしいゲームを作ったチームメンバーの皆様と、素敵な先生で記念撮影★

 


syoshino

AssetBundleGraphTool を触ってみました


エンジニアの吉野です。

先日アップデートされたばかりの AssetBundleGraphTool v1.3 をこれまた先日リリースされた Unity 2017.1 に入れてみました。

AssetBundleGraphTool の bitbucket ページ
日本語マニュアル

今回はゲームで使用する Asset がどの AssetBundle に入っているかのテーブルデータをカスタムノードで作成したのでかいつまんで紹介しようと思います。

概要

ゲームで Asset を読むときに、開発中はローカルから、実機では AssetBundle を使いたいということが良くあります。
(Unite 2017 で紹介されていた Addressable Assets が入ればいらなくなるはずなので、それまでのつなぎで…)
これを満たすための仕様としては、おおむね以下の図のようになると思います。

asset load flow

この図の AssetBundleTable で使う AssetPath → AssetName 変換用のデータをカスタムノードで作ります。

完成したGraph図はこのようになります。

graph sample

カスタムノードの作り方は日本語のマニュアルの「カスタムノードを追加する」を見てください。
以下、作成したノードの要点を解説していこうと思います。

入力/出力するノードのタイプを指定する

今回は、「AssetBundle 名と AssetPath が設定されたノード」(Grouping)と 「AssetBundle をビルドするノード」(BundleBuilder)の間に入れたいので、以下のようにします。


    public override Model.NodeOutputSemantics NodeInputType
    {
        get
        {
            return Model.NodeOutputSemantics.AssetBundleConfigurations;
        }
    }

    public override Model.NodeOutputSemantics NodeOutputType
    {
        get
        {
            return Model.NodeOutputSemantics.AssetBundleConfigurations;
        }
    }

インスペクター上から AssetBundleTable を指定できるようにする

Inputされた情報を書き込む AssetBundleTable を渡せるようにします。
指定はエディタ拡張でおなじみの OnInspectorGUI(…) 部分に追加します。


public override void OnInspectorGUI(
    NodeGUI node,
    AssetReferenceStreamManager streamManager,
    NodeGUIEditor editor,
    Action onValueChanged)
{
...
    tableAsset = EditorGUILayout.ObjectField("AssetBundleTable Object", tableAsset, typeof(AssetBundleTable), false) as AssetBundleTable;
...
}

ビルド時に AssetBundleTable を更新する

Graph をビルドすると、カスタムノードの Build(…) が呼ばれます。この時に受け取った情報をTableに設定します。


public override void Build(
    BuildTarget target,
    Model.NodeData node,
    IEnumerable<PerformGraph.AssetGroups> incoming,
    IEnumerable<Model.ConnectionData> connectionsToOutput,
    PerformGraph.Output Output,
    Action<Model.NodeData, string, float> progressFunc)
{
...
    tableAsset.Clear();
    foreach (var ag in incoming)
     {
         foreach (var assetGroup in ag.assetGroups)
         {
             foreach (var inputAsset in assetGroup.Value)
             {
                 tableAsset.Add(new AssetBundleLoadPath(assetGroup.Key, inputAsset));
             }
         }
     }
     EditorUtility.SetDirty(tableAsset);
     AssetDatabase.SaveAssets();
...
}

AssetBundleTable 自身も AssetBundle にする

実機で使うので、AssetBundleTable も忘れずに AssetBundle にします。
幸いこの後に AssetBundle をビルドするノードがあるので、Output に追加して流します。


public override void Build(
    BuildTarget target,
    Model.NodeData node,
    IEnumerable<PerformGraph.AssetGroups> incoming,
    IEnumerable<Model.ConnectionData> connectionsToOutput,
    PerformGraph.Output Output,
    Action<Model.NodeData, string, float> progressFunc)
{
...
    EditorUtility.SetDirty(tableAsset);
    AssetDatabase.SaveAssets();

    if (Output != null)
    {
        var dst = (connectionsToOutput == null || !connectionsToOutput.Any()) ?
            null : connectionsToOutput.First();

        // 上流のノードから流れてきたものをそのまま渡します。
        foreach (var ag in incoming)
        {
            Output(dst, ag.assetGroups);
        }

        // AssetBundleTableを追加して渡します。
        var additionalOutput = new Dictionary<string, List<AssetReference>>();
        additionalOutput.Add(settings.tableAssetBundleName,
            new List<AssetReference> { 
                AssetReference.CreateReference(AssetDatabase.GetAssetPath(tableAsset), typeof(AssetBundleTableListData))
            }
        );
        Output(dst, additionalOutput);
    }
}

これで、作成した Graph がビルドされるたびに AssetBundleTable が更新されて、AssetBundle にまでなってくれます。
日本語のわかりやすいマニュアルがあるおかげで、すんなりと実装できてしまいました。

使ってみた感想

ノードの拡張機能があるおかげで Asset を読んで加工してなにかする、という機能をグラフィカルかつ低コストで作成できる素晴らしいツールだと思います。
カスタムノード以外にもいろいろな機能があるのでぜひマニュアルを見てみてください。
いろいろ応用できそうなので今後も活用していこうと思います!


Unity 2017.1.0 BetaでInspectorの入力ができなくなる問題を回避する方法


こんにちは。大阪スタジオ エンジニアの西村です。

現在開発中のプロジェクトではUnity 2017.1 Beta版を使用しています。

Unity 2017.1.0b3~Unity 2017.1.0f1のWindows版でInspectorの入力ができなくなるという不具合が発生していて作業に支障が出ていました。

人によっては不具合が発生しておらず、調査の結果Microsoft IMEを使っている場合のみ発生する事がわかりました。

回避方法

Unity使用時、キーボード設定をMicrosoft IME以外に設定する。

Google 日本語入力やUSキーボードを使用している人は不具合が発生していませんでした。

うっかり入力できなくなった時の対処方法

Componentメニューを開いて閉じるだけでInspectorに入力できる状態に戻ります。

不具合はUnityに報告済みで、いずれ修正されるでしょう。


mewlist

uGUI 描画優先度のチートシート


皆様,こんにちは!
株式会社Aiming の 土井と申します!
リードソフトウェアエンジニアをやっております!

ここ数年は,業務で Unity の uGUI を使って UI 開発をする機会が多いのですが、
Order in Layer, Sorting Layer, ヒエラルキ上の並び順……(つд⊂)ゴシゴシ
Unity では重なり順を指定する項目が多いですよね。
これらの項目間の優先度が実際にどうなっているのか理解していなかったため、調査を兼ねてチートシートを作ってみました!

描画優先度を指定するものを列挙してみた

どうやらこれらの関係を全て調べ上げれば良さそうです。

チートシート

調べました。
画像は実際に Unity の GameView で表示されたもののキャプチャです。

これで、もう UI の重なり順で悩まない!

解説

カメラ毎の優先度

  • Canvas に設定された Camera による優先度
    • ScreenSpace: Overlay 設定のものが最優先
    • 次いで Depth 値が高いものが優先

同じカメラに設定されたキャンバス同士

  • SortingLayer が高い Canvas が優先
    • 同じ SortingLayer 同士では、 Order In Layer の値が高い Canvas が優先
      • さらに同じ Order In Layer 同士では、Z 値が低い(カメラに近い) Canvas が優先

Mesh や パーティクルなど UI 以外の Renderer について

  • SortingLayer や Order In Layer の値による重なり順の制御ができる
  • ただし、SortingLayer と Order In Layer が同じ値を持つ Canvas がある場合、ヒエラルキ上の並び順による重なり順のソートは行われないので、 固有の Order In Layer 値を設定しよう

Render Queue について

  • UI の描画は、 RenderQueue = “Transparent” にて行われるため、独自のマテリアルを使用する場合は RenderQueue の値に 2501 以上を指定する必要がある

最後に

  • UI のレイヤーの間に 3D モデルを表示したりエフェクトを出したりといった要件のときには、まず全体の UI のレイヤー構成を整理して仕様化しましょう。行き当たりばったりで重なり順をいじっていると混迷を極めます
  • MeshRenderer などは標準でインスペクタ上から SortingLayer や Order In Layer が設定できません。
    • https://docs.unity3d.com/ja/current/ScriptReference/Renderer.html
    • 自前でスクリプトから設定する必要があります
      • (Order In Layer は sortingOrder というプロパティです)

おしまい。


Unity5.6.0で追加されたTest RunnerのPlayModeを使ってみた


こんにちは。大阪スタジオ エンジニアの西村です。

Unity 5.6.0からTest RunnerにPlayModeが追加されました。以前からあったEditModeのテストではカバーできなかったフレームをまたぐ非同期処理などのテストが可能になります。ざっくり試してみた結果をまとめてみました。この記事ではUnity 5.6.0f3を使用しています。

PlayModeを有効にする

初期状態ではPlayModeが無効になっているので有効にします。まずWindow – Test RunnerでTest Runnerウィンドウを開きます。

PlayModeタブを選択すると”Enable playmode tests”というボタンがあり、これを押した後でUnityを再起動する必要があります。

これでPlayModeが使えるようになりました。

PlayModeでテストを実行してみる

Create Playmode test with methodsを押すとテンプレートスクリプトが作成されます。

NewPlayModeTest.cs

using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;

public class NewPlayModeTest {

    [Test]
    public void NewPlayModeTestSimplePasses() {
        // Use the Assert class to test conditions.
    }

    // A UnityTest behaves like a coroutine in PlayMode
    // and allows you to yield null to skip a frame in EditMode
    [UnityTest]
    public IEnumerator NewPlayModeTestWithEnumeratorPasses() {
        // Use the Assert class to test conditions.
        // yield to skip a frame
        yield return null;
    }
}

スクリプトがコンパイルされるとPlayModeにテストが追加されます。

とりあえず手を加えずにRun Allしてみると。シーンがテスト用のものに入れ替わりUnityがPlay状態になります。しばらく待つとテストが実行され結果がTest Runnerウィンドウに反映されます。

当然なにもないので成功です。

次は失敗させてみましょう。適当にAssertを追加します。EditModeのテストと同じ感覚で書くことができます。

NewPlayModeTest.cs

using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;

public class NewPlayModeTest {

    [Test]
    public void NewPlayModeTestSimplePasses() {
        // Use the Assert class to test conditions.
        Assert.IsTrue(false, "失敗させてみる");
    }

    // A UnityTest behaves like a coroutine in PlayMode
    // and allows you to yield null to skip a frame in EditMode
    [UnityTest]
    public IEnumerator NewPlayModeTestWithEnumeratorPasses() {
        // Use the Assert class to test conditions.
        // yield to skip a frame
        Assert.IsTrue(false, "失敗させてみる");
        yield return null;
    }
}

はい、失敗しました。

PlayModeらしいテスト

ここまではEditModeでも実行できる内容です。次にEditModeでは出来なかったフレームの更新がある状態になっているか試してみます。

NewPlayModeTest.cs

using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;

public class NewPlayModeTest {

    [Test]
    public void NewPlayModeTestSimplePasses() {
        // Use the Assert class to test conditions.
    }

    // A UnityTest behaves like a coroutine in PlayMode
    // and allows you to yield null to skip a frame in EditMode
    [UnityTest]
    public IEnumerator NewPlayModeTestWithEnumeratorPasses() {
        // Use the Assert class to test conditions.
        // yield to skip a frame
        yield return null;
    }

    [UnityTest]
    public IEnumerator 複数フレームに渡ってテストできる() {
        // Time.timeは同一フレーム中は同じ値を返す
        var startTime = Time.time;
        System.Threading.Thread.Sleep(1000);
        Assert.AreEqual(startTime, Time.time);

        yield return new WaitForSeconds(1f);
 
        // フレームが変わっていればTime.timeの値が変わっているはず
        Assert.AreNotEqual(startTime, Time.time);
        yield return null;
    }
}

複数フレームに渡ってテストできることがわかります。

MonoBehaviourTest

MonoBehaviourTestと言うものがありますが詳しい使い方がわかりませんでした。動作テストに使用したコードは以下の通り。

ExampleBehaviour.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ExampleBehaviour : MonoBehaviour {
    protected int counter = 0;

    // Use this for initialization
    void Start () {
 
    }
 
    // Update is called once per frame
    protected void Update () {
        counter++;
    }
}

ExampleBehaviourTest.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.TestTools;

public class ExampleBehaviourTest : ExampleBehaviour, IMonoBehaviourTest {
    public bool IsTestFinished { get; private set; }

    // Update is called once per frame
    new void Update () {
        base.Update();
        Debug.Log(counter);
        if (counter > 10)
        {
            // ここで止めておかないと他のテストの裏でも動き続ける
            gameObject.SetActive(false);
            IsTestFinished = true;
        }
    }
}

NewPlayModeTest.cs

using UnityEngine;
using UnityEngine.TestTools;
using NUnit.Framework;
using System.Collections;

public class NewPlayModeTest {

    [Test]
    public void NewPlayModeTestSimplePasses() {
        // Use the Assert class to test conditions.
    }

    // A UnityTest behaves like a coroutine in PlayMode
    // and allows you to yield null to skip a frame in EditMode
    [UnityTest]
    public IEnumerator NewPlayModeTestWithEnumeratorPasses() {
        // Use the Assert class to test conditions.
        // yield to skip a frame
        yield return null;
    }

    [UnityTest]
    public IEnumerator 複数フレームに渡ってテストできる() {
        // Time.timeは同一フレーム中は同じ値を返す
        var startTime = Time.time;
        System.Threading.Thread.Sleep(1000);
        Assert.AreEqual(startTime, Time.time);

        yield return new WaitForSeconds(1f);
 
        // フレームが変わっていればTime.timeの値が変わっているはず
        Assert.AreNotEqual(startTime, Time.time);
        yield return null;
    }

    [UnityTest]
    public IEnumerator MonoBehaviourのテスト() {
        yield return new MonoBehaviourTest<ExampleBehaviourTest>();
    }
}

MonoBehaviourTestを利用してMonoBehaviourを実行することも可能です。IMonoBehaviourTestを実装したMonoBehaviourがIsTestFinishedがtrueを返すことでテストが完了します。IsTestFinishedがfalseのまま30秒経過するとタイムアウトしますが、タイムアウトしても完了として扱われてしまいます。

MonoBehaviourTest実行中にAssertを呼び出してしまうと以後のテストが失敗してしまうため、Assertを呼び出すことでテスト単体を失敗させる事ができません。MonoBehaviourTestについてはそもそも使い方を間違えている可能性もあるので深追いしないことにしました。

Playerでテスト

左上のRun AllではUnity Editor上のPlayモードで実行されますが実際のPlayer上でも実行することが出来ます。

Build SettingsのSwitch PlatformでAndroidに切り替えてみると、右上のボタンがRun all in player(Android)に変わります。

実行してみるとビルドが走りAndroid端末でテストが実行されます。実行結果がUnity上のTestRunnerにフィードバックされないのは残念ですが手軽に実行することが出来ます。

まとめ

今回調べた中で分からなかった事

  1. UnityEngine.TestTools.MonoBehaviourTestの扱い方
  2. コマンドラインからPlayModeのテストを実行する方法

MonoBehaviourTestの扱い方が分からなかったため不完全燃焼ではありますが、PlayModeを利用することで今まで出来なかった非同期処理のテストも書くことが出来るようになりました。また、コマンドラインからの実行が出来ないのでCIに組み込む事が出来ませんが、コードレビュー時など手元でサクッと実行できるのは便利です。

今後もPlayModeテストをCIに組み込むことを視野に入れてウォッチを続けていきます。


Unity3DのDIフレームワーク、Zenjectの紹介


はじめまして。エンジニアの石井と申します。

今回は現在担当プロジェクトで使用しているUnity(ゲームエンジン)用DIフレームワーク、Zenjectを紹介させていただきます。

Zenjectとは

https://github.com/modesttree/Zenject

ZenjectはUnity3D向けに作成された、DI(依存性の注入)フレームワークです。
Zenjectを使うことでオブジェクト間の依存関係を外部から解決することができます。
簡単になにが良いかを説明しますと、
依存オブジェクトを別の入れ物(コンテナ)に格納しておきコンテナ経由で使うことで、
コンストラクタやメソッドの引数で渡していたり、自身でnewしていたり、FindObjectしていた部分をなくすことができます。

Zenjectサンプル

Zenjectを使ってのUIイベント処理の簡単なサンプルを作っていきます。
今回はボタンが押された時のイベントを処理するサンプルです。

1.インストール

AssetStoreからか、
https://github.com/modesttree/Zenject/releases
からダウンロードしてUnityのプロジェクトにインポートしてください。

2.Contextの作成

シーン上にContextを作成します。
右クリックからScene Contextを選んでください。
Zenject_context

3.Installerの作成

コンテナへの格納(Bind)にZenjectはInstallerというスクリプトを使用します。
今回はSingletonとしてオブジェクトをBindしていきます。

シーン上にGameObjectを作成して、
下記スクリプトをAdd Componentしてください。
※直接SceneContextでも問題ありません。
Zenject_Installer

using System;
using Zenject;

public class InstallerSample : MonoInstaller<InstallerSample>
{
    public override void InstallBindings()
    {
        Container.Bind<ZenjectSample>().AsSingle();
        Container.Bind<IInitializable>().To<ZenjectSample>().AsSingle();
        Container.Bind<IDisposable>().To<ZenjectSample>().AsSingle();
    }
}

4.ContextにInstallerをアタッチ

ContextにInstallerをアタッチすることでInstallerが機能します。
SceneContextのInstallersに、作成したInstallerSampleをアタッチしてください。

Zenject_Context_Installer0

5.UIの作成

UIを作成していきます。
下記のようにボタンを2つ作成し、
Zenject_UI_hierarchyZenject_UI

それぞれ下記のようにスクリプトをAdd Componentします。
Zenject Bindingスクリプトにより、Installerに定義しなくてもBindすることができます。
Identifierを使うことで、個別にInjectすることもできます。

Zenject_UI_Parts

using UnityEngine;
using UnityEngine.UI;
using UniRx;

public class UIView : MonoBehaviour
{
    [SerializeField]
    Button button;

    [SerializeField]
    Text text;

    public IObservable<string> OnClickObservable()
    {
        return button.OnClickAsObservable().Select(_ => text.text);
    }
}

イベントの変換はUniRxを使い、Textをstringで送出します。

6.サンプルクラスの作成

サンプルクラスを作成します。
[Inject]アトリビュートにより先ほど作成したUIViewが注入されます。
IInitializable.Initializeメソッドにより初期化され、
各ボタンのClickイベント購読し、押されたらOnClickメソッドが呼ばれるようになっています。
Sceneの切り替えやゲームの終了などでContextが破棄されるタイミングでIDisposable.Disposeメソッドが呼ばれ、
購読を停止しています。

using System;
using System.Collections.Generic;
using UniRx;
using Zenject;

public class ZenjectSample : IInitializable, IDisposable
{
    [Inject]
    List<UIView> buttons;

    List<IDisposable> subscriptions = new List<IDisposable>();

    void IInitializable.Initialize()
    {
        UnityEngine.Debug.Log("Initialize");

        buttons.ForEach(button =>
        {
            subscriptions.Add(button.OnClickObservable().Subscribe(text => OnClick(text)));
        });
    }

    void OnClick(string buttonText)
    {
        UnityEngine.Debug.Log(buttonText);
    }

    public void Dispose()
    {
        subscriptions.ForEach(subscription => subscription.Dispose());
        subscriptions.Clear();

        UnityEngine.Debug.Log("Dispose");
    }
}

7.動作確認

Sceneが読み込まれる際に、Initializeメソッドが呼ばれ、
ボタンが押されるたびにボタンのTextがログに表示され、
最後にDisposeが呼ばれていることがわかります。

Zenject_log

おわりに

簡単に紹介させていただきましたが、
他にもZenjectには様々なContext、Installerが用意されていますので、
依存関係の解決でお困りの方はぜひ一度試してみてください。


Unityのmetaファイルについての勉強会を行いました


こんにちは、エンジニアの堀尾です。

今回の開発者ブログは以前プロジェクト内で行ったUnityのmetaファイルについての勉強会のお話です。

勉強会を行った経緯

Unityを使用する上でmetaファイルは非常に重要です。しかし、エンジニア以外の人たちにとっては謎のファイルという認識になっておりたびたび問題が発生していました。

  • 問題の例
    • metaファイルのコミットを忘れたことでAssetの関係を保持できなくなる
    • GUIDが競合してしまい、エフェクトやモデルが正しく表示されなくなる

そこで、metaファイルとは何なのかどういった働きをしているのかを資料としてまとめ、勉強会を行いました。


プロジェクトのプランナーさんとデザイナーさん向けに発表を行ったのですが、内容が難しかったのか後半になるにつれ眠そうな人がちらほらと…。しかし、「今までただ謎のファイルとしか認識していなかったけど何となくわかった気がする」と言ってくれる人もいたので勉強会自体は開いてよかったなと感じています!

おわりに

勉強会を行っただけで全ての人がmetaファイルについて正しく理解できた訳では無いと思いますが、理解するまでの第一歩は踏み出せたのではないかなと思っております。


Unite 2016 Tokyoに参加してきました


はじめまして!エンジニアの鈴木聡です!

04/04-05の2日間開催された、Unite 2016 Tokyoに参加しましたので、その記事を書かせていただこうと思います!

Unityの中の人がたくさん!

Unity公式のカンファレンスということもあり、Unityの中の人の生の声が聞けるのが大きな特徴だと思います!開発の方も来日されますので、結果として講演は英語が多めに…でも大丈夫です!同時通訳が完備されていますので!(でも本当は英語で直接理解した方がいいと思います…)
unite2016_room1_m

Unity新機能

すでによく知られているものからあまり知られていないものまで、多くの新機能が発表されていました。アセットバンドルに関しては、Manifest や LZ4 圧縮や UnityWebRequest など、多くの利便性の向上が図られていて、すぐにでも活用していきたいと思いました!

リリースはまだ先になりそうですが、タイムラインエディター(Sequence Tools)がかなり将来性を感じる内容で、力を入れて開発を進めるという意思を感じましたので、実用化に向けて今から楽しみな感じです!

高速化まわり

モバイルにも3Dレンダリングの波は確実に押し寄せてきていて、コンソールやPCで実現できている表現が徐々に入ってきていて、なかなか苦労が絶えない今日この頃だと思います。その実現のために高速化や最適化についても、実際の開発例からUnityの内部構造に至るまでの様々な講義が行われ、今後の開発に役立てられそうな情報が少なからず入手できたのではないかと思います!日々精進ですね!

まとめ

今回、初めて参加させていただいたのですが、他のカンファレンスとはまた違った空気があり、なかなかよかったのではないかと思います!ここで得た知見を今後の開発に生かしていきたいと思います!
unite2016_booth_m

以下CM

実は、弊社はUnite 2016 Tokyoのブロンズ協賛をさせていただいてました!Uniteに参加された方は、入場の時に袋を手渡されていると思いますが、その中にひっそりと入っていたKitKatは、弊社「剣と魔法のログレス」のマンドラというキャラの絵が入った特注品だったんですよ!unite2016_kitkat_mはい、チョコレート。

このKitKatについては後日談がありまして、とある事情で会場で配る予定よりも大量に上回る個数を作られることになったのですが、ご厚意によりUnity Technology様より弊社に余剰分を無償提供していただけることになりました!unite2016_kitkatbox_mどどーん!

機会がある度に配布させていただこうと思っておりますので、気になる方は会場などで弊社社員にお声掛けいただければ幸いです!


ヴァリアントレギオンでUnity4からUnity5へバージョンアップした時に発生した問題


エンジニアの山内です。

絶賛サービス中のタイトル ヴァリアントレギオン のアプリ開発ではUnityを利用しており、開発開始時のUnityのバージョンは3.5で、現在は5.0.4と2回のメジャーバージョンアップを行っています。
Unity4 から Unity5 へバージョンアップした時に、こんな問題がありました、という紹介です。

特定の端末で文字が表示されない

特定GPUの端末で文字列が塗りつぶされて長方形になる問題が発生しました。文字の描画には unity-sysfont を使用していたのですが、5.x向けにはサポートされていないためと推測し、テクスチャ生成部分とレンダリング部分を改修して対処しました。

ビルド時間が2倍に

IL2CPPの影響でアプリのビルド時間が2倍になりました。対応として開発時はIL2CPPを切る方法が検討されましたが、その違いで不具合が発見されない方が怖いのでそのままにしています。

バイナリが制限サイズを越えた

IL2CPPの影響でアプリのサイズが大きくなり、ipaの制限である100MBを超えてしまいました。対応としてはチュートリアルで使用するデータをアプリから削って、追加ダウンロードするようにしました。
Androidでも制限を越えてしまいましたが、これはコンテンツ追加の影響だったので組み込みのテクスチャを減色・整頓しました。

Animation解放時にアプリがクラッシュする

ヴァリアントレギオンではUnity3の頃から開発していた名残でレガシーなAnimationを利用しています。Animationがついたモデルを解放するとき、アニメーションクリップをRemoveしなければクラッシュするというバグを踏みました。この問題は解放処理の前に停止処理を必ず行うようにして回避しました(Unityの不具合で現在もfixされていないようです)。
レガシーアニメーション関連の不具合は他にも余波があり、開発ツール側での問題解決ができておらず、一部の作業はUnity4で行うという事態になっています。さらにUnity5.1以上にバージョンアップした際にもAnimationで問題が出るため、現在5.0.4で足踏み中です。
現在これらを解消するべくAnimationからAnimatorに一括変換中です。

ほかにも

  • エネミーがバックステップする時にノーモーションでスッと下がる
  • メインメニュー上のアイコンの並びがガタガタになる
  • ガチャ演出で宝箱を空ける瞬間に宝箱が吹っ飛ぶ

など、見た目に楽しい(楽しくない)問題がありました。