UnityとWebSocket

ここ数日、UnityでWebSocketを使ってみたので分かったことを箇条書きメモとしてまとめておきます。

  • WebSocketはTCP上で動くのでコネクションが接続された、または切断されたという情報を、サーバー側、クライアント側でそれぞれ受け取れる。
  • Unityで使う場合は、「websocket-sharp」というC#用のライブラリを使うと良さそう。
  • 「websocket-sharp」ではバイナリデータも送れるみたい。

Unityで「websocket-sharp」を使って試したこと。

  • Unityから書き出したアプリが、WebSocketサーバーとしても使える。(そうするとNode.jsも必要なくなる)
  • 上記のUnity製WebSocketサーバーはPC用スタンドアローンでも、Androidアプリでも動いた。(iPhoneは試してない)
  • Unity製のWebSocketクライアントも、PC用スタンドアローンでも、Androidアプリでも動いた。(iPhoneは試してない)
  • Node.jsのサーバーと、Unity製のWebSocketクライアントも接続確認できた。

これは使える!

Unity ML-Agents Toolkitを触って機械学習を体験してみる

Unityで機械学習が使えるようになる「Unity ML-Agents Toolkit」を試してみました。
使用したML-Agentsのバージョンは0.5.0です

今回は下のアニメのように、転がってボールをバッテングで打ち返す部分を学習させてみましたのでML-Agentsをさわる際にポイントになる部分をまとめたいと思います。

学習回数: 10万回

学習回数: 1万回

学習回数: 千回

学習回数別の学習結果です。回数を重ねるごとに上達していますね。
バットの振る前に一度反対側に振りかぶるところまで学習しているところなど面白いです!

設定していった箇所を、参考にさせていただいた下記サイトにあった、アクション、ステート、リワードの順に説明していきます。
 ・【Unity】Unityで機械学習する「ML-Agent」を色々と試して得た知見とか - テラシュールブログ

アクション

機械学習AIからの指示されるアクションは、今回の場合はfloat値が1つのみで、バットを回転させる AddTorque() に使う数値に使用しています。
下の方にあるソースコードでいう vectorAction[0] です。
vectorAction[0] に渡される数値は-1~1の範囲が来るみたいです。

vectorAction の配列の個数を増やしたい場合は以下の赤枠のところで設定します。

ステート

観測していく値の設定ですね。
今回は以下の9つを観測させるようにしました。

 ・ボールの localPosition.x
 ・ボールの localPosition.z
 ・ボールの velocity.z
 ・バットの localRotation.x
 ・バットの localRotation.y
 ・バットの localRotation.z
 ・バットの velocity.x
 ・バットの velocity.y
 ・バットの velocity.z

以下、アクションとステートを設定したソースです。

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

public class BattingAgent : Agent {

    public GameObject ball;
    public GameObject bat;
    private Rigidbody ballRb;
    private Rigidbody batRb;
    public BattingArea area;

    public override void InitializeAgent()
    {
        ballRb = ball.GetComponent<Rigidbody>();
        batRb = bat.GetComponent<Rigidbody>();
    }

    public override void CollectObservations()
    {
        AddVectorObs(ball.transform.localPosition.x);
        AddVectorObs(ball.transform.localPosition.z);
        AddVectorObs(ballRb.velocity.z);
        AddVectorObs(bat.transform.localRotation.x);
        AddVectorObs(bat.transform.localRotation.y);
        AddVectorObs(bat.transform.localRotation.z);
        AddVectorObs(batRb.velocity);
    }

    public override void AgentAction(float[] vectorAction, string textAction)
    {
        float forceY = Mathf.Clamp(vectorAction[0], -1f, 1f) * 10000f;

        bat.GetComponent<Rigidbody>().AddTorque(new Vector3(0, forceY, 0),ForceMode.Force);
    }

    public override void AgentReset()
    {
        area.set_bat();//バットの位置初期化
        area.start_throw();//ボールを投げる
    }
}

リワード

報酬の設定です。今回は以下のようにしました。

 ・ボールがバットに当たるとちょっとだけプラス報酬
 ・ボールが緑のゾーンに当たるとプラス報酬
   ・さらにボールの勢いが強いと追加でプラス報酬
 ・緑のゾーン以外に飛んでいくとマイナス報酬

以下、報酬を設定したコードです。(ボールにアタッチしたクラスの該当箇所)

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("hitWall")) {

            float velocityZ = GetComponent<Rigidbody>().velocity.z;

            if (velocityZ < -5) {
                agent.SetReward(1f);

            } else if (velocityZ < -3.5) {
                agent.SetReward(0.8f);

            } else if (velocityZ < -2) {
                agent.SetReward(0.5f);

            } else {
                agent.SetReward(0.3f);
            }

            agent.Done();

        } else if (collision.gameObject.CompareTag("outWall")) {

            agent.AddReward(-1f);
            agent.Done();

        } else if (collision.gameObject.name== "bat") {

            agent.SetReward(0.1f);
        }
    }

