読者です 読者をやめる 読者になる 読者になる

波打際のブログさん

主に、プログラミング備忘録など。

俺のBEMは間違っているがわかりやすい

CSS SCSS SASS SLIM BEM HTML WEB

はじめに

 私はデザイナーではないです、cssを全く理解してないエンジニアです。そんなエンジニアが最近scssでBEMを使う時に主張したいことがあったのですが、ネット上にそんな記事はなく [要出典] とか [独自研究] とかつけられそうなので主張しておきます。

前提としてBEMは神

 Block-Element-Modifier の概念を作ることで、統一された明快なDOM構造を定義することが出来ます。

バリデーション結果に応じてアイコンを切り替える例

form.slim
.form
  .form__field
    .form__field__status
    input type='text'
form.scss
.form {
  .form__field {
    .form__field__status {
      background-image : url('ok.png');
    }
    &.form__field--ng {
      .form__field__status {
        background-image : url('ng.png');
      }
    }
  }
}

BEMに基づいて命名されているという前提が頭に入っていれば、バリデーション結果に応じて `.form__field` に `.form__field--ng` をつけたり外したりすることで、アイコンを切り替えれることが15秒くらいで理解できると思います。

最高です。秩序のないcssの世界に秩序と平和をもたらしてくれました。そんな最高なBEMを間違った用法で使いたいというのが今回の主張になります。

主張

`.form__field__status` ← これ長いからやあよ

 BEM唯一の難点です。階層が深くなるほどエレメントのチェインが長くなります。

そこでこうしたい
.form
  .form__field
    .field__status
    input type='text'

Block-Element-Modifier と Element-Element-Modifier の2種類を用いて BEMBEEM にしたい。

聞かれること/言われること

ブロックが明確ではない

Q. `.field__status` が `.form` ブロックのエレメントであることがわかりにくい。
A. そのようなケースは下記のようにブロックの中に親ブロックのエレメントが混ざっている等、構造がおかしいケースでしか存在しないと考えています。

.block1
  .block2
    .block2__element
    .block1__element
  .block1__element

また、クラスがブロックかどうかはチェインしているかどうかで判断できます。

エレメントのチェインをしなければ正しいBEMで見た目すっきり

ブロックにわけよう

Q. ブロックを分ければチェインはしなくてすむよね?
A. 階層的に意味のある構造をブロックに分割することで複雑になりリファクタしにくくなると考えています。

.form {
  .form__field {

  }
}

.field-status {
  
}

formのfieldでしか使わないstatusをブロックに分けることで影響範囲(使用範囲)が不鮮明になります。例えば3年後にformを撤去することになった際に.field-status が適切に撤去されるでしょうか?どこか他の場所から参照されることを恐れてそのままにしてしまう気がします。

階層を浅くしよう

Q. `.form__field` と `.form__status` のように浅い階層にすればチェインしなくてすむよね?
A. 階層的に意味のある構造から階層を取り除くことで複雑になりリファクタしにくくなると考えています。

.form {
  .form__field {

  }
  .form__status {
    
  }
}

このscssを見た時、`.form__status` がフィールドのバリデーション状態を表すことを理解できる人は居るのでしょうか?

これらの解決策はBEMと見た目を守れても、「意味的に正しく明快な構造」と言う重大な要素を守れていないです。

BEMに反している

Q. その書き方はBEMではない。
A. はい。しかしBEMを厳密に守ろうとするために、構造が複雑になったり、意味が無いクラスができたり、影響範囲が不鮮明になることは本末転倒だと思います。正しいBEMを書きたいわけじゃなくて、統一された明快なslimとscssを書きたいです。(俺のBEMは間違っているがわかりやすい)

Q. みんながオレオレルールを作るのは良くない。
A. その通りで何でもかんでもオレオレルールを作れば秩序が崩れます。しかし、明らかな問題があって、その解決策(オレオレルール)が明快で個人の裁量に左右されない(誰がやっても同じ書き方ができる)ものであれば、問題と共通認識を持つコストとのトレードオフだと思います。

結局この記事は何?

 今後 Block-Element-Modifier と Element-Element-Modifier でやっていきたいと考えています…が!従属等の構造的意味合いを崩してまでBEMを守ろうとする考え方が一般的です。

 ということは、気づいたり指摘されていないだけで Element-Element-Modifier の概念を導入すると破綻してしまうケースが出てくるのでは?と疑問が浮かびました。

