ムラサメ研究ブログ

主にゲームやプログラミングのログ

近況:PHP Laravel触ってます

なぜか今までPHPの仕事をしたことがなく
C++Java等のコンパイル言語ばっかだったし

今更PHP入門ですが、こいつがまた わかりやすい言語でして
表記がCやJavaに似てるから 慣れがある

ただし 何個か気を付けることがある

文字列の連結

+ではなく.
Perlですね

ラムダ、クロージャ

ラムダが使えるんだけど驚くことにC++でいうキャプチャがあるってこと
use() 内にキャプチャする変数を指定

変数

すべて頭に$が必要
メンバ変数やメソッドを呼ぶにも $this->method() とかく必要がある
メンバ変数を参照するときは メンバ変数名の前に$はいらない $this->menber
メンバ変数名を変数で呼ぶことができる。その時は変数名の前にも$をつける $idx = 'member'; $this->$idx; // $this->idxではなく $this->member が参照される

&

なんとPHPでは参照というのがある
関数の引数や foreachの変数は、コピーなのでそこに対して変更しても反映されないが
&をつけてやると、C++でいう参照となり値を変更することができる

===

JavaScriptなどにあるやつ
===だと型も見て(暗黙型変換をせず)比較してくれる
特に必要なのは null時チェックかな

isset、empty、is_null

細かい違いはおいておいて、コンパイル言語とは違い未定義という状態もあり得る
その時に falseを返したりする
ただしラムダのキャプチャ(use)なり、関数の引数にするときは Undefinedエラーが出るので定義しておかないとだめ
可変長引数も使えるけど・・・ねぇ・・・

Array

ちょいめんどう
配列はない。全部連想配列である
省略すると intのKey 自動インクリメントで保存されるので実質配列のように扱うことができるが
stringのKeyと同居することも出来る
また 初期化及び値を代入する際も hoge( 'key' => 'value') のように書く
ここは 慣れるしかない

Laravel

Railsライクなライブラリなので、なんとなーくRailsを知ってれば扱いやすいんじゃないかな?
Railsとの比較はほかの人に任せます。私は比較できるほどの知識はない

総括

Railsライク
文法はJava
所々 CやC++の知識があると楽なところが多い
C++Javaの人にはわかりやすい

Dev-Ops

なんとなく 調べながらメモをかく 駄文

Dev-Opsとは

Dev: デベロッパー。つまりプログラマなどの開発者
Ops: オペレータ。つまり運用

昔から両者は立場、考え方が違い衝突をしていた
開発者は、新機能の実装を早くリリースしたいが
運用者は、不具合の可能性のある新機能には慎重で既存の不具合修正を優先してほしい

開発者は、不具合の修正や新機能の1タスクが終わるたびにリリースしてほしいが
運用者は、バージョン管理や運用ミスを考え、数週間まとめてリリースしたい

不具合が発生した場合
開発者は運用のミスを疑い
運用者は開発のミスを疑う

開発者はオペレーターマニュアルを書き、運用はその通りに実行する

など、昔から色々と問題があった

一見いがみ合っているようだが、立場が違うだけで目的は同じ
ユーザーに喜んで使ってもらいたい。サービスを良くしたい

目的が同じなら、仲良くなれるはずだ

https://www.slideshare.net/jallspaw/10-deploys-per-day-dev-and-ops-cooperation-at-flickr

インフラ構築の自動化

昔は、インストールマニュアルというものがあり、その手順通りインストールしていた
当然 インストールの工数や人為的ミスがおこる
Chef、Puppet、Ansible、Docker など

バージョン管理システム共有

運用がソース管理システムを使わず、開発がビルドしたファイルのみを扱っていた時代もある
今では運用もGitなどを見て、コミットメッセージを見たり、任意のバージョンでビルドして確かめたり
コードレビューに参加したりすることも当たり前になってきている
Gitなど

ワンステップデプロイ

過去においては、手順書をみてサーバへのデプロイを手動で行っていた
当然 工数と人為的ミスが増える
現在では Jenkins、Capistrano

フューチャーフラグ

新機能、特別なイベントなどを設定ファイルでON/OFFする
新機能のデプロイを 設定ファイルの変更のみで出来る

メトリクス

過去においては開発者はあまり サーバー負荷の数値などに関心がなかった
運用が行う仕事だと思っていたからだ
運用がメトリクスを見て、サーバ負荷の問題を開発に報告してから調べはじめる
だが、今の時代は常に開発者もメトリクスを見て一刻も早く気付くべきだ
NewRelic、ApplicationInsights 等