感想

最初はボールをコロコロ転がすタイプじゃなくて、普通のピッチングみたいにボールを空中に投げるタイプのものを作ってたのですが、最初のテストには複雑すぎたみたいで、学習回数30万回でもうまくバットでボールを前へ飛ばすところまでもっていけませんでした。(報酬の与え方やパラメータ設定のコツが分かってくるとうまくいくんじゃないかとは思いますが)、まずは学習がうまくいくか試したかったので今回のコロコロタイプに変更しました。

あと、最初はPythonを編集する必要があるのかなーと思っていたのですが、Pythonのインストールは必要ですけど、Tensorflowとのやりとりは全てML-Agent側で用意されているので、Unity用のC#をいじるのみで済みました。

画像をゆらゆらさせるシェーダー [Unity]

シェーダーをいじる練習で、下のGIFアニメみたいな動きをするやつを作ってみました。

最初、Unityの「Sprite/Default」シェーダーを複製していじるにはどうするんだろうと思ってたら、下記の記事でUnityで標準で使えるシェーダーの取得方法が説明されていました。(良記事!)

 ・uGUIのシェーダーを改造してシェーダーを練習…のススメ

Unity 2017.2.2 用のビルトインシェーダーをダウンロードして、「DefaultResourcesExtra/Sprites-Default.shader」をUnityのプロジェクトにインポートして開いたところ、編集したい箇所にインクルード先も含まれていたので、インクルード先「CGIncludes/UnitySprites.cginc」のコードもひとまとめにしました。

そして編集し終わったのが以下のコードになります。

Shader "Sprites/Sin"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
        [MaterialToggle] PixelSnap ("Pixel snap", Float) = 0
        [HideInInspector] _RendererColor ("RendererColor", Color) = (1,1,1,1)
        [HideInInspector] _Flip ("Flip", Vector) = (1,1,1,1)
        [PerRendererData] _AlphaTex ("External Alpha", 2D) = "white" {}
        [PerRendererData] _EnableExternalAlpha ("Enable External Alpha", Float) = 0

        _SinWave("SinWave", Range(0, 1)) = 0.2
        _SinWidth("SinWidth", Range(0, 1)) = 0.5
        _SinSpeed("SinSpeed", Range(0, 1)) = 0.2
        _SinColorDistant("SinColorDistant", Range(0, 1)) = 0.2
    }

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "IgnoreProjector"="True"
            "RenderType"="Transparent"
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
        {
        CGPROGRAM
            #pragma vertex SpriteVert
            #pragma fragment SpriteFrag
            #pragma target 2.0
            #pragma multi_compile_instancing
            #pragma multi_compile _ PIXELSNAP_ON
            #pragma multi_compile _ ETC1_EXTERNAL_ALPHA

			#include "UnityCG.cginc"

			#ifdef UNITY_INSTANCING_ENABLED

				UNITY_INSTANCING_CBUFFER_START(PerDrawSprite)
					// SpriteRenderer.Color while Non-Batched/Instanced.
					fixed4 unity_SpriteRendererColorArray[UNITY_INSTANCED_ARRAY_SIZE];
					// this could be smaller but that's how bit each entry is regardless of type
					float4 unity_SpriteFlipArray[UNITY_INSTANCED_ARRAY_SIZE];
				UNITY_INSTANCING_CBUFFER_END

				#define _RendererColor unity_SpriteRendererColorArray[unity_InstanceID]
				#define _Flip unity_SpriteFlipArray[unity_InstanceID]

			#endif // instancing

			CBUFFER_START(UnityPerDrawSprite)
			#ifndef UNITY_INSTANCING_ENABLED
				fixed4 _RendererColor;
				float4 _Flip;
			#endif
				float _EnableExternalAlpha;
			CBUFFER_END

			// Material Color.
			fixed4 _Color;

			struct appdata_t
			{
				float4 vertex   : POSITION;
				float4 color    : COLOR;
				float2 texcoord : TEXCOORD0;
				UNITY_VERTEX_INPUT_INSTANCE_ID
			};

			struct v2f
			{
				float4 vertex   : SV_POSITION;
				fixed4 color    : COLOR;
				float2 texcoord : TEXCOORD0;
				UNITY_VERTEX_OUTPUT_STEREO
			};

			v2f SpriteVert(appdata_t IN)
			{
				v2f OUT;

				UNITY_SETUP_INSTANCE_ID (IN);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);

			#ifdef UNITY_INSTANCING_ENABLED
				IN.vertex.xy *= _Flip.xy;
			#endif

				OUT.vertex = UnityObjectToClipPos(IN.vertex);
				OUT.texcoord = IN.texcoord;
				OUT.color = IN.color * _Color * _RendererColor;

				#ifdef PIXELSNAP_ON
				OUT.vertex = UnityPixelSnap (OUT.vertex);
				#endif

				return OUT;
			}

			sampler2D _MainTex;
			sampler2D _AlphaTex;
			float _SinWave;
			float _SinWidth;
			float _SinSpeed;
			float _SinColorDistant;

			float _wave;
			float _speed;
			float _width;
			float _clrDis;

			float2 posColor(float2 inUV, float n)
			{
				return inUV + float2(sin(inUV.y *_wave + _speed + _clrDis * n) * _width, 0);
			}

			fixed4 SampleSpriteTexture (float2 uv)
			{
				fixed4 color = tex2D (_MainTex, uv);

			#if ETC1_EXTERNAL_ALPHA
				fixed4 alpha = tex2D (_AlphaTex, uv);
				color.a = lerp (color.a, alpha.r, _EnableExternalAlpha);
			#endif

				return color;
			}

			fixed4 SpriteFrag(v2f IN) : SV_Target
			{

				fixed4 color = fixed4(0, 0, 0, 0);

				float2 inUV = IN.texcoord;

				_wave = _SinWave * 100;
				_speed = _Time.y * _SinSpeed * 20.0;
				_width = _SinWidth * 0.2;
				_clrDis = _SinColorDistant * _SinWidth * 5;

				if(_SinColorDistant==0){//カラーチャンネルを分けない

					float mysin = sin(inUV.y *_wave + _speed) * _width;
					color = tex2D(_MainTex, inUV + float2(mysin, 0));

				}else{//カラーチャンネルを個別に設定

					color.r = tex2D(_MainTex, posColor(inUV, 2)).r;
					color.g = tex2D(_MainTex, posColor(inUV, 1)).g;
					color.b = tex2D(_MainTex, posColor(inUV, 0)).b;
					color.a = (
						tex2D(_MainTex, posColor(inUV, 2)).a+
						tex2D(_MainTex, posColor(inUV, 1)).a+
						tex2D(_MainTex, posColor(inUV, 0)).a
					)/3;

				}

				color *= IN.color;
				color.rgb *= color.a;

				return color;
			}

        ENDCG
        }
    }
}

