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

エフアンダーバー

個人でのゲーム開発

はてなブログテーマ "Notebook" のカスタマイズについて

Windows10のCreators UpdateにてEdgeがアップデートされ、 モダンブラウザのほとんどがCSS-Variablesという仕様に対応したため、 はてなブログテーマ“Notebook”のカスタマイズが容易になりました。

IEや古いAndroidブラウザ、Opera MiniなどはCSS-Variablesに対応していないため、 これらのブラウザで閲覧した場合にはカスタマイズ結果は反映されず、デフォルトの見た目で表示されます。

右の四角がに見えるなら対応ブラウザ、に見えるなら非対応ブラウザです。

カスタマイズ方法

Notebookテーマのインストール

当たり前ですが、まずはてなブログのテーマをNotebookにする必要があります。 テーマストアよりインストールしてください。 レスポンシブデザインなので、ブログメニューの「デザイン」>「スマートフォン」(スマホのアイコン)>「詳細設定」の「レスポンシブデザイン」にチェックを入れることをお忘れなく。

このカスタマイズ方法はNotebookテーマ専用です。 他のテーマで同様のカスタマイズをしたい場合にはテーマの製作者さんに問い合わせてみると、対応してくれるかもしれません。

デザインの変更

デザインの変更はブログメニューの「デザイン」>「カスタマイズ」(スパナのアイコン)>「デザインCSS」にて行います。

特別な設定(背景の指定など)をしていなければ、次のような記述になっているはずです。

/* <system section="theme" selected="10328749687191215643"> */
@import url("http://hatenablog.com/theme/10328749687191215643.css");
/* </system> */

この欄の一番下に次のように書き加え、{}の間に上書きしたい設定項目(後述)とその内容を書いていきます。

/* <system section="theme" selected="10328749687191215643"> */
@import url("http://hatenablog.com/theme/10328749687191215643.css");
/* </system> */

:root {
  (ここに設定項目を書いていく)
}

rootの前の:(コロン)を忘れないように注意してください。

設定項目は次の形式で一行ずつ書いていきます(CSSの書き方と同じ)。

(設定項目の名前):(設定値);
(設定項目の名前):(設定値);
(設定項目の名前):(設定値);

間に挟まっているのは:(コロン)で行末にあるのは;(セミコロン)です(どちらも半角)。 間違えないように注意。

設定を書き終えたら、「設定を保存する」を押して設定を反映すればOKです。

記述例

青系のデザインに変更する例。

:root {
    --color-main: #4040a7;
    --color-main-light: #644fcb;
    --color-main-dark: #3b3b79;
    --color-main-clip: #efecfa;
    
    --color-highlight-r: 100;
    --color-highlight-g: 78;
    --color-highlight-b: 203;
    
    --color-background: #ffffff;
    --color-border: #4040a7;
    
    --color-title: #4040a7;
    
    --border-width-entry: 1px;
    --border-width-sidebar: 1px;
}

f:id:fspace:20170410002840p:plain

設定項目一覧

すべての設定項目はハイフン二つ--から始まります。

色や大きさの指定方法はCSSの記述方法を参照してください。

「(未使用)」と書いてあるものは現在使っていませんが、 今後のデザイン変更で使用される可能性があります。 デザイン変更があった場合にデザイン崩れを最小限に抑えたい場合には設定しておくといいでしょう。

基本色

テキストカラー

設定項目 意味 デフォルト値
--color-text テキストの色 #454545
--color-text-light 明るめのテキストの色 #5f5f5f
--color-text-dark 暗めのテキストの色 #2c2c2c
--color-text-clip 切り抜き時のテキストの色(未使用) #c5c5c5
--color-text-white かなり明るめのテキストの色 #c5c5c5

メインカラー

設定項目 意味 デフォルト値
--color-main メインの色 #a74040
--color-main-light 明るめのメインの色 #cb644f
--color-main-dark 暗めのメインの色 #793b3b
--color-main-clip 切り抜き時のメインの色 #faefec

サブカラー