しかし、BEMとして間違っている以外の理由が思いつかないのです。そこで、何か問題にお気づきの方にサンプルコードとかと一緒にぜひコメントしていただければと考え記事にしました。

【今更感】XmingとPuttyでWindowsに最高の開発環境を作る

linux Windows

はじめに

 Windowsを使用して開発を行う場合、苦しい思いをしてコードをビルド・実行する環境を整えたとしても、 パーミッション破壊マン とか CRLF奴wwww とか EUC-JPおじさんいつもなって なることが時々あると思います。

 Macにしよ!」という声を無視しながら、Windowsを使い続ける私は過去に次のような環境で問題の解決を試みました。

しかし、どの方法も何かしらの問題を持っており、どうにか改善できないか考えていたところに発見したのが Xming です。

Xmingとは

 X11(X Window System) Server のWindows向け実装です。これを使うと GnomeやUnity、Xfceと言ったデスクトップ環境なしに、Windowsと融合する形で快適にGUIアプリケーションを使用することができます。

Windowsのウィンドウシステムに完全に溶け込むgeditの図
f:id:alfa-jpn:20160519093112p:plain

大掛かりなデスクトップ環境が不要なので超軽量です。
また、VirtualBoxのシームレスモードやVMWareのUnityモード等とは異なり、X11 Serverの実装なので様々な制約が無く、重ね重ね書きますが超軽量です


Ubuntu側にインストールしたInteliJやUbuntuが起動できるので、WindowsLinuxが統合された最高の開発環境が作れます!!!!

想定環境

  • Windows7
  • Ubuntu14.04 (Vagrantの `ubuntu/trusty32` を使いました。Vagrantを使用しない場合デスクトップ環境がないServer版を使用して下さい。)
  • Putty (上記UbuntuSSHログイン可能な状態)

環境構築

