トーフメモ

主にゲーム制作

3秒でわかる「積率(モーメント)」【確率統計学】

f:id:tofgame:20190429000018j:plain

積率とは

確率分布の特徴を調べるために扱う値です!
標本と確率の積、つまり期待値を扱って定義されます。

詳しく

 \displaystyle
\mu _k =E(x^k)
上の式を「xの原点周りのk次の積率」と呼びます。

また、
 \displaystyle
\mu^{\prime} _k =E({(x-E(x))}^k)
上の式を「xの期待値周りのk次の積率」と呼びます。

積率を使うことで期待値、分散、歪度、尖度を表現できます。

期待値

 \displaystyle
\mu_1

分散

 \displaystyle
\mu^{\prime} _2=\mu_2-{\mu_1}^2

歪度

 \displaystyle
\frac{\mu^\prime_3}{{\mu^\prime_2}^{\frac{3}{2}}}

尖度

 \displaystyle
\frac{\mu^\prime_4}{{\mu^\prime_2}^{2}}-3

参考にしました

data-science.gr.jp

関連記事

tofgame.hatenablog.com
tofgame.hatenablog.com
tofgame.hatenablog.com
tofgame.hatenablog.com

3秒でわかる「尖度(せんど)」【確率統計学】

f:id:tofgame:20190429000018j:plain

尖度とは

確率分布のとがり具合です!
正規分布と比べて中心部分にデータが偏っているか、広く分散しているかで値が変わります。

尖度>0

f:id:tofgame:20190507184007p:plain
正規分布よりとがっているときに正の値になります。

尖度<0

f:id:tofgame:20190507184027p:plain
正規分布より滑らかなときに負の値になります。

 xを確率変数、 E(x)を期待値、 \sigma標準偏差として、
 \displaystyle
\frac{E(x-E(x)^4) }{\sigma ^4}-3

ここで、

 \displaystyle
\sigma = \sqrt {E((x-E(x) )^2)}

3秒でわかる「歪度(わいど)」【確率統計学】

f:id:tofgame:20190429000018j:plain

歪度とは

確率分布の対称具合です!
歪度が0に近いほどデータの確率分布は正規分布のようなきれいな分布(対称的)になります。

歪度0

f:id:tofgame:20190505230402p:plain

歪度>0

f:id:tofgame:20190505230643p:plain

歪度<0

f:id:tofgame:20190505230656p:plain

 xを確率変数、 E(x)を期待値、 \sigma標準偏差として、
 \displaystyle
\frac{E(x-E(x)^3) }{\sigma ^3}

ここで、

 \displaystyle
\sigma = \sqrt {E((x-E(x) )^2)}

3秒でわかる「頂点シェーダー」【Unity】

f:id:tofgame:20190428235628j:plain

頂点シェーダーとは

(主に)頂点の座標等を操作するシェーダーです!
Vertex Shader(バーテックスシェーダー)とも言います。
頂点シェーダー部分はHLSL言語で書かれています。

デフォルトのシェーダーを作る

「Project」上で右クリックして、 Create -> Shader -> UnlitShader と選択するとシンプルなシェーダーができます。
f:id:tofgame:20190503233442p:plain

デフォルトのUnlitShaderからFog周りを省いたものが以下のコードです。

Shader "Unlit/NewUnlitShader"{
    Properties{
        _MainTex("Texture", 2D) = "white" {}
    }

    SubShader{
        Tags { "RenderType" = "Opaque" }
        LOD 100

	Pass{
	    CGPROGRAM
	    #pragma vertex vert
	    #pragma fragment frag

	    //入力用の構造体
	    struct appdata {
               float4 vertex : POSITION; //頂点の座標
               float2 uv : TEXCOORD0; //頂点に設定されたuv座標
	    };

	    //出力の構造体
	    struct v2f {
                float4 vertex : SV_POSITION; //頂点の座標
                float2 uv : TEXCOORD0; //頂点に設定されたuv座標
            };

            //頂点シェーダー
            v2f vert(appdata v) {
	        v2f o;
	        o.vertex = UnityObjectToClipPos(v.vertex); //頂点の座標をクリップ空間での座標に変換
	        o.uv = v.uv; //uv座標はそのまま
	        return o;
            }

            sampler2D _MainTex;

            //フラグメントシェーダー
            fixed4 frag(v2f i) : SV_Target{
	        fixed4 col = tex2D(_MainTex, i.uv);
	        return col;
            }
	    
	    ENDCG
        }
    }
}

このコードから、頂点シェーダーに関わる部分を抜粋します。

頂点シェーダーに関わる部分

#pragma vertex vert 