設定項目 意味 デフォルト値
--color-sub サブの色(未使用) #454545
--color-sub-light 明るめのサブの色 #5f5f5f
--color-sub-dark 暗めのサブの色(未使用) #2c2c2c
--color-sub-clip 切り抜き時のサブの色(未使用) #c5c5c5

ハイライト

設定項目 意味 デフォルト値
--color-highlight-r ハイライトの赤成分(0-255) 203
--color-highlight-g ハイライトの緑成分(0-255) 100
--color-highlight-b ハイライトの青成分(0-255) 78

要素色

背景

設定項目 意味 デフォルト値
--color-background 背景の色 #4c4c4c
--color-content コンテンツの背景の色 #ffffff
--color-border コンテンツの枠線の色 --color-textの値

タイトル等

設定項目 意味 デフォルト値
--color-title タイトルの文字色 #cccccc
--color-description ブログ説明文の文字色 --color-titleの値
--color-footer フッターの文字色 --color-descriptionの値

文章の一部

設定項目 意味 デフォルト値
--color-link リンクの文字色 --color-mainの値
--color-link-visited 訪問済みリンクの文字色 --color-main-lightの値
--color-link-hover マウスを重ねた際のリンクの文字色 --color-main-darkの値
--color-rule 罫線の色 --color-text-lightの値
--color-rule-light 明るめの罫線の色 --color-text-whiteの値

ソースコード

設定項目 意味 デフォルト値
--color-code-background コードの背景色 transparent
--color-code-statement 文キーワードの文字色 #3344ff
--color-code-type 型キーワードの文字色 #3344ff
--color-code-identifier 識別子の文字色 --color-textの値
--color-code-constant 定数の文字色 #cc4422
--color-code-preproc プリプロセッサの文字色 #3344ff
--color-code-comment コメントの文字色 #449933
--color-code-special 特殊キーワードの文字色 #cc4422

グローバルヘッダ

設定項目 意味 デフォルト値
--color-globalheader ヘッダの文字色 --color-main-clipの値
--color-globalheader-background ヘッダの背景色 --color-main-darkの値

色以外

各種大きさ

設定項目 意味 デフォルト値
--border-width-entry 記事の枠線の太さ 0
--border-width-sidebar サイドバーの枠線の太さ 0
--height-title-image タイトル画像の高さ 200px
--font-size-title タイトルの文字の大きさ 2rem
--font-size-description ブログ説明文の文字の大きさ 1rem

おわりに

Notebookテーマを使ってくれている方のブログをいくつか拝見したのですが、 カスタマイズしようと試みている人がけっこういるようだったので、 Edgeがアップデートされたタイミングでカスタマイズ方法を提供することにしました。

もともとオープンソースで改造可能ではあったのですが、SCSS必須ということで初心者にはちょっと難しい作業でした。 現在はCSS-Variablesのおかげで初心者でもなんとかできるレベルになったのではないかと思います。

明るめの色/暗めの色とか、赤成分/緑成分とか若干面倒な設定の仕方になっていますが、 CSSには色の計算や成分分解ができないという制限があるため現状ではどうしようもありません、ご容赦ください。 CSSはもっと柔軟な指定ができてもいいと思うのですが、パフォーマンスとかセキュリティを考えると案外難しいのでしょうね。

【Unity】 Matrix4x4の罠

Matrix4x4でとてつもなくしょうもないバグにハマったのでメモ。

問題コード

何気なくこんな感じのコードを書いて、「あれ?」となりました。

using UnityEngine;

public class MatrixSample : MonoBehaviour
{
    private void Start()
    {
        transform.position = Vector3.one;
        transform.rotation = Quaternion.identity;
        transform.localScale = Vector3.one;

        Vector3 result = transform.localToWorldMatrix * Vector3.zero;

        Debug.Log(result);   // (0.0, 0.0, 0.0)
    }
}

なんとなくわかると思いますが、期待していた結果は(1.0, 1.0, 1.0)です。