チャット&チャットBOT

Slack、HipChat等のツールを使い
デプロイ、アラートのログを共有したり
自動化できるところは自動化していきたい

そしてその後は精神論

リスペクト

ステレオタイプで考えるな(プログラマがみんなルーズじゃない)
他人の経験や意見を尊敬する
NOとだけ言わない
隠さない

信頼

OpsはDevの機能追加を信用し、DevはOpsのインフラ変更を信用する なd

失敗にたいして健全に

失敗は誰しもがおこす。ミスを責めない

非難を避ける

失敗を責めない
同じ失敗がおこらないよう、建設的な考えをすべき

まとめ

過去においては DevとOpsは立場が違い対立することもあった
そのこと自体は、お互いにけん制しあいミスを防ぐのに役立ってた面があるかもしれないが
今の流行りは、DevとOpsが仲良く協力的になる事

Opsはソース管理を使ったり、コードレビューを行ったり、過去のバージョンを自分でビルドし調査したり
もっと Devに歩み寄る

Devはメトリック等を自分たちも関心をもち、Opsに歩み寄る

自動化できる部分は積極的に自動化し、工数と人為的なミスを減らす

よく使われるツールとしては
構成ツール:Docker、Chef、Vagrant、Ansible、Puppet・・・
ソース管理:Git(Github、Gitlab)
CI:Jenkins、CirculerCI
チャット:Slack、HipChat
メトリクス:NewRelic、ApplicationInsights

そして 人間間の リスペクト、信頼、非難しないなどがとても重要

それに加え、リーン性、計測なども重要視されている

DevとOpsは仲良くなる。むしろ 仕事や立場がオーバーラップする
時代の流れはそうなっているようだ

Docker修行中

はじめに

とりあえず、勉強中の殴り書きをすることに

Dockerとは

コンテナ仮想技術と言われている
例えば、クラウドであれば物理的に異なるハードウェア&OSに対してTCP等で接続するもの
VirtualMacheneは物理的に同じハードウェア上に異なるOSを動かす
Dockerはコンテナとしてそれぞれのサービスを立ち上げる

VMと何が違うかを説明するのがまだ難しいんだけど
VMはゲストOSをインストールしその中にすべてのサービスをインストールするが
Dockerは サービス単位でコンテナを作り起動する

とりあえず、Dockerのほうが軽いと思っていいかな

ではなぜDockerを使うかというと
今までの開発では、ローカルに環境を作って開発をしていた
新人が入れば環境構築マニュアルを見てセットアップ行い、たいてい失敗して数日かかる
特に WindowsだけでなくMacで開発することも多い
それらの環境構築を、仮想コンテナで環境を整えるので、セットアップが速い

Dockerの種類

Dockerは基本的に Linuxカーネルでしか動かない
そのため、WindowsでもMacでも まずは VMの上にLinuxを動かし、そのうえでDockerを動かす必要がある

ところが Windows10 Pro 64bitでは、Hyper-V上に直接Dockerを動かす事が出来る
マジすばらしい!

ところが WindowsではBashが動かないため、Macや DockerWindowsが動かないWindowsでも開発する場合
シェルスクリプト対策をする必要がある

Bash on Windowsは便利だが、こちらもHyper-Vで動いているため、ホストのDockerを直接見ることが出来ない
そのため Bash on Windows上にDockerをインストールし、向き先をホスト上に変更したりすれば多分行けると思う

あるいは Window用に バッチファイルやPowerShellも作るなど

Docker概要

まずDockerには イメージ、コンテナ、ボリュームがある
イメージはコンテナのイメージファイルとなっている
コンテナはイメージを起動した際に作成され、イメージの一次的な情報を持っている
ボリュームは、データベースの中身など永続的に持っておきたいデータ
docker ps
docker images
docker volume ls
でそれぞれ リスト表示

docker pull イメージ名
で、イメージを DockerPubより落として来る

docker run -it イメージ名
でイメージからコンテナを起動

docker start イメージ名
で停止したコンテナを再開する

Ctrp+P、Q でコンテナを起動したままホストに戻る
exitで抜けた場合はコンテナは停止する

docker exec -it コンテナ名 /bin/bash
等で 起動中のコンテナにログインする

docker commit コンテナ名 イメージ名
で、コンテナからイメージを作成する

また、コンテナを立ち上げる際に、ボリュームやポートフォワードを指定する

Build

