開発合宿に行ってきました 後篇

developcamp_summary2

前篇の続きです

前篇で紹介した3日で作った(完成してない)ミニゲームですが、
開発の過程で起きた問題や、こんなことやってたよーってのを紹介しようと思います

モバイル表示の色味が暗い、シーン再読み込みすると暗くなる

CloudBuildを使ってAndroid(Kindle Fire)で初めて動かしてみて、最初に気づいたのがPC表示と比べて色味が暗いこと。
モニタの違いかなーとも思いつつ結構な差があったので後で調べようと思ったのですが、
SceneManager.LoadScene でシーンの再読み込みを実装をしたときにPC版でも似たような現象が起きました

developcamp_lightsetting1developcamp_lightsetting2

左が起動したての画面、右が一度シーン再読み込みを挟んだ後の画面です

調べてみたところ、どうやらライティングのベイク設定が自動になっていると起きる問題らしい
UnityでLoadLevelを実行すると画面が暗くなる

シーンの読込時に”ライティングが無効になる/色が変になる”不具合の対処法

上記の記事通り Window -> Lighting -> LightMaps タブ -> Auto のチェックを外す -> Build
でPCもAndroidも安定したライティングがされるようになりました

developcamp_lightsetting3
developcamp_lightsetting4

今回は地形はプログラムで配置している都合上、エディタ編集時はモデルが配置されていないのも原因の一つかもしれないです

カラーバッファクリアの無効化

すこし実装が進んでから処理が気になって行ったのですが、
カメラの ClearFlags -> SkyBox となっているところを Depth Only に変更しました

developcamp_clearflags

2つのカメラ描画で25fpsぐらいだったのが50fpsぐらいまで上がりました
スカイボックスが見えないような場合はかなり有効なテクニックだと思います

Unityちゃん付属のスクリプト・何を使い何を無効にするべきか

今回はUnityちゃん付属のPrefabをベースに改造していったのですが、
デフォルトでコンポーネントがいっぱいくっついているので、どれがゲーム実装に必要なものなのか調べました

  • IdleChanger(無効)
    付属のAnimationControllerと対応して、GUIからアニメ制御をするスクリプト
    今回は自前でAnimationControllerを作成したのでこちらは無効にしました
  • FaceUpdate(有効)
    GUIでのフェイスアニメの制御と、AnimationEventを受け取ってフェイスアニメの変更を行うスクリプト
    ジャンプ中の表情を変えるなどの処理はジャンプアニメの中の情報とこのスクリプトの処理で行われています
    今回は顔アニメの変更を自前で制御するのは面倒(アニメ側もイベント設定する必要がある)ので、アニメはそのままで
    こちらのスクリプトも利用することにしました。GUI設定はいらないのでIsGUIだけOFFにしておきます

  • AutoBlinkForSD(有効)
    目パチをさせるためのスクリプトですこちらは有効にしておきます

  • SpringManager(有効)
    揺れモノ制御のスクリプトです
    髪や服などこれがないと硬い感じになってしまうので有効にしておきます

  • IKLookAt(有効)
    LookAtObjに設定した目標物を向くためのスクリプトです
    こちらはスクリプトは有効にしておき、必要な時だけIKActiveをONにする使い方をしました

謎のビルドエラー

2日目の朝、起きてCloudBuildを確認してみたらWebGLのビルドでこんなエラーが

