かつて代官山らへんで働いてたengineerのUnityブログ

サーバサイドやってきたエンジニアがUnityとか触って遊ぶだけのブログ

【Unity】ワープポータル転送演出の作り方【AdventCalendar2022】

Unity Advent Calendar 2022 8日目を書きます
皆さんワープポータルは好きですか?
僕は好きです

どんなワープポータル

でもワープポータルって言っても色々ありますよね
あんなのやこんなの、表現も色々なものがあると思いますが...

今回作ろうとしたのはこんなポータル演出

これを見た皆さんはこんな作り方を一瞬で想像したことでしょう
①ポータルのエフェクトを足元に出す
②プレイヤーを地面方向にアニメーションさせる
③地面に埋まったらプレイヤー座標を移動先に移す
④移動先にポータルエフェクトを足元に出す
⑤プレイヤーを上方向にアニメーションさせる

実際その通り実装しています
が!この実装には大きな問題があります


おわかりいただけただろうか、橋をゴリラがすり抜けていくのを...🍌

御覧の通り、上記の実装は橋などがあるゲームステージを想定出来ていません
僕は実装してから気付きました

ではどうすればよいのか
プレイヤーの上下移動と連動して、ポータルに沈み込んだ体の部分を消してしまえばよいですね
今回はその方法についていくつか検討してみようと思います

方法① 重なった部分が透明になるオブジェクトでマスクする

「プレイヤー」「マスク用透明オブジェクト」「その他(地面など)」これらの描画優先度を変更することでプレイヤーの体の一部を透明にする方法です

Tags {"Queue" = "Geometry"}

プレイヤーのシェーダーにこのタグを追加してください

Tags {"Queue" = "Geometry-2"}

地面のシェーダーにこのタグを追加してください

Shader "Custom/Cutout" {
    SubShader{
        Tags {"Queue" = "Geometry-1"}

        Pass{
            ColorMask 0
        }
    }
}

このシェーダーを適用した「マスク用透明オブジェクト」を用意してください

インスペクターでそれぞれのオブジェクトのマテリアルのRender Queueの数字の大きさが
「地面」→「マスク用透明オブジェクト」→「プレイヤー」
になっていることを確認してください

あとは「マスク用透明オブジェクト」を消したいオブジェクトと被せて配置すれば


完成です、お手軽ですね
少しネックなのは地面などのマスクしたくないオブジェクトのRender Queueの数値を場合によっては変更しなければいけないことでしょうか

参考にさせていただいたのはこちらの記事です
nn-hokuson.hatenablog.com

方法② 平面オブジェクトでクリッピングする

平面のオブジェクト(Plane)を用意し、表よりも上にあれば表示、裏よりも下にあれば描画を破棄する、という方法です

Shader "Custom/Player"
{
    Properties
    {
        _MainTex("Texture", 2D) = "white" {}

        [Toggle] _CullEnable("Cull Enable", Float) = 1
        [Toggle] _Positive("Cull Positive", Float) = 1
    }
        SubShader
    {
        Tags {
            "RenderType" = "Opaque"
        }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

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

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _CullEnable;
            float _Positive;
            uniform float4 _ClippingPlane;

            v2f vert(appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);

                if (_CullEnable == 1)
                {
                    o.viewVertex = mul(UNITY_MATRIX_MV, v.vertex);
                }

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                if (_CullEnable == 1)
                {
                    float4 plane = _ClippingPlane;
                    if (_Positive == 0)
                    {
                        if (dot(plane.xyz, i.viewVertex.xyz) > plane.w)
                        {
                            discard;
                        }
                    }
                    else
                    {
                        if (dot(plane.xyz, i.viewVertex.xyz) < plane.w)
                        {
                            discard;
                        }
                    }
                }

                fixed4 col = tex2D(_MainTex, i.uv);

                return col;
            }
        ENDCG
        }
    }
}

このシェーダーをプレイヤーのマテリアルに適用します
既に使っているシェーダーがある場合は頑張って合体させてください

using UnityEngine;

public class ClippingPlane : MonoBehaviour
{
    [SerializeField]
    private Transform _clippingPlane;

    private Material _material;

    private void Awake()
    {
        var skinnedMeshRenderer = GetComponent<SkinnedMeshRenderer>();
        _material = skinnedMeshRenderer.material;
    }

    private void Update()
    {
        Matrix4x4 viewMatrix = Camera.main.worldToCameraMatrix;

        Vector3 viewUp = viewMatrix.MultiplyVector(_clippingPlane.up);
        Vector3 viewPos = viewMatrix.MultiplyPoint(_clippingPlane.position);
        float distance = Vector3.Dot(viewUp, viewPos);
        Vector4 plane = new Vector4(viewUp.x, viewUp.y, viewUp.z, distance);

        _material.SetVector("_ClippingPlane", plane);
    }
}