ここで頂点シェーダーメソッドを設定しています。

struct appdata {
    float4 vertex : POSITION; 
    float2 uv : TEXCOORD0; 
};

頂点シェーダーに渡す引数を構造体で定義します。
変数のコロン(:)の後に書かれている大文字のキーワードは、「セマンティクス」といいます。
レンダリングの過程で渡される、もしくは渡すデータがあらかじめ決まっていて、どの引数にどのデータが対応しているかをここで書きます。
この構造体では入力用のセマンティクスを対応させます。
「POSITION」は座標データで、「TEXCOORD0」が頂点に対応しているuv座標です。

struct v2f {
    float4 vertex : SV_POSITION;
    float2 uv : TEXCOORD0;
};

頂点シェーダーが渡す値を構造体で定義します。
ここでは出力用のセマンティクスを対応させます。
「SV_POSITION」は座標データで、「TEXCOORD0」が頂点に対応しているuv座標です。

v2f vert(appdata v) {
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = v.uv;
    return o;
}

頂点シェーダーのメソッドです。
ここで実際の処理をします。
UnityObjectToClipPos()を使って頂点座標をクリップ空間座標へと変換します。
クリップ座標系については以下のページを参照してください。
blog.natade.net

また、ここではuv座標は特に加工せずそのまま渡しています。

そしてフラグメントシェーダーへ

頂点シェーダーでの処理が終わったら、カリングやラスタライズを経て「フラグメントシェーダー」の処理に進みます。

(参考)入出力セマンティクスの種類

docs.microsoft.com

3秒でわかる「分散」【確率統計学】

f:id:tofgame:20190429000018j:plain

分散とは

データのちらばり具合です!
分散という言葉からもなんとなくそんな感じがしますね!

詳しく

分散を \sigma ^2、確率変数を x、期待値を E(x)として、
 \displaystyle \sigma ^2 = E((x-E(x) )^2) = E(x^2)-E(x)^2 (確率変数と期待値の二乗誤差の期待値)

また、分散の平方根標準偏差といいます。
 \displaystyle \sigma = \sqrt {E((x-E(x) )^2)}

身長の分散

A~Dさんがいるとします。

f:id:tofgame:20190502015723p:plain
Aさん(140cm)
f:id:tofgame:20190502015747p:plain
Bさん(163cm)
f:id:tofgame:20190502015802p:plain
Cさん(185cm)
f:id:tofgame:20190502015916p:plain
Dさん(155cm)

この4人の身長の分散を計算しましょう。
(1) 4人の身長の平均( E(x))
 \displaystyle
\frac {140+163+185+155}{4}=160.75

(2) (1)の二乗( E(x)^2)
 \displaystyle
(160.75)^2=25840.5625

(3) 4人の身長の二乗の平均( E(x^2))
 \displaystyle
\frac {140^2+163^2+185^2+155^2}{4}=26104.75

よって分散は、
 \displaystyle
(3)-(2)=264.1875
です!

では、4人とも同じ身長だったらどうなるでしょうか?
全員140cmだとして、
(1) 4人の身長の平均( E(x))
 \displaystyle
140

(2) (1)の二乗( E(x)^2)
 \displaystyle
140^2

(3) 4人の身長の二乗の平均( E(x^2))
 \displaystyle
140^2

分散は、
 \displaystyle
(3)-(2)=0
です!

分散は値が大きければ大きいほどデータが散らばっているのがわかりますね。
今回はこれで終わりです!

3秒でわかる!「中央値」【確率統計学】

f:id:tofgame:20190429000018j:plain

中央値とは

集まりのちょうど真ん中のことです!

詳しく

確率を \displaystyle Pとして、
 \displaystyle P(x \leq a) = 1/2を満たす \displaystyle aを中央値と言います。

集まりを小さい順に並べて、小さい順(大きい順)に集まりの数の半分まで数え上げると、中央値がわかります。
集まりの数が偶数の時は集まりの数の半分が二つあるので、その二つの平均が中央値です。

また、確率変数をx、確率関数をf(x)として、
 \displaystyle 
\int |x-y|f(x)dx
を最小にする \displaystyle yも中央値になります。

年収の中央値

年収は何億何十億と、ものすごい稼いでいる人もいます。
なので、平均値(期待値)にすると、「あれ?平均でみんなこんなに稼いでいるのか・・・」となります。
中央値は「集まりのちょうど真ん中」をみるので、集まりで一番稼いでいる人などのデータが邪魔しません。
中央値は外れ値に強いのです。

実際に、平成29年度の日本人全体の平均年収は432万ですが、中央値を国税庁のデータから推測すると、300~400万の間になります。

参照

