【UdonSharp / UdonGraph】同期変数と On Value Changed の書き方

はじめに

VRChat(でワールド制作)を始めて3年くらい経ちました。この文章を書き始めたときは2年だと思っていたんですが、ちゃんと数えたら3年でした。噓でしょ、時の流れ早すぎ……。

それはともかく、私はワールドを作り始めてから今までずっとUdonGraphをこねていました。プログラミングなんて縁があるとも思っていなかったのですが、今では知識 0 から知識 0.5 くらいにはなった気がします(最大値が100くらいのとき)。

最近はシェーダーとかエディタ拡張とかをちょこっと書いてみたりもしています。そのおかげか、スクリプトを読んだり書いたりすることへの抵抗感が薄くなってきたので、これを機にUdonSharpとも戦ってみることにしました。

Graphでやっていたことがベースになるので書き方さえ覚えればあんまり難しくはないのですが、ひとつだけ、同期変数に関連したあれこれの書き方が難しいというか、Graphと違いすぎて理解に時間がかかったので備忘録を兼ねて書いておくことにしました。

というわけで前置きが長くなりましたが、UdonSharpでの同期変数の書き方について、Graphと比べながら中身を見ていきます。素人が自己解釈で好き勝手やってるので間違ってたら教えてください。

前提知識

・UdonGraph

【VRChat】Udon Graph 入門【SDK3】 #VRChat - Qiita

・UdonSharp

UdonSharpメモ ~(最初だけ)はじめての人に向けて書いてみる~

入門 ②の処理をOnValueChangedを用いて書いてみる - ハツェの真時代傾向璋

・同期の話

VRChatワールドの同期処理まとめ|のりたま

(理屈の部分だけ、スクリプトはわからなくても多分ダイジョブ)

 

UdonGraph編

とりあえず、同期変数を使った簡単な例として「グローバルかつLateJoinerにも同期するオブジェクトON/OFF」をGraphでつくります。

 

「Interactでオブジェクトの表示/非表示を切り替える」

 

誰かがスイッチをInteractしたら、その人をスイッチのOwnerにして同期変数を変更します。この時、Set Value ノードでは sendChange が有効になっています。これにより、Value Change ノードを使えば同期変数の変更を自動的に検知して任意の処理(ここではtargetObjectの表示/非表示)を行うことができます(ManualSyncMode の場合は、Set Value の直後に RequestSerialization ノードを呼びます)。

synced を有効にする(左上Variablesのところ)

Set Value ノードで sendChange を有効にする

Value Change ノードから任意の処理を行う

視覚的にも直感的にもやっていることがわかりやすく、手間も少なくていい感じですね。

 

UdonSharp編

では、上のGraphをU#のスクリプトに書き直してみます。


using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon;

public class SyncedObjectToggle : UdonSharpBehaviour
{
    public GameObject targetObject;
    
    [UdonSynced, FieldChangeCallback(nameof(syncedValue_Property))]private bool syncedValue;
    
    public bool syncedValue_Property
    {
        set
        {
            syncedValue = value;
            targetObject.SetActive(syncedValue_Property);
        }
        get =>syncedValue;
    }
    
    void Start()
    {
        syncedValue = targetObject.activeSelf;
    }

    public override void Interact()
    {
        Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
        syncedValue_Property = !syncedValue_Property;
    }
    
}

 

Graphとの対応をざっくり書くとこんな感じ。たぶん。

 

同じ色のところが(だいたい)同じ処理をする

 

まず変数の定義のところで、同期変数には [UdonSynced] という属性をつけます(変数の頭についている[]で囲まれた部分を属性というらしいです)。Graphでは変数の synced にチェックを入れていますが、これがその部分にあたります。

さらに、[FieldChangeCallback] という属性もつけます。これがどうやら Set Value ノードの sendChange チェックにあたるらしい……のですが、Graphとは違ってなにやら syncedValue_Property という別の名前が追加されています。そして、青で囲まれた部分でこの新しい名前について何か書いているようです。