上記のように コンテナ内で環境を構築し commitでイメージに焼き付けてイメージを配布してもよいが
インストール手順ファイルを作成し、そこからイメージを作成するほうが便利である

まずはDockerfileを作成する。ここにはインストール手順を書く(Chefのようなもの)
Dockerfileを書いた後に buildコマンドを使いイメージを作成する
docker build -t イメージ名:タグ名 . で作成できる

Dockerfileの書式は後程?

docker-compose

Dockerは通常複数のコンテナを組み合わせて使う
たとえば アプリケーション、RDB、NoSQL、バッチ・・・
と複数のコンテナを立ち上げる必要がある
この際に、全部を手作業で立ち上げるのも大変だし、それぞれボリュームやポートフォワードオプションも設定する必要がある
もちろん シェルスクリプトで全部書けば簡単だが、docker-composeを使えば簡単になる

docker-composeを使えば、複数のコンテナを順番に起動することが出来る。ボリュームやポートフォワード、ホスト名の設定等ができる

docker-compose build で、全てのコンテナイメージ作成

docker-compose up ですべてのコンテナを立ち上げる

docker-compose down で すべてのコンテナを落とす

docker-compose run 名前 で 1つのコンテナを立ち上げる

その他いろいろと便利

UNITY メニュー拡張 part2 値を保存する

はじめに

UNITYのメニュー拡張をし Windowの表示は出来ました
ところが、このままでは一度ウインドウを閉じたあと再び開いても設定した値がデフォルトに戻ります
前回設定した値が残っていて欲しい時は多いと思うので、その設定です

方針

EditorWindowの値をファイルに保存すれば良いが、UNITYにはそれを補助する機能がある
ScriptableObjectを継承していれば簡単に yamlの形でAssetとして保存出来る

アセット作成

まず保存するためのAssetを作る必要があるが、おそらくコードで作成させることも出来るとおもうが
エディタ拡張でぱぱっとやってみる

まず保存するデータのクラスを ScriptableObjectを継承し作成する

[CreateAssetMenu]
public class TestObject : ScriptableObject
{
    [SerializeField] public  string hoge_;
}

[CreateAssetMenu] のアトリビュートをつけると、メニューの Asset->Create に表示されるようになる
つまり メニューから、保存先のAssetを作成する

ScriptableObjectを継承するのはSerializeのためである
今回は string hoge_ を保存する

とりあえず上記で 保存するオブジェクトは出来た

シリアライズ&デシリアライズ

先ほど作った TestObjectを使い シリアライズ、デシリアライズを行う

まず、アセットが読みこまれてない場合は読みこむ
if (this.data == null) { this.data = LoadData(); }

中身は、アセット一覧より TestObjectのものを探す

そして、データのアクセスは TestObjectを経由する

EditorGUI.BeginChangeCheck();
EditorGUI.EndChangeCheck();
にて、GUIの値が変更された時のみ、Assetに書き込む
data.hoge = hoge;
EditorUtility.SetDirty(this.data);

public class TestWindow : EditorWindow
{
    [SerializeField] private TestObject data;


    [MenuItem("Tools/Test")]
    public static void ShowWindow()
    {
        EditorWindow.GetWindow(typeof(TestWindow));
    }

    void OnGUI()
    {
        if (this.data == null) { this.data = LoadData(); }

        EditorGUI.BeginChangeCheck();

        string hoge_ = data.hoge_;
        hoge_ = EditorGUILayout.TextField("HOGE", hoge_);

        if (EditorGUI.EndChangeCheck()) 
        {
            data.hoge_ = hoge_;
            EditorUtility.SetDirty(this.data); 
        }

    }
    static TestObject LoadData()
    {
        return (TestObject)AssetDatabase.FindAssets("t:ScriptableObject")
           .Select(guid => AssetDatabase.GUIDToAssetPath(guid)) 
           .Select(path => AssetDatabase.LoadAssetAtPath(path, typeof(TestObject))) 
           .Where(obj => obj != null) 
           .FirstOrDefault(); 
    }

}

これで簡単だが、EditorWindowの値をシリアライズさせることができる

UNITY メニュー拡張 part1

コード
https://github.com/YukiMiyatake/UnityLesson/tree/MenuItem_1

はじめに

UNITYでは、インスペクターやメニューを簡単に拡張できる
エディタ拡張により開発効率も大幅に上げる事が可能である
面倒でもこの手の機能は初期に作っておけば、その後の開発期間が大幅に短縮される事が多い
そのため、ぜひとも覚えておきたい

メニュー拡張基本