www.nta.go.jp

3秒でわかる!「カスタムコルーチン」【Unity】

f:id:tofgame:20190428235503j:plain

カスタムコルーチンとは

自分で実装するコルーチンです!

作り方

分指定で待ってくれるコルーチンを作ります。

(1)クラスを定義

public class WaitForMinutes{
}

(2)CustomYieldInstructionを継承

using UnityEngine;
public class WaitForMinutes : CustomYieldInstruction {
}

(3)keepWaitingをオーバーライド

抽象メソッドを実装しよう。

public class WaitForMinutes : CustomYieldInstruction {
    public override bool keepWaiting {
        get { return true; }
    }
}

(4)機能を作る

コンストラクタを定義しよう。

using UnityEngine;
public class WaitForMinutes : CustomYieldInstruction {
    private float waitTime; //実際の待ち時間

    public WaitForMinutes (float time) {
        waitTime = Time.realtimeSinceStartup + time * 60;
    }

    public override bool keepWaiting {
        get { return (Time.realtimeSinceStartup < waitTime); }
    }
}

(5)あとは使うだけ!

using System.Collections;
using UnityEngine;

public class Test : MonoBehaviour {
	private void Start () {
        StartCoroutine(TestCorountine());
	}

    private IEnumerator TestCorountine () {
        yield return new WaitForMinutes(5);
        Debug.Log("Waited 5 minutes.");
    }
}


シーンファイルの正体【Unity】

f:id:tofgame:20190428235106j:plain

Unityを使っているとき、ほぼ確実に使う「シーンファイル」。
拡張子は.unityで、シーンを保存すると作成されます。

けどこれ何を保存してるんだろう・・・
シーンの情報が入っているんだろうけど・・・
・・・

気になったので調べました!
今回使用したUnityのバージョンはUnity2018 3.7f1です。

シーンファイルの形式

YAML」という名前のHTMLやXMLと似たようなマークアップ言語らしいです。

SampleScene.unityはこんなかんじ

%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!29 &1
OcclusionCullingSettings:
  m_ObjectHideFlags: 0
  serializedVersion: 2
  m_OcclusionBakeSettings:
    smallestOccluder: 5
    smallestHole: 0.25
    backfaceThreshold: 100
  m_SceneGUID: 00000000000000000000000000000000
  m_OcclusionCullingData: {fileID: 0}
--- !u!104 &2
RenderSettings:

・・・
[中略]
・・・

--- !u!4 &534669905
Transform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 534669902}
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
  m_LocalPosition: {x: 0, y: 1, z: -10}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_Children: []
  m_Father: {fileID: 0}
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

結構長い(266行)

ぱっと見たところ、RenderSettingsやGameObject、Transformといった単語が見られます。
それに階層分けされている雰囲気です。

ゲームオブジェクトを新たに作成するとどうなるのか

以下の部分が追加されました。

--- !u!1 &1828928079
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 1828928080}
  m_Layer: 0
  m_Name: GameObject
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!4 &1828928080
Transform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1828928079}
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
  m_LocalPosition: {x: 0, y: 0, z: 0}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_Children: []
  m_Father: {fileID: 0}
  m_RootOrder: 2
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

これを見ると、

--- !u!1 &1828928079

の部分が始まりで、その下に、

GameObject:
Transform:

といったようなコンポーネントの情報が続いています。

Transformを変更してみた

--- !u!1 &1828928079
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 1828928080}
  m_Layer: 0
  m_Name: GameObject
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!4 &1828928080
Transform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1828928079}
  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
  m_LocalPosition: {x: 1, y: 0, z: 0}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_Children: []
  m_Father: {fileID: 0}
  m_RootOrder: 2
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}

Transform:以下のm_LocalPosition:の部分が{x: 1, y: 0, z: 0}に変わっています!

では、シーンファイルのデータを直接変えたらどうなるでしょうか?
Transform:以下のm_LocalScale:を{x: 2, y: 1, z: 1}に変えてみます!

f:id:tofgame:20190430032212p:plain
外部変更が検知され、

f:id:tofgame:20190430032348p:plain
Unity上でも変更が確認できました!

--- !u!1 &1828928079の部分はどういう意味か

!u! の後がクラスIDで、
& の後がファイル内で一意に割り振られるオブジェクトIDです。
クラスIDの一覧はこれです。
docs.unity3d.com
ここにも書いてあるとおり、自分でスクリプトを作成してアタッチするとクラスIDは114(MonoBehaviour)になるそうです。

おわりに