この青の部分が、私がはじめ理解できなかったところです。

 

変数とプロパティ

syncedValue が変数なのに対して syncedValue_Property はプロパティというそうですが、私は今までこの2つの違いを認識していませんでした。

【C#】プロパティとメンバ変数の違い

変数とプロパティについて、私は上記を参考にふわっと理解しているのですが、変数を操作するときに間に立ってくれる代理人のようなイメージをしています。

U#で Value Change を書くときは、この代理人を通して変数を操作してあげる必要があります。

同期変数とプロパティの部分を見てみます。


    [UdonSynced, FieldChangeCallback(nameof(syncedValue_Property))]private bool syncedValue;
    public bool syncedValue_Property
    {
        set
        {
            syncedValue = value;
            targetObject.SetActive(syncedValue_Property);
        }
        get =>syncedValue;
    }

Graphでは、syncedValue という同期変数の変更を伝えたいとき、sendChange を有効にしていました。

U#では、変更を伝えてくれる代理人の名前をつける必要があります。なんでもいいのですが、とりあえず後ろに _Property とつけてこれがプロパティであるとアピールしておきます。忘れたときに読み返しても混乱しなさそうなので。

そうしたら、具体的に何を伝えるのかということを代理人に教えておきます。

get=>syncedValue;

とは「syncedValue_Property を読み取るときは、syncedValue と同じ値を返す」みたいな意味だと思います。たぶんそんな感じ。多分。

set { syncedValue = value; } 

とは、「syncedValue_Property を操作したときは、syncedValue にも同じ値を渡す」みたいな意味でしょう。きっと恐らく。

裏を返せば、set{} の中身は syncedValue_Property を操作したときに呼ばれるということなので、ここに任意の処理を書いておくことでGraphでいう Value Change ノードと同じことができるのだと思います。ということで、今回はオブジェクトのアクティブ状態を変更する処理を書きました。もちろん CustomEvent も呼べます。

これで代理人に必要な情報を与えることができたので、自由にこの同期変数を操作できるようになりました。

注意するべきなのは、値を変更するときは必ず代理人を通す、つまりプロパティの方を扱います。偉い人とお話するときは窓口を通してアポをとらなければいけないのと似ていますね。でも会えない可能性がある偉い人とは違って、プロパティさんは必ず変数さんにアクセスしてくれます。

逆に読み取るときは、変数でもプロパティでもどっちでもいいみたいです(常に同じ値なのでそれはそう)。偉い人が今どんなお仕事をしているのか、公開されている情報を見る分には許可は必要ないのと同じです。ただプロパティさんは変数さんが持っているのと必ず同じ情報を返してくれるので、正確性は100%です。

実際の動作部分の記述ではほとんどプロパティさんが呼ばれて変数さんは名前だけのお飾りみたいになることもあります。もう全部あいつ一人でいいんじゃないかな?でもプロパティさんは変数さんがいないと空っぽなので、周りから見えなくても変数さんは必要なんですね。何それエモい。

おわりに

流れが変わってしまいそうなので章を分けて話を戻します。

U#の同期変数と Value Change について、素人が嚙み砕いた理解を書いてみました。理解するのに半日くらいかかりましたがわかってしまうとなんてことはないですね。はやくU#こわくないになりたいです。これからもU#と戦っていきます。

ここまでお付き合いいただきありがとうございました。

延長戦の話

こんばんは。あるいはこんにちは。

知っている人は知っているでしょうが、私は大学4年生です。そしてもう半年大学4年生をやる予定です。留年というやつですね。

3月に卒業するものと思われて話を振られると説明がめんどくさそうなので、ここで言っておくことにしました。本題はこれで終わりです。

「なんで卒業できなかったの?」とかは、まあまあ長くなりそうなのでここでは書きませんが、話すことに抵抗はないので気になる人は直接聞いてくださいませね。