行列演算であることを考えれば当たり前のことなのですが、 Unityで普段Matrix4x4で計算することがあまりないので、 ついQuaternionと同じ感覚で書いてしまいました。

原因

Matrix4x4Vector3との間には乗算が定義されていません。 また、Vector3にはVector4への暗黙的な型変換が定義されています。

この結果として右辺のVector3はw値として0が設定されたVector4に暗黙的に型変換され、 Matrix4x4Vector4との間に定義された乗算が呼び出されます。 この演算結果はVector4ですが、Vector4からVector3への暗黙的な型変換も存在するため、 w値が捨てられたVector3が結果として代入されます。

つまり、

Vector3 result = transform.localToWorldMatrix * Vector3.zero;

は実際には、

Vector3 result = (Vector3)(transform.localToWorldMatrix * (Vector4)Vector3.zero);

として実行されます。

行列演算時のw値が0なので、当然平行移動は一切適用されません。

正しい記述

w値に1が入れば正しく計算できるため、明示的に指定してやれば正しい結果となります。

Vector3 result = transform.localToWorldMatrix * new Vector4(0.0f, 0.0f, 0.0f, 1.0f);

ただ、Matrix4x4にはMultiplyPointという専用のメソッドが用意されているので、そちらを利用した方がいいでしょう。

Vector3 result = transform.localToWorldMatrix.MultiplyPoint(Vector3.zero);

localToWorldMatrixworldToLocalMatrixのように4行目の要素を利用しないのであれば、 MultiplyPoint3x4を利用すると不要な計算をスキップできます。

Vector3 result = transform.localToWorldMatrix.MultiplyPoint3x4(Vector3.zero);

おわりに

わかってしまえば本当にしょうもないことなのですが、正しいはずと思い込むと結構ハマります。 ベクトルの変換、せめて次元を増やすときは明示的でよかったんじゃないかな・・・。

今までなるべく短い記事というのは避けていたのですが、 特にこれ以上書くこともないし、記事にしないよりはマシかなということで投稿。 今後はちょくちょくこんな感じの記事も書いていくかもしれません。



執筆時のUnityのバージョン:5.6.0f3

【Unity】 SmoothDampの謎

ユニティちゃんのリパッケージ作業中、SmoothDampの挙動がどうも自分の理解と違うことに気づいたので調べてみました。

www.f-sp.com

SmoothDampとは?

スクリプトリファレンスによれば、

Gradually changes a vector towards a desired goal over time. The vector is smoothed by some spring-damper like function, which will never overshoot. The most common use is for smoothing a follow camera.

和訳すると、

時間の経過とともに目標位置へ向けてベクトルを徐々に変化させます。 ベクトルはあるバネ-ダンパ系のような関数により滑らかに変化し、目標位置を通り過ぎることはありません。最も一般的な用途はカメラの追尾を滑らかにすることです。

要は目標位置が移動する場合の補間です。

ここでは、最もよく使われるであろうVector3のものを前提に話を進めますが、 同様のメソッドがMathf, Vector2, Vector4にもそれぞれあります。

各パラメータの意味は以下の通り。

パラメータ 説明(スクリプトリファレンスの和訳)
current Vector3 現在位置。
target Vector3 目標位置。
currentVelocity ref Vector3 現在速度、この値は関数を呼び出す度に変更されます。
smoothTime float 目標位置への到達にかけるおおよその時間。小さい値ほど目標位置へ素早く到達します。
maxSpeed float 必要であれば速さの最大値を制限します。
deltaTime float この関数の最後の呼び出しからの経過時間。デフォルトはTime.deltaTimeです。

現在速度を保存するためのフィールドを用意して、 SmoothDampで現在位置を更新し続けることにより、 目標位置へと滑らかに移動しつづけるオブジェクトを作成できます。

using UnityEngine;

public class Sample : MonoBehaviour
{
    public Transform Target;

    public float SmoothTime;

    private Vector3 velocity;

    private void Update()
    {
        transform.position = Vector3.SmoothDamp(transform.position, Target.position, ref velocity, SmoothTime);
    }
}