f:id:murasame-labo:20171015190149p:plain 上記のように メニューにオリジナルの項目を追加できる
メニューは階層化出来、メニュー選択でウインドウ表示することが多い
まず、インスペクタやメニュー等の拡張を エディタ拡張と呼び
Editorフォルダ以下にスクリプトを置く必要がある

メニューの場合は クラスメソッド(static関数)に MenuItemアトリビュートをつければいい

Assets/Menu/Editor/TestMenu.cs

using UnityEngine;
using UnityEditor;

public class TestMenu
{
    [MenuItem("Test/Menu %a")]
    public static void ShowMenu()
    {
        Debug.Log("ShowMenu");
    }
}

MenuItemの第一引数に メニューの階層を指定する。
また、文字列に %a などを入れると、ショートカットキーを設定できる
そのあたりの詳細は是非マニュアルを見て欲しい

Unity - スクリプトリファレンス: MenuItem

非常に簡単にメニュー拡張が出来た

MenuItemの引数

MenuItemは3つの引数をもてる
第一引数はString型。メニューの項目名で、ショートカットキーも設定できる
第三引数はint型。表示の優先度

問題が第二引数
同じ名前のメニュー項目がある場合、メニュー表示時に第二引数がtrueのものが先に呼ばれ
falseを返すとそのメニュー項目はDisableになり、trueの場合はEnableになりクリックするともう一つの関数が呼ばれる
つまり メニュー表示時にValidationを行う事ができる

例えば、選択中のオブジェクトにScriptなりコンポーネントを自動で追加するメニュー拡張をしようと思った場合
GameObject以外を選択していたら 無効にすることができる

また、MenuItemの関数には、MenuCommandという引数を取る事が可能
調べたところ、このMenuCommandは Hierarchyの ルートが取れるようだ。まだ何に使うかわかってない。再調査が必要
Selection.activeObject で、現在選択中のオブジェクトが取得できる

ので、今回は下記のものを作る
Test/AddObject %b メニューを作り、選択しているものがGameObject以外は無効にする
GameObjectの場合は
MenuCommandという名前のGameObjectを MenuCommand.contextを親にして作成
NewGameという名前のGameObjectを Selection.activeObjectを親にし作成し RidgedBodyをつける
共にUndoに登録(これをしなければ Ctrl+Zで取り消せない)

    [MenuItem("Test/AddObject %b", true)]
    static bool AddObjectValidate()
    {
        // GameObjectか?
        return Selection.activeObject.GetType() == typeof(GameObject);
    }

    [MenuItem("Test/AddObject %b", false)]
    static void AddObject(MenuCommand command)
    {
        GameObject objMenuCommand = new GameObject("MenuCommand");
        // Undo登録
        Undo.RegisterCreatedObjectUndo(objMenuCommand, "Test_AddObject " + objMenuCommand.name);
        GameObjectUtility.SetParentAndAlign(objMenuCommand, command.context as GameObject);


        GameObject obj = new GameObject("NewGame");
        GameObjectUtility.SetParentAndAlign(obj, Selection.activeGameObject );

        // Undo登録
        Undo.RegisterCreatedObjectUndo(obj, "Test_AddObject " + obj.name);
        Undo.AddComponent<Rigidbody>(obj);

    }

f:id:murasame-labo:20171016053405p:plain

実行の結果、MenuCommandは Hierarchyルートに、NewGameは選択したGameObject(ここではTest)の下に出来た
なお GameObject以外を選択した場合には MenuがDisableする事も確認できた

UNITYシェーダー研究 Vertexライティング?

コード
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_7

はじめに

前回まではピクセルシェーダにてライティング計算をしていました
これはクオリティの高いライティングになりますが、少し処理が重くなるので
ディフューズカラー、スペキュラを頂点シェーダで計算しようという計画

シェーダー入出力の説明

今まで説明省略してたのを大雑把に
頂点シェーダ(vert)、フラグメントシェーダ(frag)には引数がある

頂点シェーダの引数は構造体で表現し、頂点ごとのパラメータが入ってくる
例えば ポジション、法線、UVなど

そして頂点シェーダの返り値も構造体で、頂点毎の情報を設定する

そして 頂点毎にフラグメントシェーダに その時 ポジション(SV_POSITION)以外は線形補完されます
UVが保管され、テクスチャマッピングが正しく出来るのはわかると思うが
法線なども補完され、ライティングがスムースになります

シェーダ引数は、型のほかにセマンティクスがあり、目的別に指定します
細かい事は説明しません