最近大学や就活のことを聞かれることもあまりなかったのですが、気を遣われていたような気がしないでもないです。実際、確定するまでは私自身も話しにくい部分はあったので助かってはいました。いまはそれなりに折り合いついているのでどうぞ遠慮なく。

以上近況報告というか個人的なお話でした。読んでくれた人はありがとう。

PoiyomiとlilToonのライティング設定

アバターや衣装でlilToonとPoiyomiを併用するときに、だいたいどんなライティングでも馴染んで見えるようにする設定値の個人的なメモ書き。

ライティングの確認に使ってるのはこれ。

booth.pm

 

シェーダーバージョン(リンクはマニュアルに飛ぶ)

lilToon 1.4.1 

Poiyomi Toon 7.3.xxx / 8.0.xxx

 

明るさの基本設定

それぞれのシェーダーのライティング設定項目はここ。

まずlilToon。

下の「拡張設定」はとりあえずライトの合成だけ見る。

これがPoiyomi 7.3.xxx。

大項目上から2番目「Lighting&Reflections」の中。

そしてこれがPoiyomi 8.0.xxx。

大項目上から2番目「Shading」の中。

それぞれのパラメータの対応はたぶんこう(上からlilToon・7.3.xxx・8.0.xxx)。

明るさの下限

・”Lighting Modifiers” > Min Brightness

・”Light Data” > ”Base Pass Lighting” > Min Brightness

 

明るさの上限

・不明

・”Light Data” > ”Base Pass Lighting” > Max Brightness

 

ライトのモノクロ化

・”Lighting Modifiers” > Monochromatic Lighting?

・”Light Data” > ”Base Pass Lighting” > Grayscale Lighting?

 

影色への環境光影響度

・不明

・不明

 

この辺の数値をそろえればだいだい馴染む。「不明」は対応パラメータを見つけられてないやつ。明るさの上限は「1」、影色への環境光影響度は「0」でとりあえず同じように見えている気がする。

 

加算ライトの設定

ワールドにリアルタイムライトが複数あるときの合成処理の設定。

方針として考えられるのは大きく二つ。

・限られた環境できれいに見えるようにする

・白飛びリスクを許容しつつどんな環境でも同じような見え方にする

 

lilToonのマニュアルでは、加算ライトの合成方法について、

・比較(明)・・・光学的に正しくないが白飛びしにくい

・加算・・・光学的に正しいが白飛びしやすい

ということが書いてある。

一方Poiyomiでは、どうやら加算合成した後の輝度上限を設定することで白飛びを回避しているらしい?(マニュアルには”Defines the maximum allowed brightness resulting from the add pass.”って書いてある)

 

よって、白飛びしないことを重視してlilToonの比較(明)の色味に近くなるようPoiyomiの最大輝度を設定するか、白飛びリスクを受け入れて両方とも制限なしの加算モードにするかのどちらかを好みで選ぶということになる。

 

1. どっちも加算にする場合

こっちは簡単。

 

〇lilToon

 ・「ライトの合成」 = 加算

〇Poiyomi

 ●「Additive Lighting(7.3.xxx) / Add Pass Lighting(8.0.xxx)」タブで設定。

  ・Point Light Passthrough

  ・Limit Intensity(7.3.xxx) / Limit Britghtness?(8.0.xxx) = 無効

 ●「Additive Lighting(7.3.xxx) / Shading > Shading(8.0.xxx)」タブで設定。

  ・ Lighting Type = Toon

  ※8.0.xxxのShading > ShadingタブはLight Dataタブの一個下。

 

2. lilToonの比較(明)に合わせる

〇lilToon

 ・「ライトの合成」 = 比較(明)

〇Poiyomi

 ●「Additive Lighting(7.3.xxx) / Add Pass Lighting(8.0.xxx)」タブで設定。

  ・Point Light Passthrough

  ・Limit Intensity(7.3.xxx) / Limit Britghtness?(8.0.xxx) = 有効

  ◎Max Intensity(7.3.xxx) / Max Britghtness(8.0.xxx) = お好みで

 ●「Additive Lighting(7.3.xxx) / Shading > Shading(8.0.xxx)」タブで設定。

  ・ Lighting Type = Toon

  ※8.0.xxxのShading > ShadingタブはLight Dataタブの一個下。

 