Xmingのインストール

 Xmingの公式サイト(http://www.straightrunning.com/xmingnotes/)から Xming-mesa と Xming-fonts をダウンロード後、インストーラの指示にしたがってインストールします。

X11Forwardingの有効化

 PuttyのX11Forwardingを有効化します。
f:id:alfa-jpn:20160519095243p:plain
なんとこれだけで基本的な設定は完了です!SSHで接続して gedit などGUIアプリケーションを起動してみるとWindowsのウィンドウとしてアプリが起動します。

しかし、このままだと日本語入力に難ありなので下記に続きます。

日本語入力に関する設定

Xmingの起動オプションを修正

 Xmingの起動オプションに `-xkbmodel jp106 -xkblayout jp` を追加します。私の場合は下記の設定でショートカットをスタートアップに作りました。

C:\Program Files (x86)\Xming\Xming.exe" :0 -clipboard -multiwindow -xkbmodel jp106 -xkblayout jp
パッケージのインストール

 Ubuntuで日本語入力を行うために必要なパッケージをインストールします。

apt-get install ibus-mozc dbus-x11 fonts-vlgothic -y
.profileの修正

 日本語を入力するために必要な環境変数を.profileに追記します。

export LANG=ja_JP.UTF-8
export DefaultIMModule=ibus
export XMODIFIERS="@im=ibus"
export GTK_IM_MODULE=ibus
export QT_IM_MODULE=ibus
export IBUS_ENABLE_SYNC_MODE=1
ibus-daemon -d -x

追記したらログアウト/ログインします。

ibusの設定

`ibus-setup` コマンドを実行して設定画面を開き下記の2点を設定します。

  • `General` タブの `Next input method` に設定されているショートカットキーをすべて削除
  • `InputMethod` タブの `日本語 - Japanese` を削除して `日本語 - Mozc` だけにする

これで日本語入力の設定は完了です。

半角/全角キーが連打されてしまう場合

 どうやらWindowsのライブラリの不具合で 半角/全角キー の入力がリピートされてしまう場合があるそうです。(なった)
その場合は `x11-xserver-utils` をインストール後 .profile に `xset -r 49` を追記します。


以上になります、要点だけをまとめたのでわかりにくい箇所などがあったらコメントを頂ければ・・!

シェーダー入門したくないけど書いてみたい その6 ライティング編

3D Unity シェーダ

はじめに

 この記事ではシェーダに関して入門することができません。とりえあずUnityでシェーダを書いて見たい人向けのブログです。

今回はライティングに対応したシェーダを書いてみます。

事前準備

 事前準備が済んでいない方は、第一回目の事前準備の項目を参考に事前準備を行ってください。
シェーダー入門したくないけど書いてみたい その1 - 波打際のブログさん

06.LightingShader

`06.LightingShader/LightingShader.unity` を開いてみてください。今までの演習ではライトが全く影響しないシェーダーでしたが、このシェーダではライトがきちんと反映されて影もレンダリングされています。
f:id:alfa-jpn:20150816184857p:plain

このシェーダは次のように実装されています。

Shader "ShaderLesson/06.LightingShader/Question" {
    SubShader {
        Pass {
            Tags { "RenderType" = "Opaque" "LightMode" = "ForwardBase" }
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase

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

            uniform fixed4 _LightColor0;

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

            v2f vert(appdata_base v) {
                v2f o;
                
                o.pos      = mul(UNITY_MATRIX_MVP, v.vertex);
                o.lightDir = normalize(ObjSpaceLightDir(v.vertex));
                o.normal   = normalize(v.normal).xyz;

                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 = fixed4(1, 0, 0, 1);
                c.rgb = (c * lightColor * atten) + ambient;
                return c;
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

このシェーダのポイント

 このシェーダのポイントは、AutoLight.cgincをインクルードしていること、DiffuseをFallbackに指定していることです。

AutoLight.cginc

 ライティングを実装するために必要なモジュールです。このモジュールのおかげでほとんど計算式を書かずにライティングを実装しています。今回使用したマクロは次のとおりです。

LIGHTING_COORDS(a, b)マクロ
 ライティングに必要なメンバをv2f構造体に定義します。引数でメンバをTEXCOORDの何番に格納するか指定し、頂点シェーダからTRANSFER_VERTEX_TO_FRAGMENTマクロとTRANSFER_SHADOWマクロを使用してセットしています。

LIGHT_ATTENUATIONマクロ
 ライトの減衰率を計算します。

Fallback

 このシェーダでは自分自身に対するライトの影響と映り込む影の処理しか実装していません。面倒くさい処理は書きたくないのでFallbackを指定して処理を委託しています。

演習

 ライティングシェーダを書き換えてオブジェクトに映り込む影の色を青色にするシェーダを作ってください。
f:id:alfa-jpn:20150816195718p:plain

シェーダー入門したくないけど書いてみたい その5 透過編

3D Unity シェーダ

はじめに

 この記事ではシェーダに関して入門することができません。とりえあずUnityでシェーダを書いて見たい人向けのブログです。

今回は透過に対応したシェーダを書いてみます。

事前準備

 事前準備が済んでいない方は、第一回目の事前準備の項目を参考に事前準備を行ってください。
シェーダー入門したくないけど書いてみたい その1 - 波打際のブログさん

05.TransparencyShader

`05.TransparencyShader/TransparencyShader.unity` を開いてみてください。開くと半透明の箱がレンダリングされていると思います。
f:id:alfa-jpn:20150807015515p:plain

このシェーダは次のように実装されています。

Shader "ShaderLesson/05.TransparencyShader/Question" {
    SubShader {
        Pass {
            Tags { "RenderType" = "Transparent" "Queue"="Transparent" }
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            float4 vert(float4 v:POSITION) : SV_POSITION {
                return mul (UNITY_MATRIX_MVP, v);
            }

            fixed4 frag() : SV_Target {
                return fixed4(1.0, 0, 0, 0.5);
            }
            ENDCG
        }
    }
}

このシェーダのポイント

 このシェーダのポイントは、Blendでブレンディングの指定をしていること、QueueタグにTransparentを指定していることです。

ブレンディングの指定

 ピクセル同士をどのようにブレンドするかを `Blend` 構文で指定しています。これを指定しないとブレンディングが無効なのでシェーダの値がそのままピクセルの値になります。`SrcAlpha OneMinusSrcAlpha` でアルファブレンディングを指定しています。

Queueタグ

 今まで定型文のように全く変化のなかったタグですが、今回はいつもと異なりRenderTypeとQueueタグにTransparentが指定されています。特に重要になってくるのがQueueタグです。QueueタグにTransparentを指定することでシェーダのレンダリング順を後回しにしています

 なぜ後回しにしなければいけないのかというと、他のシェーダーがピクセルに描画してしまうと、それより後ろにあるオブジェクトはそのピクセルに書き込めなくなってしまうためです。

f:id:alfa-jpn:20150807023148p:plain

RenderTypeは他のシェーダからシェーダを参照するときに使われる値を設定します。

演習

 透過シェーダを書き換えて、箱にテクスチャを指定できるようにし、下記のようにテクスチャの灰色部分が完全に透過してレンダリングされるシェーダを作ってください。(ヒント: if制御構文使えます。)
f:id:alfa-jpn:20150807024335p:plain


今回はここまでです。
次回はUnityのライティングに対応したシェーダについて解説と演習を行います。

シェーダー入門したくないけど書いてみたい その4 テクスチャ編

3D Unity シェーダ

はじめに

 この記事ではシェーダに関して入門することができません。とりえあずUnityでシェーダを書いて見たい人向けのブログです。

今回はテクスチャを利用したシェーダを書いてみます。

事前準備

 事前準備が済んでいない方は、第一回目の事前準備の項目を参考に事前準備を行ってください。
シェーダー入門したくないけど書いてみたい その1 - 波打際のブログさん

04.TextureShader

 `04.TextureShader/TextureShader.unity` を開いてみてください。開くと箱に世界の理(ことわり)がレンダリングされていると思います。
f:id:alfa-jpn:20150806005229p:plain

このシェーダは次のように実装されています。

Shader "ShaderLesson/04.TextureShader/Question" {
    Properties {
        _MainTex("テクスチャ", 2D) = "white" { }
    }

    SubShader {
        Pass {
            Tags { "RenderType" = "Opaque" }
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            uniform sampler2D _MainTex;
            uniform fixed4 _MainTex_ST;

            struct appdata {
                float4 vertex   : POSITION;
                float4 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv  : TEXCOORD0;
            };
        
            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                return o;
            }

            fixed4 frag(v2f v) : SV_Target {
                return tex2D(_MainTex, v.uv);
            }
            ENDCG
        }
    }
}

このシェーダのポイント

 このシェーダのポイントは、TRANSFORM_TEXマクロでテクスチャ用のUV座標を作成していること、tex2D関数でテクスチャのピクセルカラーを取得していることです。

TRANSFORM_TEXマクロ

 TRANSFORM_TEXマクロは `UnityCG.cginc` をインクルードすることで使えるUnityの便利マクロです。テクスチャに設定されたタイリングとオフセットを反映したUV座標(テクスチャ座標)を取得するためのマクロです。また、このマクロを利用するために `uniform fixed4 _MainTex_ST;` という一時変数を定義しています。

tex2D関数

 tex2D関数はUV座標(テクスチャ座標)を指定してテクスチャのピクセルカラーを取得することができます。今回はTRANSFORM_TEXマクロで取得したUV座標を指定して `_MainTex` のピクセルカラーを取得しています。

演習

 テクスチャシェーダを書き換えて、下記のように灰色の背景のテクスチャから黄色の背景の箱がレンダリングされるシェーダを作ってください。(ヒント: 赤+緑=黄色、つまり青色成分を出力しなければ・・・)
f:id:alfa-jpn:20150806011237p:plain


今回はここまでです。
次回は透過に対応したシェーダについて解説と演習を行います。

シェーダー入門したくないけど書いてみたい その3 プロパティ編

3D シェーダ Unity

はじめに

 この記事ではシェーダに関して入門することができません。とりえあずUnityでシェーダを書いて見たい人向けのブログです。

今回はプロパティを使ったシェーダを書いてみます。

事前準備

 事前準備が済んでいない方は、第一回目の事前準備の項目を参考に事前準備を行ってください。
シェーダー入門したくないけど書いてみたい その1 - 波打際のブログさん

03.PepertyShader

 `03.PropertyShader/PropertyShader.unity` を開いてみてください。開くと赤い箱が現れるので、ヒエラルキービューからCubeオブジェクトを選択し、マテリアルのプロパティを確認してみてください。すると次のように色の強さが調整できるようになっています。

f:id:alfa-jpn:20150804234217p:plain

色の強さを変更すると箱の色(赤色成分)が変化します。このシェーダは次の様に実装されています。

Shader "ShaderLesson/03.PropertyShader/Question" {
    Properties {
        _Intensity("色の強さ", Float) = 1.0
    }

    SubShader {
        Pass {
	    Tags { "RenderType" = "Opaque" }
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
           
            uniform fixed _Intensity;

            float4 vert(float4 v:POSITION) : SV_POSITION {
                return mul (UNITY_MATRIX_MVP, v);
            }

            fixed4 frag() : SV_Target {
                return fixed4(_Intensity, 0, 0, 1.0);
            }
            ENDCG
        }
    }
}

このシェーダのポイント

 このシェーダのポイントは、プロパティブロックで任意の値をセットできること、その値をシェーダのCgコード内から参照することができることです。

Cgコードという言葉が突然出てきましたが、CGPROGRAM〜ENDCGで囲まれているC言語のようなプログラムのことです。

プロパティブロック

 シェーダに外部から任意の値を渡すときに使用します。数値や色、テクスチャ等をセットすることができます。このシェーダでは `_Intensity`という名前で浮動小数点数を受け渡しするためのプロパティを定義しています。

Cgコード内から参照

 プロパティの値はCgコード内に同名の変数を定義することで参照することができます。

uniform fixed _Intensity;

と変数を定義することで、_Intensityプロパティの値をCgコード内から参照できるようにしています。

先頭につけているuniform修飾子コンパイラに外部の値であることを通知していますが、Unityにおいてuniform修飾子は必須ではないので省略しても動作します。

演習

 プロパティシェーダを書き換えて、下記のように指定した色で箱をレンダリングできるシェーダを作ってください。
(演習問題の進め方は 第一回目の 事前準備->演習問題について を参照してください。)
f:id:alfa-jpn:20150804235402p:plain


今回はここまでです。
次回はテクスチャのマッピングについて解説と演習を行います。

シェーダー入門したくないけど書いてみたい その2 UV編

3D シェーダ Unity

はじめに

 この記事ではシェーダに関して入門することができません。とりえあずUnityでシェーダを書いて見たい人向けのブログです。

事前準備

 事前準備が済んでいない方は、第一回目の事前準備の項目を参考に事前準備を行ってください。
シェーダー入門したくないけど書いてみたい その1 - 波打際のブログさん

02.UVShader

 早速ですが `02.UVShader/UVShader.unity` を開いてみてください。開くと何やらカラフルな箱が表示されていると思います。
f:id:alfa-jpn:20150804014632p:plain

 このCubeオブジェクトのマテリアルには次のようなシェーダーが適用されています。

Shader "ShaderLesson/02.UVShader/Question" {
	SubShader {
        Pass {
	    Tags { "RenderType" = "Opaque" }
        
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct appdata {
                float4 vertex   : POSITION;
                float4 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv  : TEXCOORD0;
            };
        
            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = v.texcoord.xy;
                return o;
            }

            fixed4 frag(v2f v) : SV_Target {
                return fixed4(v.uv.x, v.uv.y, 0, 1.0);
            }
            ENDCG
        }
    }
}

このシェーダのポイント

 このシェーダのポイントは、頂点シェーダからフラグメントシェーダにv2f構造体で値を渡していること、その値はメッシュに設定されたUV座標(テクスチャ座標)であるということです。

appdata構造体に関しては、なんかUnityが都合いい感じにいろいろデータくれて超便利だ〜程度に考えておけば良いと思います。

v2f構造体

 v2fという名前は慣例のようなもので Vertex To Fragmentの略です。頂点シェーダからフラグメントシェーダに複数の値を渡す時にはこのように構造体を定義してそれを利用します。ここでは頂点のスクリーン座標と、その座標のUV座標(テクスチャ座標)をフラグメントシェーダに渡しています。

UV座標(テクスチャ座標)

 テクスチャ座標と書いたほうがピンとくると思います(というか本質です)。モデルの頂点座標にテクスチャのどの部分を描画するかを0-1の値(UV座標)で表したものです。

f:id:alfa-jpn:20150804025630p:plain

上記のようなイメージです。今回はUV座標のu値を赤成分に、v値を緑成分として出力しています。

演習

 UVシェーダー書き換えて、下記の画像と同じ模様の箱がレンダリングされるシェーダを作ってください。
(演習問題の進め方は 第一回目の 事前準備->演習問題について を参照してください。)
f:id:alfa-jpn:20150804022240p:plain

今回はここまでです。UV座標(テクスチャ座標)の概念はシェーダを書く上で割と必要になるのでいろいろ試しておくと良いかもしれません。

次回はシェーダのプロパティについて解説と演習を行います。