ある程度は直接unityファイルから編集できそう(バグ起きそうだけど)なので、うまいことシーンファイル内部を参照できれば不思議なことができそうです。
共同開発でしばしばコンフリクト起こす場所でもあるので、内部が少し把握できると対策案が組めそうですね。

参考にしました

docs.unity3d.com

C#からPythonを実行する方法【Unity】

f:id:tofgame:20190428235628j:plain
UnityとPythonをつなげてやりたいことがあります。
Python機械学習して、Unityで作ったゲームの中で使う、そんな感じのことがしたいのです。
なのでまずはPythonをどうやって実行するか、というところが課題でした。
調べてみると案外あっさり見つかったので、手順とポイントをまとめていきます!
参考にしたサイトはページ下部にリンクを張るので、そちらを見てください!

実際にやること

UnityのスクリプトからPythonの実行ファイルをたたいて結果を返す感じです。
なので、Pythonの実行環境がない人は先にそちらを構築してください。

Unity(C#)側

using System.Diagnostics;
using System.IO;
using UnityEngine;

public class CsPy : MonoBehaviour {
    //pythonがある場所
    private string pyExePath = @"(Pythonの実行ファイルが置いてある場所)\python.exe";

    //実行したいスクリプトがある場所
    private string pyCodePath = @"(実行したいスクリプトの場所)\tst.py";

    private void Start () {
        //外部プロセスの設定
        ProcessStartInfo processStartInfo = new ProcessStartInfo() {
            FileName = pyExePath, //実行するファイル(python)
            UseShellExecute = false,//シェルを使うかどうか
            CreateNoWindow = true, //ウィンドウを開くかどうか
            RedirectStandardOutput = true, //テキスト出力をStandardOutputストリームに書き込むかどうか
            Arguments = pyCodePath + " " + "Hello,python.", //実行するスクリプト 引数(複数可)
        };

        //外部プロセスの開始
        Process process = Process.Start(processStartInfo);

        //ストリームから出力を得る
        StreamReader streamReader = process.StandardOutput;
        string str = streamReader.ReadLine();

        //外部プロセスの終了
        process.WaitForExit();
        process.Close();

        //実行
        print(str);
    }
}

python.exeの場所と、実際に実行したいpythonスクリプトの場所のフルパスをコード内に書きます。
Processクラスを使用して外部のファイルを実行します。
今回は、実行したpythonスクリプトから得たストリームを文字列にしてunity側で使います。

Python

import sys #引数を得るために使用
playerSelect=str(sys.argv[1])
print( "REPLY[" + playerSelect + "]:" + "Hello,CS." )

sysライブラリを使用して、c#側から送った引数を使用します。
printした文字列が、c#側のストリームに送られます。

実行すると

f:id:tofgame:20190430011001p:plain
うまくやり取りできました!


参考にしたサイト

code.msdn.microsoft.com
qiita.com

3秒でわかる「期待値」【確率統計学】

f:id:tofgame:20190429000018j:plain

期待値とは

「起こることの数値」×「起こる確率」の合計です!
確率分布の真ん中がわかります。(外れ値がある場合感覚的に変な数値になる)

 xを確率変数、 f(x)を確率関数、期待値を E[x]とする。
期待値 E[x] x f(x)による重み付き平均として定義する。
(1)確率変数が離散値
 \displaystyle
E[x]= \sum_x xf(x)

(2)確率変数が連続値
 \displaystyle
E[x]= \int xf(x)dx

ロト6の期待値

ロト6の当選金額と確率はこんな感じです。

1等 1/6096454 2億円
2等 6/6096454 1000万円
3等 216/6096454 30万円
4等 9990/6096454 6800円
5等 155400/6096454 1000円
はずれ 0円

期待値を計算すると、
 \displaystyle
\frac {1}{6096454}×200000000+\frac {6}{6096454}×10000000+\frac {216}{6096454}×300000+\frac {9990}{6096454}×6800+\frac {155400}{6096454}×1000 \fallingdotseq 90

期待値は90でした!
ロト6は一口200円なので、仮に全部買ったら45%回収できる計算ですね。
運よくないと厳しそうですね・・・。

FGO(Fate/Grand Order)の期待値

FGOスマートフォンゲームです。
コンテンツなので、それにどのくらい価値を感じるかで損得が変わってきます。
仮に価値を割り振ってみましょう。

最高レアリティキャラ 1/100 30000円
それ以外 99/100 0円

期待値は300となります。
このコンテンツに30000円の価値を感じているとするなら、300円で当てるとお得ということです。
FGOでのガチャ一回の値段は一番安い条件で176円なので・・・
1回で引かないと損!(暴論)
・・・
ガチャ1回の価値は人それぞれです。(たぶん多くの人にとっては)もっと高くなります。