加算合成した後の輝度上限というのが実際にどういう計算をしているのかは知らないが、追加のライトが明るくなればなるほどlilToonは明るくなるがPoiyomiは一定値で頭打ち、逆に暗くなればなるほどPoiyomiは暗くなるがlilToonは一定の明るさを保つ、という状態になる。

どれくらいの明るさのライトを想定するかによってちょうどいい値が変わるのでよく行くワールドとかよく遊ぶフレンドに合わせておくのが無難だろうか。困ったらとりあえず1でいいと思う。

【VRChat】Quest対応水中シーンを作りたい

はじめに

Quest対応の水中ワールドの自分用パッケージを公開してみます。

このワールドでやっていること(の一部)を実装できます。

vrchat.com

自作したシェーダーとMITライセンスの改変シェーダー、それらを使用したサンプルシーンが含まれます。制作にあたって色んな記事を参照しててライセンスとかよくわからないので、必要な方は自己責任で使ってください。一応参考にした記事は貼っておきます。

パッケージ配布

簡易コースティクス、簡易泡パーティクル、簡易水面、視界色付けが入っています。詳細は後述します。

drive.google.com

入っているのはあくまで水中っぽい色合いや雰囲気を出すためのものだけなので、沈めたい景色はお好きなように作ってください。

シェーダー詳細

パラメータがいろいろありますが細かい解説はしていません。触ればなんとなくわかります。

・SeaCausticsProjector

UnityのProjector機能を用いた簡易的なコースティクス

広い地面に投影することを目的に作っています。四方に壁のある部屋やプールなどには向かないかもしれません。あと遠目にみるとちょっとキモイ。

・TransparentMatCap

泡用の透明なマットキャップ。リムライトもついています。テクスチャのアルファ値を参照します。liltoonとかでも同じことができるのでわざわざこのシェーダーを使う理由はそんなにないかも。

パーティクルに使っても普通の板ポリパーティクルとほとんど見た目変わりません。マットキャップなので……

・FadeWater

Fogを貫通し、Mask画像を用いてアルファ抜きする水シェーダー。CubeMapを当てるとちょっとだけ見た目がよくなります。本当にちょっとだけ。

遠くを見せたくないからFogをかけたい、でも頭上の水面は見せたい……というときのためだけのシェーダーです。

下から見上げることだけを想定し、水シェーダーとしての機能は最低限しかないのでほかの用途ではほとんど使い物にならないと思います。

・QuestColorOverlay

これです。Tweetに書いていることが全部です。今回のパッケージにも同梱しておきました。

サンプルシーン

UnityPackage内のSceneファイルを開くとたぶんこういうやつがでてきます。一応ライトベイクをしています。

これをベースにいろいろいじって使ってください。見た目を確認するためだけのシーンのつもりでつくったのでVRCWorldはおいていません。

おわりに

最後にシェーダーコードの参考記事を(覚えているだけ)列挙して終わります。ここまでお付き合いいただきありがとうございました。

amagamina.jp

qiita.com

3dcg-school.pro

meganeunity.hateblo.jp

styly.cc

techblog.kayac.com

www.wwwmaplesyrup-cs6.work

baba-s.hatenablog.com

【VRChat】カーテンを手で開けたい

はじめに

VRChatのワールドでカーテンを手でつかんで開けたり閉めたりしたいという話です。

以下のワールドでやってることを実装します。

vrchat.com

 

具体的には下のTweetみたいな挙動をします。テスト段階なので実際のワールドとはちょっと違います。

 

結論

スライドドアギミックの取っ手の動く範囲を基準に、取っ手の現在座標から終点までの距離を0~1に正規化して、100倍してカーテンメッシュのシェイプキーに突っ込む。

やり方