シェーダーのインスペクタに表示される4つのスライダーの意味
 ・SinWave : ウェーブの縦の間隔
 ・SinWidth : 揺れ幅
 ・SinSpeed : 揺れる速度
 ・SinColorDistant : 各カラーチャンネルの間隔

透過部分を含む画像の注意点
 ・GIFアニメ上部の黒フチの円画像はサイドの端が切れないように余白を多めに取っています。
 ・さらに完全な透過部分(おおまかなブロック)はシェーダーが処理を省略しているような感じなので、その部分は画像がないものとみなされてしまいました。(アニメーション時にフチが切れてしまいました。)その対策として、透過PNGの背景を1/255くらいの透過度にしています。

画像やフォントの参照先 [Unity]

uGUIのImageコンポーネント等の画像参照は何を参照しているのかを調べてみました。

下記画像のようにプロジェクトを2つ作ります。

それぞれにimgフォルダを作成し、それぞれのフォルダにishi.pngを入れます。

プロジェクト1の方のmain1シーンで上記画像のようにuGUIのImageコンポーネントで画像を表示させます。

プロジェクト1の方のmain1シーンをプロジェクト2に複製します。

プロジェクト2の方のmain1シーンを開くと画像は表示されず、Souce Imageが “Missing” となってしまいます。

画像のディレクトリパスは同じなのになんでだろうと思い、
いろいろ試していたら、画像と同じディレクトリに作成されるmetaファイルのguidを参照して画像を探しているということが分かりました。

つまり、画像とセットになっているmetaファイルさえ同じなら、ディレクトリパスが変わっても参照が切れないということみたいです。
上記テストの場合は、metaファイルが違うので、”Missing” となってしまいました。

もし、別のプロジェクトで共通の画像やフォントや音声ファイル等を使っていて、後々はプロジェクトをガッチャンコしたいという場合は、あらかじめ共通の画像やフォントや音声ファイル等のmetaファイルを共有しておくと、ガッチャンコ作業がスムーズになりそうです。