このC#コードをプレイヤーオブジェクトにアタッチします

平面オブジェクト(Plane)を作ってアタッチしたClippingPlaneの_clippingPlaneのパラメータに参照させます
Planeを表示したくない場合は非アクティブにしても大丈夫です
あとはPlaneをプレイヤーの体を遮るように配置すれば


完成です。シェーダーだけでなくプログラムも絡んでくるので①よりも少しややこしいですが、Planeとプレイヤーだけで完結できるのが魅力ですね

参考にさせていただいたのはこちらの記事です
edom18.hateblo.jp

まとめ

以下が修正後です

しっかりポータルに入っていっている風になっているかと思います🌴

他にもブーリアン演算とかメッシュを無理やり変形させるとか方法は様々あるとは思うのですが
僕は方法②を使うことにしました
ポータル自体が平面の表現で相性が良かったことと、平面オブジェクトとプレイヤーのみで完結することが出来るためです
方法①の方は複数カメラを使ったゲームと相性がよかったりと、それぞれの利点があるので見せ方やゲーム内の状態によって使い分けで行くのがいいような気がします

ゴリラ・オンライン

www.youtube.com

最近知ったんですが、ゴリラオンラインっていうめちゃくちゃ面白いアプリがあるらしいです
海外セレブもこぞって遊んでいるとか...
今ならなんとゴリラオンラインが無料で遊べます!
是非遊んでみて!それでは~~~🦍

ゴリラ・オンライン

ゴリラ・オンライン

  • kosuke sato
  • ゲーム
  • 無料
apps.apple.com

【GooglePlay】Google Play Indie Games Festival 2019のTOP20選出!

f:id:chroske:20190601222731p:plain

ということで仲間と開発していたイケテるゴリラゲー
Google Play Indie Games Festival 2019
トップ20に選出されました!

うおーーーーーー!!!!!!まじか!!!!!!そんな事ある????

正直応募時点ではAndroidのみで発生するバグが残ったままだったりもしたのでこりゃー無理だわと諦めていたのでホントにまさかって感じではありますが夢じゃないな?

BitSummit 7 Spirits

本日から開催されているBitSummit 7にもGooglePlayブースで展示されているみたいです。ほんとか?

f:id:chroske:20190601223028j:plain:h300
ホントだった!!!

ただ僕は絶賛休日出勤しておりましたたため行けなかったのですが.....カローシ!
誰か遊んでくれたらいいなあと遠い地のオフィスより願っております。

ファイナルイベント

ファイナルイベントなるものもあるらしいです。トップ20から更にクリエイター同士が血を血で洗うバトルロワイヤルを繰り広げTOP3までを決めるのだとか。闇のゲームですね
僕も戦士の一人として参加していると思いますのでよろしけれ遊びに来ていただければと。バナナくらいしかあげれませんが。
events.withgoogle.com

オンライン投票

オンライン投票なんていうのもあるみたいです。
1位になるとGoogle社にお呼ばれしてお昼ご飯をいただけるとか。めっちゃいただきたい。
気が向いたらぜひ!
play.google.com

目標は『リリースすること』

僕たちの目標はあくまでも『リリースすること』でありコンテストで入賞することではないので、今後もリリースに向けて頑張っていきます。でも嬉しいものは嬉しいですね、そりゃあね
あとはこのゲームきっかけで色んなクリエイターさんとお知り合いになれたらなあなんて思っております。人見知りだけどがんばります。
そいでは~~~~~~~

【Unity】ゴリラゲーβ版公開【ゴリラ】

現在開発中のめちゃめちゃイケてるゴリラのゲームβ版を先日GooglePlayStoreに公開しました!
play.google.com

β版なんて公開方法があったんですね全然知らんかった
β版と正式リリース版何が違うんじゃ?って感じだと思うんですが、インストール可能な人数を制限できるのと、レビューが投稿出来ない代わりに開発者へのフィードバックが送れるようになってるみたいです。ガンガン送ってきてくれ!
バグが出てしまったとしてもレビューが荒れることがないのはいいかもですね。

ちなみにiPhoneのAppStoreにはβ版みたいな仕組みがないので今回はGooglePlayStoreのみの公開となっております。

進捗とか 助けられたAsset紹介とか

ゴリラが暴れまわるマップもいい感じに仕上がっており、かなりジャングルジャングルしております。

ジャンゴージャンゴー