2行で終わるのはあんまりなのでもうちょっとだけ詳しく書きます。

用意するもの

最低限必要なもの

・スライドドアギミック

booth.pm

・開閉シェイプキーのあるカーテンのモデル

お好みで用意するもの

・カーテンシェーダー

booth.pm

これ入れてカーテンがゆらゆらしてるだけでだいぶいい感じです。おすすめ。

下準備

カーテンのモデルを用意します。お好みでカーテンシェーダーも適用しておきます。

説明のために片側だけにしています。

※このモデルは以下の無料アセットに開いた状態のシェイプキーを追加したものになります。影が汚いのはトポロジーを下手にいじったせいです。

booth.pm

 

スライドドアギミックのPrefabを置きます。カーテンの端(掴む方)に取っ手が合うようにします。

 

取っ手の終点を用意します。高さと奥行きは取っ手と同じ座標にして、横だけカーテンが開ききったときの端に合わせます。カーテンのメッシュが横向いてるので画像だとグローバルZ軸が左右になってますが気にしないでください。

 

スライドドアギミックのUdonのパラメータを調整します。

これはZ軸方向にスライドする向きに配置した上にドアの裏表もひっくり返ったままなのでこんな奇妙なパラメータになっていますが、ちゃんとX軸が左右になるように配置していればほとんどいじる必要はないです。Maxの値だけ適当にいじっておけばいいです。

詳しいことはスライドドアギミックのマニュアルを参照してください。

下準備が終わりました。

Udonを用意する

カーテンの開閉のためのUdon本体を用意します。

Udonの中身の説明をするのが面倒なので、Prefabを用意しました。この記事の後ろの方にDLリンクを貼っているので必要な方はそちらからどうぞ。

こいつのInspectorを、下のように設定してください。

・GrabPoint → スライドドアギミックのハンドルのメッシュ部分

・EndPoint → 取っ手の終点(ここではHierarchyにあるEndPointという名前の空オブジェクト)

・CurtainMesh → 開閉シェイプキーのついたカーテンのメッシュ

設定が終わったらClientSimで動作確認します(画像でスライドドアギミックのPrefabが一つ増えていますがパラメータ確認用に置いただけなので関係ないです)。

動けばOK。あとはスライドドアギミックのメッシュを非表示にして取っ手のコライダーの調整をします。StaticModelはオブジェクトごと非表示にしても問題ありません。

MoveModelのコライダーを残しておけば、カーテンが閉まっている間は向こう側に行けないという状態になります。

既知の問題(?)点

シェイプキーが100にならない

この設定だときっかり100になりません。たぶんUdonの計算がよくないせいだと思いますが、よくわからないのでそのままです。直し方わかる人は教えてください。

動かしすぎるとシェイプキーの値が小さくなる

スライドドアギミック側のパラメータ次第ではEndPointを若干超えて取っ手を動かすことができますが、取っ手がEndPointを超えるとシェイプキーの値がだんだん小さくなります。計算がよくないのでわかる人は以下略

ライトベイクに不向き

シェイプキーを使う都合上SkinnedMeshになるので、ライトベイクに向きません。BakeryならShadowMsakをごにょごにょすればベイクした上からリアルタイムの影を落とせます。やり方はlilさんがTweetしてくれています。

Udonの中身を知りたい人向け

中はこうなっています。

やっていることは冒頭で言ったとおりですが、付け加えるならシェイプキーに突っ込む値を100から引く処理をしています。

これはシェイプキーを閉→開でつくってしまったからなので、開→閉にしておけばここの処理はいらないと思います。

Prefabの配布

解説に使ったPrefabが入っているだけのUnityPackageです。全VRChatワールドのカーテンがカシャカシャできるようになることを願っています。

drive.google.com

おわりに

カーテンの実装にあたってお借りしたすべての素材とその作者ならびにUdonのアドバイスとテストに協力してくれたフレンドに感謝します。

あとは動かすときにSEとかついたら楽しそうですね。スライド速度を音源の再生速度とかピッチにいい感じに突っ込んで、こう……

 