error CS0246: The type or namespace name `SequenceCalclator' could not be found. Are you missing a using directive or an assembly reference?

SequenceCalclatorは自前のクラスでそんなの見当たらんで って言われてるわけですが、なぜそんなことになるのが思い当るところがなく・・
おなじリビジョンのAndroid版はビルド通っていたので余計に謎でした・・
結局CloudBuildのバグっぽいという判断に至りました
エラーが出ていた箇所の少し上を見るとプロジェクトに含まれているファイル一覧が出力されているのですが、
そこにはこのファイルが含まれていませんでした。

developcamp_cloudbuilderror

適当な変更を加えて再度コミットしたらビルドが通ったので、CloudBuildでなぜか起きるビルドエラーがあったらひとまず次のビルドを待ってみるのもいいかもしれません

Unityちゃんにカメラ目線をとってもらう

ゲーム開始時にUnityちゃんがカメラのほうを向いていますが、これはUnityちゃん付属のIKLookAtで実現しています
メインカメラの下に適当なGameObject(要MeshRenderer)を配置して、そのGameObjectをIKLookAtにセットし
IKActiveをOnにしたらカメラ目線してくれる という感じです

developcamp_lookcam1
developcamp_lookcam2developcamp_lookcam3

スローモーションについて

ジャンプ位置が近くなるとスローモーションになるようにしています
Time.timeScaleを操作することでゲームスピードの変更ができるのですが、
スクリプトの更新処理などは同一周期で回ってくるので、そのままだと
Unity側で処理されているもの(AnimationやPhysicsなど)しか速度が反映されません
なので速度変更対応したいものは Time.timeScale を掛けて処理することで対応できます
逆にUIなど影響を受けずに一定で更新したいものはそのまま処理すればOKです

シェーダーでリングを描く

タイミング合わせのリングの描画をどうしようかなーと悩んでいたのですが、
リング画像を作ってそれを拡縮するスタイルだとリングの細さもスケールに応じて変化してしまうので
シェーダーを使って描画するのを試してみました。
【Unity】 Unityのシェーダで2Dのリング(円)を描く
デモシーンへようこそ (76p~)
アンチエイリアス的なことをしたかったので、白いベースに黒い縁取りで境界をリニアに変化させるようにしました
最終的なシェーダーはこんな感じです

RingShader.shader

Shader "Custom/RingShader" {
	Properties {
		_BaseColor ("Base Color", Color) = (1,1,1,1)
		_LColor ("Low Color", Color) = (1,1,1,1)
		_MColor ("Mid Color", Color) = (1,1,1,1)
		_HColor ("High Color", Color) = (1,1,1,1)
		_EdgeColor("Edge Color", Color) = (0,0,0,1)
	    _Radius ("Radius", Range (0.0, 1.0)) = 0.3 // sliders
	    _BaseWidth ("Base Width", Range (0.0, 1.0)) = 0.2 // sliders
	    _EdgeWidth ("Edge Width", Range (0.0, 1.0)) = 0.2 // sliders
	    _BlendWidth ("Blend Width", Range (0.0, 1.0)) = 0.2 // sliders
	    _BaseAlpha ("Base Alpha", Range (0.0, 1.0)) = 0.2 // sliders
	}
	SubShader {
		
		Pass {

			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha 

			CGPROGRAM

			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			#pragma target 3.0
			
			uniform fixed4 _BaseColor;
			uniform fixed4 _LColor;
			uniform fixed4 _MColor;
			uniform fixed4 _HColor;
			uniform fixed4 _EdgeColor;
			uniform float _Radius;
			uniform float _BaseRadius;
			uniform float _BaseWidth;
			uniform float _EdgeWidth;
			uniform float _BlendWidth;
			uniform float _BaseAlpha;

			struct vStruct
			{
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
			};

			vStruct vert(appdata_base v)
			{
				vStruct o;
				o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
				o.uv = v.texcoord;
				return o;
			}

			half4 frag( vStruct i ) : COLOR
			{
				float dist = abs( _Radius * 0.5 - distance( i.uv, float2( 0.5, 0.5 ) ) );

				return float4( lerp( _BaseColor.rgb, _EdgeColor.rgb, saturate( dist - ( _BaseWidth ) ) / _BlendWidth ), ( 1.0 - saturate( ( dist - ( _BaseWidth + _EdgeWidth ) ) / _BlendWidth ) ) * _BaseAlpha );
			}

			ENDCG
		}
	}
}

キャプチャしてテクスチャとして利用する

シェーダーでリングを描いたのですが、やはり処理負荷がかかってしまうのと、
複数リングを描こうと思ったとき面倒だなーとおもったので結局テクスチャの拡縮で行うことに・・
せっかくシェーダーで描いたので利用しようとおもい、Unityで描いた絵を画像ファイル化して使用しました
UnityでRenderTextureをファイルに保存
上記のスクリプトをベースに、フォーマットをARGB32、カメラの Clear Flags をSolidColorのアルファ0にすることで、背景は抜きになるようなテクスチャを生成できました
もう少し凝ったキャプチャを行いたいときはFrameCapturerなんてものもあるみたいです

フェードアウト処理

UIなどスパッと消えるのは味気ないのでフェードさせるためのコンポーネントを作りました
こちらをUIなどに付けて消したいタイミングでFadeを呼ぶ感じで使いました

Remover.cs

using UnityEngine;
using System.Collections;

public class Remover : MonoBehaviour {

    public int FadeFrame = 10;

    private bool mIsRequestRemove = false;
    private int mCount = 0;

    private UnityEngine.UI.Image[] mImages;
    private UnityEngine.UI.Button[] mButtons;
    private UnityEngine.UI.Text[] mTexts;

	// Use this for initialization
	void Start () {

        mImages = GetComponentsInChildren<UnityEngine.UI.Image>();
        mButtons = GetComponentsInChildren<UnityEngine.UI.Button>();
        mTexts = GetComponentsInChildren<UnityEngine.UI.Text>();
	}
	
	// Update is called once per frame
	void Update () {

        if (!mIsRequestRemove) return;
        mCount++;

        CalcFade();
        if (mCount > FadeFrame) GameObject.Destroy(gameObject);
	}

    private void CalcFade()
    {
        float alpha = 1.0f - Mathf.Clamp((float)mCount / (float)FadeFrame, 0.0f, 1.0f);

        for( int i = 0; i < mImages.Length; i++)
        {
            Color temp = mImages[i].color;
            temp.a = alpha;
            mImages[i].color = temp;
            mImages[i].raycastTarget = false;
        }

        for( int i = 0; i < mButtons.Length; i++)
        {
            UnityEngine.UI.ColorBlock tempBlock = mButtons[i].colors;
            {
                Color temp = mButtons[i].colors.normalColor;
                temp.a = alpha;
                tempBlock.normalColor = temp;
            }
            {
                Color temp = mButtons[i].colors.highlightedColor;
                temp.a = alpha;
                tempBlock.highlightedColor = temp;
            }
            {
                Color temp = mButtons[i].colors.pressedColor;
                temp.a = alpha;
                tempBlock.pressedColor = temp;
            }
            {
                Color temp = mButtons[i].colors.disabledColor;
                temp.a = alpha;
                tempBlock.disabledColor = temp;
            }
            mButtons[i].colors = tempBlock;
        }

        for( int i = 0; i < mTexts.Length; i++)
        {
            Color temp = mTexts[i].color;
            temp.a = alpha;
            mTexts[i].color = temp;
        }
    }

    public void FadeRemove()
    {
        mIsRequestRemove = true;
    }
}

少し冗長ですが、開発速度を優先してフェード対応させたい種類が増えたらフェード用コードを追記する感じにしました

VisualStudioでモデルを作る

ジャンプの方向を決めるときのUIを3D空間で出したかったので、モデルを用意する必要があったのですが手持ちのモデリングソフトがない・・
そんなときに使えるのがまさかの VisualStudio でした

developcamp_vsmodel

2013あたりからfbxの読み書きができるようになっており、プリミティブ形状の組み合わせ程度のモデル生成ぐらいならできそうな感じでした
今回は円錐と円柱を組み合わせて矢印をつくり、fbxで保存 -> Unityで読み込みで問題なくいけました

AnimationCurveが便利

パラメータ調整をする際、成績による難易度上昇など、線形な変化では表現が難しいところがあるとおもいます
そのようなときに手軽に使えるのがAnimationCurveです

developcamp_animationcurve

AnimationCurve型で public なメンバを作り、インスペクタに出すとカーブの編集ができます
あとはAnimationCurve.Evaluteで対応した値がとってこれます

やりたかったこと

ここからはやりたかったけど手を出せなかったところについて

Unityちゃんアニメのイベントの設定

Unityちゃん付属のアニメにはイベントが設定されており、それをもとに表情の変更が行われています
単一のアニメ再生のときはだいたいそれでうまくいくのですが、
複数アニメのブレンドをするときには違和感のある表情変化になってしまいます
今回では走っている最中に歩きと走りのアニメをブレンドしているため、表情の切り替えがおかしな感じになってしまっています

アニメの上半身・下半身ブレンド

部分的なアニメブレンドをさせて、Tap失敗したときに上半身ダメージを再生したり、
ジャンプ中に下半身Runningをさせたりってのをしようかなと考えてました
AnimationControllerのレイヤー設定とAvatorMaskの組み合わせで実現できます

解像度ごとの設定

モバイルなど解像度が違う環境で動作させたとき、3Dオブジェクトの見え方の制御の仕方がわからなかったです
調べてみたらアスペクト比が解像度によって変化しているのを固定化すればいいみたい
Unityでアスペクト比率を固定する
モバイルだと解像度は様々なのでここの対応はしっかりと行っておきたいですね

砂の表現

砂の表現を凸凹した感じにしたいなーと思っていたのですが、
Meshを操作することでプログラムから頂点を制御することができるそうです
実は合宿前の事前調査でこのあたりはやっていたのですが、結局手つかずのままおわってしまいました・・

ラグドール

実は今回、ゲームを作るきっかけでもありました
ラグドールを遊びに組み込んだゲームは見ているだけで面白く、繰り返し遊ぶネタにもなるなと思い
ラグドールを生かせる遊びはないか考えた結果幅跳びを作ることにしました。
走りながら失敗したとき、ジャンプに失敗したとき、高いところから着地した時などに転ばせる予定だったのですが
調整がうまくいかず、結局あきらめる羽目に・・
すこし触ってみた感じ破たんしないような調整をするのが大変そうな印象でした

インスペクタが見にくい

最後、締切5分前でデバッグ用に変更していたパラメータ戻そうと弄ったら、そこが別のパラメーターで
プレゼン時にバグってたという悲しい出来事がありました
変数が増えてくるとインスペクタが次第に汚くなってきて、どれだどれだかわからなくなってきます
Unity/C# – Inspector表示の変更①
調べてみたら結構簡単に整理ができそうですね

AnimationCurveのデバッグ表示

AnimationCurveは大変便利なのですが、現在カーブのどのあたりなのかが分からなくて不便だなと感じました
調べてみたのですが、ゲーム上に表示するようなものはなさそうでした
実装自体は楽そうなので画面上に出せると調整が捗るかもしれません

合宿レポートは以上です!
最近はプライベートでコードを書くことが減ってきたので、たまに合宿とかあるとまた開発意欲が湧いてきますね
今回作ったアプリも気力があったら整えてリリースするかも?

© Unity Technologies Japan/UCL


コメントを残す

メールアドレスが公開されることはありません。