注目していただきたいのはこちらのマップの地面の部分、terrainで作っているように見えるかもしれませんが、実はterrainではなく3Dデザイナーさんがモデリングして作っています。
なんでterrainじゃないのん?てところなんですが、terrainって普通に使ってしまうと頂点数がものすごいことになってモバイル向きじゃないんですよね。
でも地面のテクスチャのブレンドしてる感じとかすごいいい感じでどうやってるのってところなのですが、今回はBlend Machineというアセットを使用しました。

BlendMachine

gumroad.com

このアセットはベースのマテリアルに対して別のマテリアルをブレンドする事ができるものです。
terrainのブラシ機能普通の3Dオブジェクトに対しても使えると思っていただければいいともいます。
おそらくシェーダーを使って復数重ねたテクスチャを透過させたりして実現しているのではなかろうかなと。

簡単な解説動画もありますし、中にマニュアルも入っているので(全部英語だけど)そちらを見ていただければなんとなく使い方はわかるものと思われます。
www.youtube.com
ただし一つ注意点が
ブレンドの状態を保持しているControlMapというテクスチャファイルがBlendMachineより出力されるのですが、そのデフォルトのフォーマットがモバイルに対応しておらず、「RGBA Half」というフォーマットに手動で変更してやる必要があります。
この圧縮形式は軽いものではないようなのですが、実機で見てみたところそこまでパフォーマンスに影響は与えていないようでしたので、地面だけであればあまり問題にならないものと思われます。

Mesh Baker

assetstore.unity.com
もう散々色んな所でオススメされているアセットなので今更詳細な説明は省きますが、本当にすごいアセットでした。というかMesh Bakerがなければ本当に色々やばかった....
マップをジャングルジャングルさせる関係で一度に描画するオブジェクト数がものすごいことになってしまい、一時はドローコールが700近くまで上がってしまって「あーしんだ....」とつぶやきながら空を仰いだものですが、ちょうどAssetStoreがセール中で神にもすがる気持ちでMesh Bakerを購入し導入したところドローコールを200近くまで下げることが出来ました。マジありがとう!神!GOD!!
f:id:chroske:20190509153311p:plain:h200
Baches 669てお前....

BlendMachineとの相性が悪かったりしないかなと心配したのですがそこは問題ありませんでした。
コツとしては当たり判定のあるオブジェクトと無いオブジェクトを分けて管理しておくといいかなと思います。
Occlusion Cullingはメッシュの結合の関係であまり動かなくなってしまいますが、一説によるとOcclusion Cullingで出したり消したりするのも結構なCPUパワーを食うようでモバイルには不向きとの話も聞きますのでこれはこれでありなのかなと。

正式リリースは8~9月頃を予定

β版とはいえゲームをストアに出すのは初めてなのでなんだかとてもやり切った感が....まだだまだ終わらんよ
ドローコールの次はメモリ周りの改善だ....!!
サーバの方も結構頑張ったのでその内なにか書こうかなと。ちなみにGCPのGKEを使っております。
それではゴリラゲーをよろしくおねがいしま~~~~~~

【Unity】アニメーションしてる3Dモデルをラグドール化してふっ飛ばしてみる【AdventCalendar】

Unity #2 Advent Calender 2018 22日目を書かせていただきます。

前回はGREAT__SHARKさんのEntity Component System を作って学ぶでした。
ECSそろそろ触っておかなきゃなーと思ってる最中、概念や導入部分から公式サンプルの解説までかなり丁寧に説明してくださっているのでめちゃめちゃありがたかったです!必見!


どうも、くそすけです。
Unity関連でアドベントカレンダーを書くのは2回目になります。
2015年にこんな記事を書いてました。どんなテンションで書いてたのかもう思い出せない...
chroske.hatenablog.com

最近作ってるもの

友人のデザイナーさん達とこんなゲームを作っております。


ゴリラと人間が鬼ごっこする最高にHappy&Wildなゲームです。
誰かを巻き込んで個人開発をするのは初めてだったりするので、ちゃんと完成までもっていかないとなあと思っております。
僕が担当しているのはUnityとリアルタイムサーバの部分です。そうなんですマルチプレイなんです。
めっちゃアクセス来て捌けなかったらどうしようとか皮算用しておりますがまあ大丈夫でしょう。
そんな来ないって
どこかで見かけたら遊んでやってください!
今回はこのゲームでも使っているアニメーションしてるモデルをラグドール化する方法についてです。

ラグドール

