【Unity】初心者でもCinemachineを使いたい
まずはこれを見てくれ。
Unityのcinemachine。初めて使ったけど、これスゲーな。カットシーンだけじゃなくてゲームにもこうやって使える。素晴らしい。ブログ書こう。 pic.twitter.com/9hOtHptNqC
— うーはーぐーるぅ (@uuha_goul) 2017年7月5日
複数のカメラが切り替わるとグッとゲームらしいです。
これ作るの面倒なんだよね~、なんて方。
ご安心ください。簡単に作れますよ。そうCinemachineならね。
Cinemachine?
UnityがAssetストアで無料で公開しているAssetで、統合カメラシステムです。
https://www.assetstore.unity3d.com/jp/#!/content/79898
Unity 2017.01.から使用可能になる予定となっていて、
今使いたい場合は、β版のUnityが必要です。
早速使ってみる
Unity Betaを準備してAsset StoreからImportしたら
Examplesから各Sceneを開いて機能を確かめたら良い!
そして、公式のModule Examplesを見るんだ!
と言いたい所ですがそれは置いておいて
「自分で使う場合はどうやるんだ?」
って所を書こうと思います。
まずは動かしてみよう
CinemachineをImportしているプロジェクトで画面上部メニューから
Cinemachine > Create Virtual Camera
を押すと、Main Cameraに Cinemachine Brainが追加される。
さらにCinemachine Virtual Cameraが追加されたCM vcam1がSceneに追加される。
もうこれだけでCinemachineが動いています。スゴイ。当たり前かw
見たまんまをあえて書きますと、
Main Cameraの位置に関わらず、CM vcam1の位置からカメラ表示するようになっている
Main Cameraの位置を動かそうとしても追加されたCM vcam1の位置が上書きされて動かせなくなっているはず。
また、sub cameraを作っているのではなく、スクリプト制御しているということもわかります。
ちょっと遊んでみる
もう一度
Cinemachine > Create Virtual Camera
を選択して、2つ目のVirtual Cameraを追加しよう。
ちょっと位置を調整して切り替わりのテストをしてみます。
こんな感じで切り替わりがわかる形にしました。(真上からの視点)
切り替わり
1つ目のカメラをCM vcam1
2つ目のカメラをCM vcam2
とします。
CM vcam1 -> CinemachineVirtualCamera enable ON CM vcam2 -> CinemachineVirtualCamera enable OFF
の状態で実行して、
CM vcam2のenableをON/OFFしてみよう。
CinemachineVirtualCameraのenableやGameObjectのActiveを切り替えるだけで
自動でカメラが切り替わってくれる事がわかりました。
念のために優先度もテストしておこう
CM vcam1 -> priority 11 CM vcam2 -> priority 10
この状態で CM vcam2 のenableをON/OFF切り替えてみても
何も反応しないということがわかります。
想定通り、優先度の値が大きい方が優先になるということですね。
まとめると
この事から
- GameObjectのActiveかCinemachineVirtualCameraのenableをON/OFFするだけで動く
- Priorityが大きい方が優先になる
- Priorityが同じ場合はONにした方が動く
ということがわかった。
ゲームに応用してみよう
Cinemachineがちょっとは理解できた。結構単純な仕組みで動いていそうだ。
ゲームで使いたい場合は、単純なFollow Modeなどでも十分使えるが、
もうちょっと応用してみて、アクションゲームやADVゲーム風のカメラワークを
作ってみたい。
仕様
- カメラ自体は動かない
- カメラはキャラクターを見る(LookAt)
- カメラごとに範囲を指定
- 指定範囲にプレイヤーが入ったらそのカメラに切り替わる
- 切り替わる時のアニメーションは個別に設定
- カメラ追加も出来るだけ容易に
実装
特に実装は要らないかなと思っていたのですが、
こんなのだけは作る必要がありました。
範囲に入れば優先度を上げ、範囲から出れば優先度を下げるだけ。
このスクリプトを各vcamにセットして、
SphereColliderもAddComponentします。(Is Trigger ONを忘れずに)
SphereColliderのRadiusで範囲指定して、実行すればもうそれだけでOK!
(二回目。これはこの範囲指定vcamを利用して作ってます。範囲に入るとカメラが切り替わっている)
Unityのcinemachine。初めて使ったけど、これスゲーな。カットシーンだけじゃなくてゲームにもこうやって使える。素晴らしい。ブログ書こう。 pic.twitter.com/9hOtHptNqC
— うーはーぐーるぅ (@uuha_goul) 2017年7月5日
カメラの追加はEditorから作るようにして、一気に必要なComponentの追加、
設定項目の設定なども一気にやっちゃうとさらに良さそう。
public void AddColliderVirtualCamera() { var go = new GameObject(); var sphereCollider = go.AddComponent<SphereCollider>(); var cinema = go.AddComponent<Cinemachine.CinemachineVirtualCamera>(); var cinemaCollider = go.AddComponent<CinemaCollider>(); sphereCollider.isTrigger = true; }
Inspectorでボタン押下作れると便利。神、お世話になってます。
baba-s.hatenablog.com
個別にTweenアニメーション指定も出来る…だと?
Cinemachine Brainを改めて見てみる。
Default BlendというEase In Outという表示がセットされている。
これはカメラとカメラを切り替える時のTweenアニメーションを指定している。
ここを個別に設定するには、Custom Blendsを使う。
Custom Blendsはほとんど直感で使えてしまうぐらい簡単だ。
Create Asset -> でMain Camera Blends.assetを作成して、
From , To , Style , Timeを追加していくだけだ。
Fromは「どこから」 Toは「どこまで」 Styleは「どのように」 Timeは「どれくらい」
だ。
FromやToには
「ANY CAMERA」
という項目があり、
これはそのまんま、「いずれかのカメラ」を指す。
StyleのTweenアニメーションもいくつか用意されている
- Cut -> 即座にパッと切り替わる
- Linear -> 一定速度で切り替わる
- Ease In Out -> 開始時と終了時に速度が変化する
など。この辺りは好きに触ってみてほしい。
これをガシガシ追加していけば個別にアニメーションを設定出来る
cinemachine神
ブログに起こすと凄く長くなったが、実際に触ると簡単だし!
でもきっと本気でカットシーン作ろうとしたら凄い苦労するんだろうなぁ。
また、今回作ったサンプルでぐりぐり走ってると結構カメラが激しく動くんでちょっと酔ってしまいました。
大きめなフィールドにしたり、移動速度を調整したり、カメラの切り替わり速度を調整すればその辺は改善できそう。
使うときはいい感じに調整してみて。
よくわからんメモ
CinemachineCollider.cs といういかにも!なスクリプトがcinemachine側に用意されているのですが、 どうも使い方がよく分からず…自分の思っていた機能ではない? API Reference無いかなぁ…。
【Unity】UnityのSceneを劇的ビフォーアフターしたい
概要
こうしたい。
いいらいとさんがちょっと教えてくれた。
こういうこと教えてくれる人は少ないです。貴重な御方です。
10秒雰囲気メイキング
— いいらいと (@easy_light2213) 2017年6月27日
半円リングを回転させる。
アセットに課金してグラデがかかったskybox設定。
planeを設定したパーティクルをひらひら回転させて背景になんか降らせる。
完。#unity1week pic.twitter.com/YbzfA5lPQW
なるほど、自分もやってみよう!
元のデータを用意
テキトーにSceneを作る。
テキトーにオブジェクトを配置してみる。
ここでは
- Cube
- Sphere
- Cylinder
の3つをFloorに見立てたCubeの上に配置しました。
ダサすぎワロタ。
これをオシャレに変えてみましょう。
まずは空から変えていこう
unityはSkyboxという空間の内側にSceneは描画されます。
そのSkyboxから変えていこう
Shaderで二色のカラーをグラデーションした感じでいかがでしょう。
神がいた。
github.com
もうこれこのままでええやん…?完成形や。
Lerp(A,B,C)はAからBに向かって変化した時に、Cの値で保管する関数
これを利用したMaterialを作って
Windows > Lighting > Setting
SkyboxのMaterialに作ったものをぽいっと設定すると
(眩しすぎるならIntensity Multiplierを減らしてみて)
なんということでしょう、先程まで殺風景だった背面があっという間にオシャレなグラデーションの空間に!
ああ、次はカメラだ
これだけでもオシャレさはあるが、まだいける。
次は
Post Processing Stack(無料)
https://www.assetstore.unity3d.com/jp/#!/content/83912
を使う。
Unity5.5以上が対象になってしまうので、それ以前の人は
Import PackageからImage Effectで似たような機能が使えるはずだ。
(間違ってたらゴメンナサイ)
別で作ったProfileの値でPost Effect(色々描画して、最後にかけるエフェクト)を調整出来る
○Profileの作成
Project -> Create -> Post Processing Profileで作成
○Post Efffectの設定
Post Processing BehaviorスクリプトをMain Cameraにアタッチ
○Post Effect profileの反映
作成したPostProcessing Profileを Post Processing Behavior 紐付けて準備完了だ。
PostProcessing Profileの設定
- Antialiasing
- Ambient Occlution
- Depth of Field
- Bloom
- Color Grading
の5つをONにしただけでこんな感じ。
まるで豆腐のように美味しそうだったオブジェクトが、
あっという間に食べられなくなりました。
後はお好みでパラメータをいじったり、
point lightを入れてみたりするとそれだけでグッとそれっぽい。
いかがでしょうか
オブジェクトには触らずに
- Skybox
- PostProcess
この2つを触るだけでグッと良くなるのがUnity。本当に劇的。
まだパラメータが一杯あるのでまだまだ出来ることはありそう。
【Unity】第三回unity1week「積む」に挑戦 後編 (開発編)
ちゃんとゲームらしく
タイトル -> チュートリアル -> ゲーム -> 結果
と画面を作る。
必要な素材も大体ここで描く。
fireAlpacaで適当に描く。
他のゲームもいきなりゲーム画面のやつばかりやんけ!と
甘えから今まで一回もチュートリアルなんて作ってこなかったが、
今回はちゃんと作ろう。少しでもゲームをプレイして貰いたいし。
説明文に頑張って書く!という最終手段もあるけど。
開発
開発はスイスイ進んだ。流石に三回目ともなれば色々と積み重ねたものがある。
2DベースでuGUIでゲームを作るというあんまりやってはならない事かもしれないが、
それで行く事にした。
各パーツと捉えてこれらは全て別々のCanvasにしている。
Canvasを複数作ってON/OFFをスッと切り替えて、画面遷移を制御するようにして、
順番やsortOrder , positionを細かく自由に変更可能にしていた。
縦書き
unityはデフォルトで縦書きに対応はしていない。
が、先人達がすでに挑戦してくれていたのを拝借。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Linq; public class VerticalText : UIBehaviour, IMeshModifier { private Text textComponent; private List<char> nonRotatableCharacters; // 回転させない文字リスト private List<char> adjustCharacters; // 位置を調整する文字リスト private void InitLimitCharacters() { if (nonRotatableCharacters == null) { nonRotatableCharacters = new List<char>(); nonRotatableCharacters.Add('ー'); } if (adjustCharacters == null) { adjustCharacters = new List<char>(); adjustCharacters.Add('ァ'); } } public new void OnValidate() { //base.OnValidate(); -> WebGLビルド時のみエラーになる。iOSやaOSは平気なのに…何故? textComponent = this.GetComponent<Text>(); var graphics = base.GetComponent<Graphic>(); if (graphics != null) { graphics.SetVerticesDirty(); // 更新有りと伝える } } public void ModifyMesh(Mesh m) { } public void ModifyMesh(VertexHelper verts) { var stream = ListPool<UIVertex>.Get(); verts.GetUIVertexStream(stream); modify(ref stream); verts.Clear(); verts.AddUIVertexTriangleStream(stream); ListPool<UIVertex>.Release(stream); } void modify(ref List<UIVertex> stream) { if (textComponent == null) { textComponent = this.GetComponent<Text>(); } var characters = textComponent.text.ToCharArray(); if (characters.Length == 0) { return; } InitLimitCharacters(); float angle = 90.0f; // 頂点を云々する。1テキスト6頂点 // 6文字ずつ進む for (int i = 0, streamCount = stream.Count; i < streamCount; i += 6) { int index = i / 6; if (IsNonrotatableCharactor(characters[index])) { continue; } // 文字の中央を取得(上なら[i+1]) var center = Vector2.Lerp(stream[i].position, stream[i + 3].position, 0.5f); if (IsAdjustCharactor(characters[index])) { center = center + new Vector2(-2.0f , 2.0f); } // 頂点を回す for (int r = 0; r < 6; r++) { var element = stream[i + r]; var pos = element.position - (Vector3)center; ; var newPos = new Vector2( pos.x * Mathf.Cos(angle * Mathf.Deg2Rad) - pos.y * Mathf.Sin(angle * Mathf.Deg2Rad), pos.x * Mathf.Sin(angle * Mathf.Deg2Rad) + pos.y * Mathf.Cos(angle * Mathf.Deg2Rad)); element.position = (Vector3)(newPos + center); stream[i + r] = element; } } } bool IsNonrotatableCharactor(char character) { return nonRotatableCharacters.Any(x => x == character); } bool IsAdjustCharactor(char character) { return adjustCharacters.Any(x => x == character); } }
OnValidate
https://docs.unity3d.com/ja/540/ScriptReference/MonoBehaviour.OnValidate.html
ModifyMesh
https://docs.unity3d.com/ja/current/ScriptReference/UI.IMeshModifier.ModifyMesh.html
簡単に言えばUIの更新イベントフックして、そこで回転させている。
縦書きにするとァやー(長音符)の位置が少しズレてしまい、違和感を感じるので、
それを調整するようにしました。
また、ListPoolはUnityのソースから拝借。
神様ありがとうございます!
http://madnesslabo.net/utage/?page_id=6042
注意点としては、
WebGLビルドした時に
error CS0117: `UnityEngine.EventSystems.UIBehaviour' does not contain a definition for `OnValidate'
とbase.OnValidate()が怒られる。
これなんでだろー?iOSやaOSビルドは問題が無かった。
とりあえずコメントアウトしていて、特に影響なかったのでそのまま…。
感想
変なゲームを好きに作ろうとしたら
被ったー!盛大に被ったー!しかも結構多いw
もうすでに三回目になり、
付いていくのもギリギリですが、
毎回本当に勉強になるなぁ。
少しずつ出来ることが広がっていくのを感じる。
今まではなんかゴチャ混ぜ感があって、
適当に詰め込んだ感じがあったけど今回は割りとまとまった方だと思う。
ゲーム部分をもっと詰めたいな。なんか本とか読んでみよう。
そろそろTweet機能を付けてみてもええか…。
第三回 unity1week 「積む」に挑戦 前編 (企画編)
先週また@naichilabさんのunityroomにて、unity1weekが開催されました。
unityの勉強は継続してやっていて、このブログも出来るだけ続けていきたいなと思っているので
今回もチャレンジしていく事にしました。
積みゲーけしけし | 無料ゲーム投稿サイト unityroom - Unityのゲームをアップロードして公開しよう
企画について
積むって。
もーこれが難しい。だって最初にパッと浮かんだのアレですよ、アレ!
もうずーっと上位にいるネズミとかクマとかファンシーで可愛いアレのイメージが強すぎる。
とりあえずその考えを頭から振り払って、
他のことを考えよう。
荷物を積む、オブジェクトを積み上げて塔を作る、
など上にベクトルを向けるものがイメージとしてすぐに浮かぶが、
これも一杯出るだろうなぁ、と一旦振り払う。
積むんだよな、ゲームだよな、とシンプルに考えだした時
「積む+ゲーム=積みゲーをゲームにしたらどうだろう」
と思い立つ。
詳細をちょっとだけ詰める
積みゲーがただあるだけではダメだ。
それをどうにかしてゲームに落とし込まないと。
積みゲーを消化するって言うよな…。
シンプルに消化していくゲームはどうだろう。
- 積みゲーから一本ゲームを選ぶ
- そのゲームにパラメータが割り振ってある
- ゲームにはクリアまでの所要時間がある
- プレイヤーは所要時間を消費しそのパラメータ分成長する
- 称号が手に入る(アクションゲームばかりクリアしていくとアクションマスターなど)
- 一定期間過ぎた時に結果を振り返ることが出来る(一ヶ月など)
というゲームはどうだろうか。
だめだー
簡単にモックにしてやってみるが、全然駄目。
- ゲーム拘束時間が長くて、辛い。
- 長い割に別に大した結果が出るわけでもない。
- やれる事に比べて周りを固める要素が多く、分かりにくい。
普通の人ならちょっと触ったらもうブラウザ閉じてるわーこれは。
もうこの時点で金曜日の深夜。だけど考え直そう。
無理矢理な方向転換
もっとシンプルにしよう。所要時間も称号も捨てよう。
パラメータももっとシンプルでいい。
パズルゲーム風に要素を調整して…。
・お題が出る
・それを選ぶ
・正解なら+1 不正解なら0
としたらグッと触り心地が良くなった。
(面白いかどうかは一旦置いておいて)
後はこれでなんとか完成まで持っていこう。
開発編につづく
ghoul-life.hatenablog.com
【Unity】uGUIでimageに穴を開けたい!(くり抜く)
ルパン三世的な…?
チュートリアルなどでゲーム画面の説明をしたい。
よくある画面を暗くして見てほしい箇所にライトを当てるような演出をしたい。
こんなんただ塗って穴開けてるだけやん。
それだけなのに、パッと実装が分からない。
Maskじゃない、なんというかMaskの逆?
uGUIだけで出来る?どうやるんだ?
- すでにゲームは作ってあるので、そこはなるべく触りたくない
- uGUIベースのゲームなのでuGUIでやりたい。
- 実装はなるべくシンプルで簡単に
どうも既存機能だけでは厳しそうな感触。
なんらかの工夫が必要か。
くりぬく?
nn-hokuson.hatenablog.com
なるほど、ColorMask 0!
これでも行けそう?だけど
裏のQueueもいじらないと駄目か…。うーむ。
あ、そうか別Layerにして、サブカメラにすればいけそうかな?
ワイプ、トランジション?
裏に影響なく画面全体を被せるのはこれが近いのかな?
tsubakit1.hateblo.jp
これを応用すればやりたいことが出来そう?
おお!これか!?これじゃん!
nn-hokuson.hatenablog.com
これを応用してみよう。
実装してみる
もうほとんどそのままだけどパラメータを外から設定出来るように。
Shader "Custom/HoleViewShader" { Properties{ _BackColor("background color", Color) = (0, 0, 0, 0) // 背景色 _HoleX("Hole Position X", float) = 0 // 穴の位置X _HoleY("Hole Position Y", float) = 0 // 穴の位置Y _Radius("hole radius", float) = 0 // 穴の大きさ _ScreenW("Screen Width" , float) = 960.0 // 画面の幅 _ScreenH("Screen Height" , float) = 640.0 // 画面の高さ } SubShader{ Tags{ "Queue" = "Transparent" } // alphaに対応するのに必要 Blend SrcAlpha OneMinusSrcAlpha // alphaに対応するために必要 Pass{ CGPROGRAM #include "UnityCG.cginc" #pragma vertex vert_img #pragma fragment frag // Propertiesの値をShaderに渡す float _HoleX; float _HoleY; fixed4 _BackColor; float _Radius; float _ScreenW; float _ScreenH; fixed4 frag(v2f_img i) : COLOR{ i.uv.x *= _ScreenW / _ScreenH; // アスペクト比を計算してあげる if (distance(i.uv, fixed2(_HoleX, _HoleY)) < _Radius){ discard; // 指定位置より一定距離以内だったら処理を飛ばすだけ } return _BackColor; } ENDCG } } }
値を渡すスクリプト側はこんな感じ。
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class HoleView : MonoBehaviour { void Start() { // 外部から設定する SetHole(new Vector2(480, 320), 0.2f); } public void SetHole(Vector2 pos, float radius) { // 画面との比率を出す var pinPos = new Vector2(pos.x / Screen.width, pos.y / Screen.height); // アスペクト比の問題なのか、位置的にはこのあたりで調整すると思った位置になるので計算してあげる // x range 0.45 - 1.05 この値が画面端から画面端でいい感じの所 // y range 0.3 - 0.7 // 比率と範囲値を計算して値を出す pinPos.x *= (1.05f - 0.45f); pinPos.y *= (0.7f - 0.3f); // 最下値を足して範囲に入れる pinPos.x += 0.45f; pinPos.y += 0.3f; // shaderに値を渡す var img = GetComponent<Image>(); img.material.SetFloat("_HoleX", pinPos.x); img.material.SetFloat("_HoleY", pinPos.y); img.material.SetFloat("_Radius", radius); img.material.SetFloat("_ScreenW", Screen.width); img.material.SetFloat("_ScreenH", Screen.height); } }
githubにもさくっと。
github.com
Inspectorはこれだけ!
Image一個あればOK!
これで超簡単!後からちょい載せで使えるヤーツの完成です。
uGUI同士ならCanvasのSortOrderとかで順番をいい感じにしてあげてください。
【Unity】Inputをいい感じにしたい
UnityでInputを取りたいなぁ。なんて思ったら
void Update()
などで
Input.GetMouseButton(0)
とか書くだろう。
そしてif分で押したらこのイベントをして…
なんていう処理を書くだろう
そしてそれが複数必要になることだってあるだろう。
そして仕様上それら全てを一時的に無効にしたいタイミングが来たりしたら
とっても大変だろう。(pause中とかね)
なので、Input周りはWrapperを使うようにしている。
自分はこんな感じで。
SingletonMonoBehaviorはこちら。
ghoul-life.hatenablog.com
シンプルだけど重宝している…。
simple is best.
どっからでもアクセス出来て、on/offも一括だし。
一括on/offは
Input.simulateMouseWithTouches = false
これでももしかしていける?使ったこと無いけど。
とりあえずこれはmouseだけど、touchとかも入れていけば
mobileにも対応できそう?
なんかそのままでもmobileでとりあえずは動いちゃうとか
聞いたこと有る。
一部だけ有効にしたい、とか
マルチタッチに対応したい、とか
DownとUpの差を見てフリック判定する、とか(時間も要るか)
要望は尽きない。メンテナンスしていきたい所。
なんかパッと書けちゃいそうだけどねーこのレベルだと。
でも自分用にメモっとこう。
【Unity】SingletonMonoBehaviourでUnityObjectにアクセス
ゲーム全体を管理するGameManagerであったり、Inputであったりと
- 一意で管理したい
- 好きな所からサクッとアクセスしたい
なんて場合に重宝する。
これだけだけど、メモっておこう