たとえば前回は

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

    struct v2f
    {
        float4 vertex : SV_POSITION;
        float4 vertexW: TEXCOORD0;
        float2 uv     : TEXCOORD1;
        float3 normal : TEXCOORD2;
    };

頂点シェーダの引数は ポジション、法線、UV座標
フラグメントシェーダ引数は ポジション、ワールド座標でのポジション、UV、法線

フラグメントシェーダ引数はピクセルごとに補完される

方針

1個目のライト(ForwardBase)は現状通りのピクセルでのライティングを行う

2個目以降のライト(ForwardAdd)は、頂点シェーダでライト計算を行い ピクセルシェーダでは補完された値を使う
少し精度はわるくなるが、計算量は削減されるはずである

具体的には ディフューズの値を補完することができる
スペキュラに関しては補完することも可能であるが、大きくクオリティが落ちる可能性がある
今回は例として スペキュラも補完して軽くしよう

コード

具体的には ディフューズとスペキュラの計算を頂点シェーダに持っていく
ディフューズとスペキュラの値を フラグメントシェーダに渡す

構造体は下記になる

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

    struct v2f
    {
        float4 vertex : SV_POSITION;
        float2 uv     : TEXCOORD1;

        float3 diffuse : TEXCOORD2;
        float3 specular : TEXCOORD3;
    };

頂点シェーダの入力に変更なし

フラグメントシェーダの入力は
仕様にて vertexは削れない
ワールド座標のポジションは不要 法線も不要

ディフューズ値とスペキュラ値を追加

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        float4 vertexW = mul(unity_ObjectToWorld, v.vertex);

        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        float3 normal = UnityObjectToWorldNormal(v.normal);


        float3 L = normalize(_WorldSpaceLightPos0.xyz);
        float3 V = normalize(_WorldSpaceCameraPos - vertexW.xyz);
        float3 N = normal;
        float3 H = normalize(L + V);
        float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i);
        float3 NdotL = dot(N, L);

        o.diffuse = (NdotL*0.5 + 0.5) * lightCol;
        o.specular = pow(max(0.0, dot(H, N)), _Spec1Power) *  _Spec1Color.xyz * lightCol;  // Half vector

        return o;
    }

    float3 frag(v2f i) : SV_Target
    {

        // texture albedo
        float4 tex = tex2D(_MainTex, i.uv);

        return i.diffuse * tex + i.specular;
    }

フラグメントシェーダで計算していたディフューズとスペキュラを頂点シェーダで計算
フラグメントシェーダでは 補完されたディフューズとスペキュラをそのまま利用する

通常
f:id:murasame-labo:20170925004107p:plain

軽量化 f:id:murasame-labo:20170925004122p:plain

心配になるぐらい 変わらなかった(心配

UNITYシェーダ研究 複数ライトの影響

コード
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_6

前回まで

前回はAmbientも入れて だいたいライティング出来た
ただ、複数ライトを置いても1つ目のライトの影響しか受けない
ので、複数ライトの影響をうけるようにする

ライトの強さ

複数ライト対応するまえに、今までのライトではライトカラーの影響はうけるが
ライトの強さの影響が入っていないので追加する

ライトの種類により減衰しないライト(DirectionalLightなど)と 減衰するライト(PointLight等)があり
それらをまとめて計算するマクロが存在するので それを使う

AutoLight.cginc をIncludeし、LIGHT_ATTENUATION(i) でライトの強さが取得できる iはピクセルデータ

それをライトカラーに乗算することで、強さを反映したライト色が計算できる

float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i);

ライトカラーは、ディフューズとスペキュラに影響するので、それぞれに乗算する

float3 diffuse = (NdotL*0.5 + 0.5) * lightCol;

float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) *  _Spec1Color.xyz * lightCol;  // Half vector

複数ライトに対応

UNITYでは、キャラに最も影響の強いライトは LightMode=ForwardBaseで計算するが
その他のライトは LightMode=ForwardAddで入ってくる
ので、ForwardAddのパスを増やせばいい

シェーダーの中身は 今回はほぼ、ForwardBaseのものをコピーする
ただし、Ambientは1回しか計算してはいけないので ForwardAddでは計算行わない事と
BlendModeを One Oneなどにしなければ、合成されない

BlendMode設定なし
f:id:murasame-labo:20170924035237p:plain

メインライトのみ
f:id:murasame-labo:20170924035250p:plain

ライト2個(赤ライト追加)
f:id:murasame-labo:20170924035334p:plain()

複数ライトが反映されるようになった