そういえば最近PhysBoneに押込み機能がついたので、もしワールドにPBが実装されたらそちらのほうが簡単なような気がします。早く実装してくれ。

【Unity】Projectorコンポーネントに対応したシェーダーの備忘録

はじめに

タイトル通り。Projectorで投影するためのシェーダーを書くにあたって躓いたところのまとめ。シェーダー素人が書いているのですべての記述がふわふわ。コードブロックの使い方よく分かってないのでコピペしたら改行とかおかしいかも。

シェーダーに書くもの

#include "UnityCG.cginc"
Pass
{
     CGPROGRAM
     #pragma vertex vert
     #pragma fragment frag

     #include "UnityCG.cginc"
     ・・・

これを書かないことはあんまりないと思うけど、一応。

struct v2f{}の中身
struct v2f
{
     ・・・
     float4 uv : TEXCOORD0;
     ・・・
};

大抵の場合、UVはxyの2軸の平面でしか扱わないのでfloat2で書いてあることが多い気がする。後述の関数?でUV座標扱うときにfloat4じゃないといけないっぽいのでfloat4で書いておく。
struct appdate{}の中にもuv : TEXCOORD0;書いてるけど、これはfloat2でも問題なく動いているように見える。どっちが何に使われているのかよく知らない。自分が書いたシェーダーの中ではそもそも使ってないのかもしれない。

float4x4 unity_Projector; float4x4 unity_ProjectorClip;
float4x4 unity_Projector;
float4x4 unity_ProjectorClip;

Propertiesで書いた変数とかのグローバル変数と一緒のとこに書くやつ。とりあえずfloat4x4 unity_Projector;だけあればなんとかなる。

v2f vert(appdata v){}の中身
v2f vert(appdata v)
{
     v2f o;
     o.vertex = UnityObjectToClipPos(v.vertex);
     o.uv = mul(unity_Projector, v.vertex) ;
     return o;
}

o.uv = mul(unity_Projector, v.vertex) ;←ここが大事(たぶん)。投影するためのUV座標をなんやかんやするやつ。

fixed4 frag(v2f i) : SV_Target{}の中身
fixed4 frag(v2f i) : SV_Target{
     ・・・
     float4 uv = UNITY_PROJ_COORD(i.uv);
     ・・・

わざわざ変数を作る必要はないが、とにかくUVをUNITY_PROJ_COORD()に突っ込んでおく。これでいい感じに投影されるようになる(たぶん)。
Textureのサンプリングに使うときはtex2Dproj(_Maintex, UNITY_PROJ_COORD(i.uv));みたいな感じで書くっぽい。StandardAssetの中に入ってるシェーダーにはそう書いてあったので。

おわりに

オブジェクトのUV座標を投影用にいろいろ計算するとこだけ書き換えればとりあえず動く。どういう理屈でなんの計算をやっているのかを解説するだけの知識はないので書いてないけど、↓の記事が参考になるかも。
amagamina.jp
someiyoshino.info

ワールド制作について最近思っていること

まえがき

いろんなひとがワールド制作についていろんなことを言っていたので、私も何か言ってみることにしました。

いままでにTwitterで呟いたことのまとめみたいな部分もあって、特に目新しい情報はないかもしれません。またすでにほかの人が言っていることに重なる部分もあると思います。お暇な方は読んでいってください。

何を「綺麗」と思うのか

"Questでも綺麗なワールドを見たくて自給自足する人"

これは私のTwitterプロフィールにある文言です。

私は綺麗なワールドが好きです、と言ったところで、多分みんなそれは同じだと思います。違うのは何を綺麗だと感じるかだと思いますが、では、私にとっての綺麗とはなんでしょうか。

あるフレンドは、所謂Liminal系のワールドを好んで巡っているようです。本人曰く「陰湿」な雰囲気のものが多い。私もLiminal系ワールドは良いものだと思いますが、「綺麗」だとは思っていないような気がします。でも彼はそういう陰湿さもひっくるめて「綺麗」だと思うんだそうです。

「Olympia」「ORGANIM」という有名なワールドがあります。私も行きましたし、感動しました。確かに綺麗だなと思いました。でもこれは客観的な「綺麗」であって、主観的な「綺麗」ではないと感じています。よく作られている良いワールドですが、「このワールドは好きですか?」と聞かれたら、首を傾げます。最近だと「井の頭公園駅」も似たような感じです。

私の好きな、綺麗だと思うワールドは、(特に断りもなく勝手に名前を出してしまいますが)例えばyukiさんとか、はにょえさんとか、ポケットさんとか、yo-gurutoさんとか、Estyさんとかが作られているようなものです。もちろんほかにもたくさん好きなワールド作者さんはいますが、とにかくそういう感じの雰囲気です。幻想的な絵とか、植物的な絵とかのわかりやすいやつが私の好みのように見えますが、実はそう単純でもなく、ぱっと見「そういう系」でも意外と刺さらないワールドも多いです。

結論を言ってしまうと、何が私にとっての「綺麗」なのか、全然さっぱりわかりません。ある程度の傾向はありますが、「これだ!」と定義しきることはできていません。自分が何を感じているのか、もっと言語化できるようになりたいものです。ワールド制作に限ったことではないですが。

「綺麗」の欠片を集めて

さて、「何が自分にとっての綺麗なのか、結局わからん」と放り投げてしまうのもなんだか癪なので、私が今まで作ったワールドの中から、綺麗だと思っているものをあげてみようと思います。もし何か共通点が見つかったら、私に教えてください。

「あの夏に閉ざされて-Chronostasis-」

このワールドにはほんのりとバックストーリーがあります(ワールドの中ではほとんど語られていません)が、時代設定は特に考えていません。中世でも、近代でも、現代でもお話自体は成り立ちます。

見る人によっては、近世以前っぽい雰囲気の建物や小物の中に堂々と現代的な電柱が並んでいるのが気になるかもしれませんが、私は特になんの違和感もなくこの景色を気に入っています。

実をいうと電柱があるのは着想を得た楽曲のPVがそうだったからで、景色としては別にあってもなくてもそんなに変わらない気はします。

 

「CARNATION˸Recollectionー想い出ー」

ここは二次創作というかファンアートというか、再現ワールドなので数に入れるか迷いましたが、この絵が好きなので入れておきます。

やりたかった窓の表現と、やわらかい背景色のグラデーションが上手くいったので気に入っています。作ってる最中は自分で見すぎてよくわからなくなって、お蔵入りにしようかとも考えていましたが、フレンドがかなり気に入ってくれたのと公開してほしいというありがたい声を受けて公開しました。今は公開してよかったなと思っています。

 

「追憶のレコード-Songs of Memories-」

レコードシステムを作ろうとして、結果的に景色も作ってしまったワールド。ここがどういう場所でなんの建物なのか、なにも決めていませんが雰囲気がいいので気に入っています。見る人が見れば建物の細かいところにツッコミが入りそう。

 

「聖堂-Before The Decay-」

本格的にモデリングにチャレンジし始めたころのワールドです。夜の方は隠しポータルからしか行けないようになっていますが、私はどちらかというとこっちのほうが好きです。

これも見る人が見ればツッコミどころ満載だと思いますが、個人的に雰囲気はお気に入りです。結構頑張って作ったからというのも理由かもしれません。

 

「あわいの書庫~The Boundary Library~」

いまのところ私のワールドの中で一番人気?です。かなり難産だったのもあってその理由はわかりませんが、雰囲気自体は私も気に入っています。

ここも世界観設定のないワールドです。「紫の夕焼け」というお題から始まって、見た目がよくなるようにポンポンとオブジェクトを生やしていたらこうなっていました。

 

「遥かなる旅の果てに~The Faraway Journey~」

空と水面がランダムにちぐはぐになるギミックを作りたかったワールドです。

水没した線路はVRChatにおいてありふれすぎるほどありふれていますが、どういうわけか何度見ても一定のエモさを感じてしまうのが悔しいです。

ワールド制作初期のころに作ったワールドですが、その中では今でも気に入っているほうです。ウユニ塩湖が綺麗なのがいけない。

綺麗≠好き

ここに挙げたもののほかでも、自分のワールドにはそれぞれ愛着があるので、ちゃんと気に入っています。ただ、それらは「綺麗」とは少し違うところで気に入っているような気がします。ほかの人のワールドでも同じことがあります。このあたりももっと言語化したいのですが、今はまだ解像度が低くて難しそうです。

正しいことと美しいこと

これは少し前にTwitterでつぶやいて、ちょっと多めの反応があったことですが、私はワールド制作において「正しいこと」は気にしていない(というより、気にしたくない?)ようです。

どうやって自立しているのかわからない建造物をつくり、なんの光源からかよくわからない光を当て、時代背景のバラバラなオブジェクトを並べています。それが美しいと思うからです。そして正しいことを考えるのは面倒だからです。趣味でやっていることなので、面倒なことは極力したくないのです。

私のワールド制作のスタートは、大抵は何らかの「見たい一枚絵」です。こんな絵がほしい、こういう景色を見たいというところからスタートします。それらのきっかけは、音楽であったり、実際に見た景色であったりしますが、ともかくそれらスタート地点の絵は、とてもぼんやりとしています。「自分の部屋の絵をなにも見ずに記憶だけで描いてください」と言われて正しく描ける人はほとんどいないと思いますが、そんな感じです。目の前にモデルがあるわけでもない、想像上の絵を3D空間にどうにかこうにか描き出していく作業が、私にとってのワールド制作です。私は2D絵を描かない(描けない)のでわかりませんが、絵を描くのと同じなんじゃないかと勝手に思っています。違ったらすみません。

頭の中の絵には必ず、不明瞭な部分があります。「ここはどうなっているんだろう」と考えて、手が止まります。無視してとりあえず見えている部分だけ作ろうと思っても、空間はつながっているのでどうしても進めないこともあるし、単に気になりすぎて思考がストップすることもあります。不明瞭な部分は一か所だけでなくいくつもあるので、ある程度の絵ができるまでにそれが何度も続きます。こういう不明瞭な部分をどうにかしようとするときに、「正しいこと」を考えてしまいます。私が世界の創造主だから勝手に決めてしまえばいいのに、普段の私が正しい物理世界に生きているからか、そう簡単にはいかないようです。

そうやって「正しいこと」に思考を奪われると、スタート地点で見ていた絵がどんどんぼやけてしまうような気がして、とても焦ります。私はワールドを作っているとき、内心焦りを感じていることが多いです。

「正しいことより、自分が美しいと思うこと」というのは、そういう自分に言い聞かせている言葉でもあります。

つくることと考えること

また、これも前につぶやいたような気がしますが、私は「つくること」よりも「考えること」のほうが好きです。つくるというのは、考えたことをほかの人に共有する手段であって、目的ではないと感じています。頭の中でワールドの構想を練るだけ練って、表に出さないどころか形にすらせずに満足することもあります。つくることに伴う種々の実作業は嫌いではありませんが、やらなくてすむなら、多分やらないほうを選ぶと思います。

そして、つくることには時間がかかります。その間に考えていることがぼやけてしまうような気がして、こっちでも焦りを感じたりします。もっとゆったりとつくることを楽しめるようになりたいですね。

おわりに

まとまりのない文章でしたが、最近のいろんなひとの考え方を見て、自分の考えていることを少し言語化してみました。自給自足とか、やりたいときにやりたいことだけやるだとか、それらのスタンスを変えたわけではないけど、言い聞かせみたいになっていた部分もあるので、来年は自然体でそうあれるようにしたいですね。

ここまで読んでくださった方、ありがとうございます。

それでは、よいお年を。