TimeLineとかECSとかShaderGraphとか比較的今どきの機能関連でガンガン記事が投稿される中
ラグドール?!って感じかと思いますがラグドールです。僕も思います。ネタがなかった...
洋ゲーなどではお馴染みラグドール。関節がぐにゃぐにゃのあれですね。
和製商業ゲームだとあんまり見かないような気がしますが僕はこの表現が結構好きです。
ではさっそくメニューの[GameObject/3D Object/Ragdoll]からこちらのイケメンゴリラをラグドール化していきます。

ゴリラってかっこいいよね!

ラグドールにアニメーションをつける

Unityのメカニムアニメーションはラグドールを動かすことを多分想定していないので、ラグドールをそのままアニメーションさせようとすると生まれたての子鹿みたいになります。

イケメンが台無しですね

なので各パーツに付与されたRigidbodyのisKinematicをtrueにして物理を無効化してやる必要があります。

void SetKinematic(bool newValue)
{
	Component[] components=GetComponentsInChildren(typeof(Rigidbody));

	foreach (Component c in components)
	{
		(c as Rigidbody).isKinematic=newValue;
	}
}

こんな感じの処理でいいでしょう。GetComponentsInChildrenしてるのでパーツ数が多いと重くなるかもしれませんが。

アニメーションしてるモデルをラグドール化する

さっきtrueにしたisKinematicを今度はfalseにしてアニメーションを止めてやるだけです。
isKinematicを切り替えてからアニメーションを止めてやったほうが綺麗にラグドール化する気がします。誤差の範囲だとは思いますが。

public void ChangeRagdoll()
{
	SetKinematic(false);
	anim.enabled = false;
}


ラグドールをふっ飛ばす

AddForceしてやりましょう。説明は不要ですね。

void Update () {
	if (Input.GetMouseButtonDown(0))
	{
		Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
		RaycastHit hit;
		if (Physics.Raycast(ray,out hit))
		{
			if (hit.rigidbody!=null)
			{
				ChangeRagdoll();

				impactTarget = hit.rigidbody;
				impact = ray.direction * impactPower;
				impactTarget.AddForce(impact,ForceMode.VelocityChange);
			}
		}
	}
}

クリックしたところにrayを飛ばしてそこにAddForceしてやるようにしました。

まとめ

まあ実装するとしたらこんな感じだよねーって内容だったと思います。
特に意外性もないっていう。
物理でなにかしらが動いてるだけでもゲームっぽい雰囲気は出ますし、実装も簡単なのでカジュアルなゲームで使ってみたら面白い演出が出来たりするんじゃないかなーと。

明日はUnity #2 Advent Calender 2018 23日目、投稿者はherieruさんです!Shader関連ですかな?
それでは~~~~

【Unity】セルのところで止まるスクロールビュー【FancyScrollView】

ちょうどセルのところで止めてくれるタイプのスクロールビューってあるじゃないですか
SnapScrollとか呼んだりするみたいなのですが、あれを作りたくて自分で組んでみました。

ゴリラがスクロールするよ!


そして半日かけて作ったあとに
「もしかしてどっかに同じようなスクリプト転がってるんじゃね?(禁忌)」
と思い調べてみたら見事にありましたので、今回はそんな神スクリプトの紹介です。

こちらがその神スクリプト、FancyScrollView!
github.com
https://github.com/setchi/FancyScrollView/raw/master/Documents/screencast1.gif
https://github.com/setchi/FancyScrollView/raw/master/Documents/screencast2.gif
めちゃめちゃスタイリッシュ&ファンシィー!


半日かけて作ったなんちゃってスクロールビューは投げ捨ててFancyScrollViewでゴリラをスクロールさせてみることにしました。
そして完成したのがこちら

前のと一緒!!!


ええ、前のとなにが違うのかわかりませんね。僕のなんちゃってスクロールビューも実はよく出来ていたのかもしれません。
ただ内部の処理は僕が作ったものとはかなり異なっていました。

僕の方はuGuiのScrollViewを改造して作ったのですが、こちらはスクロールの動きはAnimationで管理されていて非常に細かい変更が楽です。スクロールさせつつスタイリッシュなアニメーションを混ぜたい場合などにめちゃめちゃ便利なのではないでしょうか。
あとは無限スクロールなんかにも対応しており、応用の幅が広いです。
表示させているセルも最小限になる作りなっており、ド派手なアニメーションなどさせない限り負荷も問題ない模様。
サンプルも充実しており、実装で特に迷うことはありませんでした。


こんなものが普通に無料で転がっているのだから、恐ろしい時代になったものです。
みんなもFancyScrollViewでスタイリッシュになろう!




そういえばブログ名ちょっとだけ変えました。

【Unity】1週間ゲームジャムに参加してみた【クソゲー】

代官山らへんで働かなくなってはや2年が経とうとしております。くそすけです。
そろそろブログ名変えなきゃなぁ