むしろ、metaファイルさえ共有されていれば、それぞれのプロジェクトでは同じ画像ファイル名でダミーの画像を使っていてもいいということですね。

影をベタ塗りするシェーダー [Unity]

Unityのシェーダーのしくみを理解しようと思いまして、練習として影をグラデーションではなく1色のベタ塗りで表現するシェーダーを作ってみました。

上記はキャラや地面に今回作ったシェーダーを当てています。

コードは以下の感じです。

ライティングの部分はこちらの記事を参考にさせていただきました。ありがとうございます!
 ・シェーダー入門したくないけど書いてみたい その6 ライティング編 - 波打際のブログさん

Shader "Custom/SolidColor" {

	Properties{
		_Color("Color", Color) = (1,1,1,1)
		_MainTex("Albedo (RGB)", 2D) = "white" {}
		_Parting("Shade Parting", Range(0,1)) = 0.5
	}

	SubShader{
		Pass{
			Tags{ "RenderType" = "Opaque" "LightMode" = "ForwardBase" }

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fwdbase

			#include "UnityCG.cginc"
			#include "AutoLight.cginc"

			fixed4 _Color;
			sampler2D _MainTex;
			half _Parting;
			uniform fixed4 _LightColor0;

			struct v2f {
				float4 pos      : SV_POSITION;
				float3 lightDir : TEXCOORD0;
				float3 normal   : TEXCOORD1;
				float2 uv:TEXCOORD2;
				LIGHTING_COORDS(3, 4)
			};

			v2f vert(appdata_base v, float2 uv : TEXCOORD2) {
				v2f o;

				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.lightDir = normalize(ObjSpaceLightDir(v.vertex));
				o.normal = normalize(v.normal).xyz;
				o.uv = uv;

				TRANSFER_VERTEX_TO_FRAGMENT(o);
				TRANSFER_SHADOW(o);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target{

				fixed atten = LIGHT_ATTENUATION(i);
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT;
				fixed4 lightColor = _LightColor0 * saturate(dot(i.lightDir, i.normal));

				fixed4 c = tex2D (_MainTex, i.uv) * _Color;

				fixed3 shade = (0.5 * lightColor * atten) + ambient;
				fixed f = (shade.r < _Parting) ? -0.08 : 0;

				c.rgb = c+ fixed3(f, f, f);

				return c;
			}
			ENDCG
		}
	}
	Fallback "Diffuse"
}

球体を使った方が分かりやすいので、団子のモデルで解説しようと思います。

上記はUnity5のデフォルトで用意されている「Standard」シェーダーです。

今回作ったシェーダーに変更すると上記のようになります。

どんな処理を行っているかといいますと

まずは裏で、モデルにグレーの色を塗って影がどんな感じにでるのかを調べます。(ここは実際には描画しません。)

一定の明るさに達しない箇所がベタ塗りの影になります。

次に、上記のような影がまったく付いてない状態のものに、先ほど計算したベタ塗りの影を足してあげるということを今回のシェーダーで行っています。

後、影の割合を個別調整できるように、上記の「Shade Parting」をインスペクタでいじれるようにしています。

今回シェーダーのことを調べてみて、裏でこんな処理をやってたのかと(なんとなく)分かってスッキリしました。

1 2 3 4 5 6 ... 12

ABOUT

MY APPLI

サッカーピープル
サッカーピープル

操作のしやすさ最優先の
自由自在なサッカーゲームです
iOS版 / Android版

コロッコトロッコ
コロッコトロッコ

石ころのコロッコを転がしながら
サラリーマンを会社へ運ぶゲームです
iPhone版 / Android版

CATEGORIES

ARCHIVES

TAGS

3Dプリント AIR ANE Arduino Away3D client work DIY Flash Funnel JavaScript Kinect LED openFrameworks PhotoShop Progression Qubicle Unity Xcode アプリ インターバル撮影 カメラ キャラ ゲーム制作 コロッコトロッコ スクリーンセーバー デバッグ フットサル ミニマル ランニング レーザーカッター 事務所 低山部 作品 動物 塗装 展示会 文字 料理 福岡てら子 美食家 街ぶらり 道具 鉄道 電子工作

FEEDS

SNS

--

Arduinoをはじめよう
Arduinoをはじめよう

電子工作が未経験の人にも
分かりやすい入門書

Arduinoをはじめようキット
Arduinoをはじめようキット

上の書籍の内容が
一通り試せるツールセット

Prototyping Lab ―「作りながら考える」ためのArduino実践レシピ
Prototyping Lab

「Arduinoをはじめよう」より
深く掘り下げた内容の実践書

Make: Electronics
Make: Electronics

電子工作の基礎から実践まで