動作検証コード

で、何が予想と違ったかというと次のコードを実行した時の結果です。

using System;
using UnityEngine;

public class SmoothDamp : MonoBehaviour
{
    private void Start()
    {
        Vector3 current = Vector3.zero;
        Vector3 target = Vector3.one;
        Vector3 velocity = Vector3.zero;
        float smoothTime = 0.1f;
        float maxSpeed = Single.PositiveInfinity;
        float deltaTime = smoothTime;

        Vector3 result = Vector3.SmoothDamp(current, target, ref velocity, smoothTime, maxSpeed, deltaTime);

        Debug.Log(result);     // (0.6, 0.6, 0.6)
        Debug.Log(velocity);   // (5.9, 5.9, 5.9)
    }
}

smoothTimedeltaTimeの値を同じにして、座標(0,0,0)から座標(1,1,1)へと移動させるコードです。

移動にかける時間と経過時間を同じにしているので、結果の座標は(1,1,1)に、速度は(0,0,0)になって欲しかったのですが、実際の値はだいぶ違います。

改めてスクリプトリファレンスを見直してみると、 spring-damper like function(バネ-ダンパ系のような関数)とか、Approximately the time(おおよその時間)といった怪しげな記述が。

どういうことなのかもう少し調べてみることにします。

動作コード

入力と出力を眺めてもよくわからなかったのでコードを直接読むことにします。

SmoothDampのコードは次のようになっているようです(変数名は私が適当に付けました)。

public static Vector3 SmoothDamp(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
{
    smoothTime = Mathf.Max(0.0001f, smoothTime);

    float a = 2.0f / smoothTime;
    float b = a * deltaTime;
    float c = (float)(1.0 / (1.0 + b + 0.48 * b * b + 0.235 * b * b * b));

    Vector3 delta = current - target;
    Vector3 originalTarget = target;
    float maxLength = maxSpeed * smoothTime;
    Vector3 clampedDelta = Vector3.ClampMagnitude(delta, maxLength);
    target = current - clampedDelta;

    Vector3 distance = (currentVelocity + a * clampedDelta) * deltaTime;
    currentVelocity = (currentVelocity - a * distance) * c;
    Vector3 newPosition = target + (clampedDelta + distance) * c;

    if (Vector3.Dot(originalTarget - current, newPosition - originalTarget) > 0.0)
    {
        newPosition = originalTarget;
        currentVelocity = (newPosition - originalTarget) / deltaTime;
    }

    return newPosition;
}

重要でない部分を省いて、なんとなく整理してみるとこんな感じ。

public static Vector3 SmoothDamp(Vector3 current, Vector3 target, ref Vector3 currentVelocity, float smoothTime, float maxSpeed, float deltaTime)
{
    float t = (deltaTime / smoothTime) * 2.0f;
    float s = Mathf.Exp(-t);
    float u = s * t * 2.0f;
    float v = s * (1.0f - t);
    Vector3 nextVelocity = ((target - current) / smoothTime) * u + currentVelocity * v;
    Vector3 position = Vector3.Lerp(current, target, 1.0f - (u + v)) + currentVelocity * deltaTime * s;
        
    currentVelocity = nextVelocity;

    return position;
}

うん、わからん。

バネ-ダンパ系という話だったので減衰振動っぽい式が出てくるかなと思っていたのですがいまいち読み解けず。 自分の知識ではどうにもなりそうにないのでこの辺で投げっぱなしときます。

余談ですが、元コードの最後の部分。

newPosition = originalTarget;
currentVelocity = (newPosition - originalTarget) / deltaTime;

なんかバグっぽいような気がしますね。

おわりに

いろいろ調べてはみたのですが、結局よくわからなかったので知識ある人が通りかかることを期待して記事にしてみました。 もし理解できた方がいればコメントいただけるとありがたいです。

今後しばらくはため込んだUnityネタを書いていくつもりです。 比較的書くのが楽なものから消化しているので、小ネタ系が少し続くかも。



執筆時のUnityのバージョン:5.6.0f3