唐突にUnity1週間ゲームジャムなるものに参加しました。
f:id:chroske:20180611211550p:plain

参加しましたって言ってもゲーム作って投稿するだけなんですが
毎回お題が変わるようなのですが、今回は「ぎりぎり」がお題でした。
作ったのはコチラの神ゲー、「ラスト・グレネード」です。
lastgranadegif
https://unityroom.com/games/pub_nantoka


ラスト1個のグレネードで最後に残った敵を爆殺するだけの簡単なゲームです。焦るなよ?
某島に100人落とされてサバイバルする系のゲームをやってる人は「あー、アレね」ってなると思いますがアレです。

ちなみにアイディアは締切日当日のお昼に浮かんだので開発期間は8時間です!
え?いっしゅうかん?

ビルドは早めにやっておこう!

ゲームジャム的なものには初めて参加したのですが、これ重要ですね。
基本ビルドは上手く通らない!!
今回WebGLビルドが初めてだったのでそれもあるのかもしれませんが、全然上手く行きませんでした。
AssetStoreの無料アセットのパワーに頼りすぎたのも原因だったりします。あいつらめっちゃバグる!
まあUnityは結構バージョン依存するのでアセットがバグっちゃうのは仕方ないことです。力には責任がつきまとうのだ。

ガンガンAssetStoreに頼ろう!

僕らプログラマは基本イラストを書いたりかっこいい3Dモデルを作ったりはできないはずなので、そこはガンガンAssetStoreに頼っていきましょう。無料でもかなりいい素材がゴロゴロしてます。
今回使わせていただいたのはこちら
assetstore.unity.com
assetstore.unity.com
assetstore.unity.com
Environments系のアセットはライティングとかシェーダーとかかなり勉強になります。

アイディアがでっかくなっちゃう病にはゲームジャム!

僕も結構ゲームのアイディアとか考えるんですが、決まってでっかくなりがちなんですよね。
そんな人にこそゲームジャムかなあと。締切が決まってれば「面白くしよう!」の前に「間に合わせなければ...」っていうのが来るので自然とアイディアがコンパクトになります。多分。
向き不向きはあると思うんですが自分には合ってるような気がしたのでまた機会があれば参加してみようかと思います。


ブログ名なんにしようかなあ

【Unity】NetworkMigrationManagerを試してみる【UNET】

Unity5.3.1p3より追加されたNetworkMigrationManagerですが、
これなにが出来るのかって言うと、ホストマイグレーションができます。
ホストマイグレーションっていうのはつまり通信中にホストが落ちちゃった場合にクライアントの誰かが新しいホストになりかわって通信を繋ぎ直し、ゲームを継続するというもの。
Photonなんかだと普通に実装されている機能ですが、UNETには今までなかったのでこれでちょっとはPhotonの対抗馬となれるのかなーと思い実際に動かしてみました。

やり方

1) 空のプロジェクトを作成します。

2) 空のオブジェクトを作成し、コンポーネントにNetworkManager HUD、NetworkManager、NetworkMigrationManagerを追加してやります。

3) もう一個空のオブジェクトを作成し、NetworkIdentityを追加します。Local Player Authorityにチェックを入れます。

4) 3で作成したオブジェクトをprefab化します。元のは消していいです。

5) 2で作成したオブジェクトのNetworkManagerのPlayer Prefabに4で作成したprefabを追加します。

6) ホストマイグレーションのテストなので、エディタ以外に最低もう一個クライアントが必要となります。PCアプリとしてプロジェクトをビルドします。

7) 6でビルドしたPCアプリを実行して、エディタも実行します。

8) どっちかでLAN Hostします。もう一方でLAN Clientします。つながりましたね?

9) LAN Hostした方をSTOPします。これでホストが落ちたことになります。

10) LAN Clientした方でPick new Host→Start as Hostします。これでこいつが新しいホストになります。
f:id:chroske:20160715150804p:plain

11) STOPした方でPick new Host→Recconect as Clientします。これでさっきホストだった方がクライアントとしてつながりました。
f:id:chroske:20160715150801p:plain

まとめ

とりあえず動かすだけなら簡単に出来ましたね。
実際これを使って開発するとしたら継承してロジックなどを書き足しながら使ってやることになるのかと思います。
あとこれ現在のところUnity MultiplayerなどのMachMakerを使っている場合には使えません。
マニュアルにも書いてあるんですが一番下に書いてあって気づくのに時間かかった。上に書いとけよお!!

IP指定で直接接続してる場合のみ使えるみたいです。
つまりまだまだPhotonのホストマイグレーションほど便利には使えないみたいですね。今後に期待です。