概要
を導入することで
のようなエフェクトを簡単に作れます。
設定例
Materialを作成し、シェーダーは Sprite ( Pixel Lit Dissolve )
を選択します。
を導入することで
のようなエフェクトを簡単に作れます。
Materialを作成し、シェーダーは Sprite ( Pixel Lit Dissolve )
を選択します。
例えばタイトル画面からホーム画面に遷移するとして、基盤を使う側のコードはこんな感じとなります。
using UnityEngine; using UnityEngine.UI; using UniRx; using Zenject; public class Title : SceneBase { [Inject(Id = "start")] Button startButton; [Inject] TransitionController transitionController; void Start() { // タッチでホーム画面へ startButton.OnClickAsObservable().Subscribe(_ => { transitionController.LoadScene("Home", TransitionController.LoadingMode.LongLoading); }).AddTo(this); } }
using UnityEngine; using UnityEngine.UI; using Zenject; using UniRx; public class Home : SceneBase { [Inject(Id = "quest")] Button questButton; [Inject] TransitionController transitionController; void Start() { // Something code here ... } }
[SerializeField]でオブジェクトを直接リンクして参照するかわりに
ZenjectのInject
アトリビュートでもってインスタンスを注入します。
UIの参照がInjectされるためにZenjectBindingコンポーネントを使用します。
ZenjectBinding
というコンポーネントをidentifier
とともに貼り付けておきます。
Resources以下にあるProjectContext.prefab
はZenjectの仕組みによってゲーム起動直後にシーンに読み込まれます。
そして、DontDestroyなのでシーン遷移しても消えないグローバルオブジェクトです。
この性質を利用して、ProjectContext直下にトランジション演出用のコンポーネントTransitionController
を置きました。
こんな具合でProjectContext
以下に置いたCanvas
にあります。
そしてTransitionControllerのインスタンスをProjectContext
のグローバルなInstallerに下記のような
コードでアタッチさせています。
using UnityEngine; using Zenject; using UniRx; using System.Linq; /// <summary> /// ProjectContextにアタッチされるグローバルなInstaller. /// </summary> public class ApplicationInstaller : MonoInstaller { [SerializeField] TransitionController transitionController; public override void InstallBindings() { Container.Bind<TransitionController>().FromInstance(transitionController).AsSingle(); } }
あと色々省略してますがTransitionControllerのコードです。
public class TransitionController : MonoBehaviour { public enum LoadingMode { // ローディング画面を挟まない None, // 長めのローディングになりそうな場合 LongLoading, } [SerializeField] GameObject view = default; [SerializeField] GameObject block = default; [SerializeField] Material transitionMaterial = default; LoadingMode loadingMode = LoadingMode.None; public void LoadScene(string sceneName, LoadingMode mode = LoadingMode.None, object argument = null) { loadingMode = mode; NavigationService.NavigateSingleAsync(sceneName, argument) .Subscribe(_ => { }, e => Debug.LogException(e)); } public void LoadSceneAdditive(string sceneName, object argument = null, LoadingMode mode = LoadingMode.None) { loadingMode = mode; NavigationService.NavigateAsync(sceneName, argument) .Subscribe(_ => { }, e => Debug.LogException(e)); } void Awake() { GameObject.DontDestroyOnLoad(this.gameObject); NavigationService.AsyncBroker.Subscribe<AsyncBeginLoadSceneEvent>(_ => Observable.FromCoroutine(Show)).AddTo(this); NavigationService.AsyncBroker.Subscribe<AsyncAfterLoadSceneEvent>(_ => Observable.FromCoroutine(Hide)).AddTo(this); NavigationService.Broker.Receive<BackButtonEvent>().Subscribe(_ => OnBackEvent()).AddTo(this); NavigationService.Broker.Receive<TapBlockEvent>().Subscribe(_ => block.SetActive(true)).AddTo(this); NavigationService.Broker.Receive<TapUnblockEvent>().Subscribe(_ => block.SetActive(false)).AddTo(this); } ....~~いろいろ略~~ }
その3に続く...(予定)
// SerializeFieldをゲットする。componentはなんらかのComponentを指定する。
var fields = component.GetType()
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy)
.SelectMany(x => x.CustomAttributes.Where(t => t.AttributeType == typeof(SerializeField)).Select(_ => x));
UnityでUniRxとZenjectを使ってのソシャゲを意識したシーン遷移基盤を趣味で作ってます。
仕事ではUniRxは使っているのですが、Zenjectは仕事では使ったことがありません。 最近はやりのZenjectの練習もかねて、UniRxとZenjectを組み合わせればいいものができるのではないかという 実験的な試みとして、ゆるゆると作っていきたいと思います。
すでに作ったものの基本的な機能を挙げていくと...
と、このあたりをすでに実装してます。
NavigationService
のNavigateAsync
メソッドを呼ぶと、内部でZenjectSceneLoader
でLoadScene
し、
遷移先にSceneBase
コンポーネントがあることを想定してFindし、
SceneBase
コンポーネントのPrepareSceneAsyncなどのインターフェイスを呼びだす流れとなっています。
NavigationService
クラス抜粋
public static class NavigationService { static public IMessageBroker Broker { get; } = new MessageBroker(); static public IAsyncMessageBroker AsyncBroker { get; } = new AsyncMessageBroker(); static Stack<SceneParam> sceneStack = new Stack<SceneParam>(); static bool isNavigating; static ZenjectSceneLoader loader => ProjectContext.Instance.Container.Resolve<ZenjectSceneLoader>(); /// <summary> /// シーンを加算ロードします。 /// </summary> /// <param name="sceneName">読み込むシーン名</param> /// <param name="argument">読み込むシーンで必要となる場合はパラメータを与えます</param> /// <returns></returns> public static IObservable<Unit> NavigateAsync(string sceneName, object argument = null) { var sceneParam = new SceneParam(sceneName, argument, isStack: true, loadSceneMode: LoadSceneMode.Additive); return NavigateAsyncCore(sceneParam); } ~いろいろ省略してます~ static IEnumerator CoNavigateAsync(IEnumerable<SceneParam> sceneParams) { ~略~ yield return DispatchAsync<AsyncBeginLoadSceneEvent>(new AsyncBeginLoadSceneEvent()); foreach (var sceneParam in sceneParams) { yield return LoadSceneAsync(sceneParam, false); } yield return DispatchAsync<AsyncAfterLoadSceneEvent>(new AsyncAfterLoadSceneEvent()); foreach (var sceneParam in sceneParams) { if (sceneParam.SceneBase != null) yield return sceneParam.SceneBase.StartSceneAsync().ToYieldInstruction(); } }
SceneBase
クラス抜粋
public abstract class SceneBase : MonoBehaviour { // これがシーン遷移時にセットされる引数を表す [InjectOptional(Id = "SceneBaseArgument")] public object Argument { get; protected set; } public virtual void ArgParseTo(SceneParam p) { } public bool IsLoaded { get; set; } public virtual IObservable<SceneBase> PrepareSceneAsync() { return Observable.Return(this); } public virtual IObservable<Unit> StartSceneAsync() { return Observable.Return(Unit.Default); } }
Unityちゃんの素材を使用しました。 © Unity Technologies Japan/UCL
と、今日はこのへんで... その2に続く...
UniRx+Zenjectで作るシーン遷移基盤 メモその2ZenjectBindingとか - 技術ブログ を書きました。
ShaderGraph
で炎が揺らゆくエフェクトを作成してみましたのでその作成方法をご紹介します。
炎の画像をゆらゆらと揺らしつつ、炎の下のほうは揺らめきを抑えています。
画像のように大きく3つの部分から構成されています。 3つのグループをそれぞれ解説します。
UVにスクロールするノイズUV成分を足すことで炎の揺らぎを表現します。
Gradient Noise
ノードをノイズとして使っています。
が、そのままでは静止したままですので、Timeノード
をごにょごにょとつなげていくことで
UVをスクロールさせています。
fraction
ノードは小数部分を取り出すノードです。
炎らしくするために、下の方ほど揺らがないようにするための成分を作成しています。
UV
ノードからV成分を取り出すためにSplit
ノードにつないでます。
炎画像 - WrathGames Studio
https://bitbucket.org/macnaga/scriptwizard/src/master/Assets/Editor/ScriptWizard.cs
Exposed References
にSerializeFieldをAdd
ボタンにて追加します。
フィールドにオブジェクトの参照をリンクします。
Use [SerializeField]
チェックを入れておくと、publicフィールドとしてではなく、[SerializeField]として追加します。
Create
でスクリプトが生成されます。
Create
時にCreate And AddComponent
チェックを入れてAddComponentTo
にゲームオブジェクトを指定すると、
Exposed References
に指定されていたオブジェクトへの参照を維持した状態で
そのゲームオブジェクトに新規スクリプトをAddComponentします。
Unityシーン遷移時に引数(パラメータ)を渡したい時ってあると思います。 インターフェイスを実装したら引数が渡されてくれれば便利かなと思ったので実装してみます。
このようなインターフェイスを定義します。
using UnityEngine; using UnityEngine.EventSystems; public interface ISceneWasLoaded : IEventSystemHandler { void OnSceneWasLoaded(object argument); }
引数を指定できるバージョンの LoadSceneWithArg というメソッドを作成します。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.SceneManagement; public static class SceneManagerEx { static public void LoadSceneWithArg( string sceneName, object argument, LoadSceneMode mode) { UnityAction<Scene, LoadSceneMode> sceneLoaded = default; Action removeHandler = () => { SceneManager.sceneLoaded -= sceneLoaded; }; sceneLoaded = (loadedScene, sceneMode) => { removeHandler(); foreach (var root in loadedScene.GetRootGameObjects()) { ExecuteEvents.Execute<ISceneWasLoaded>(root, null, (receiver, e) => receiver.OnSceneWasLoaded(argument)); } }; SceneManager.sceneLoaded += sceneLoaded; SceneManager.LoadScene(sceneName, mode); } }
遷移先のシーンでISceneWasLoadedインターフェイスを実装して、引数をとれるようにします。
public class HogeScene : MonoBehaviour, ISceneWasLoaded { public void OnSceneWasLoaded(object argument) { Debug.Log("OnSceneWasLoaded."); } }
SceneManagerEx.LoadSceneWithArg("HogeScene", new Hoge(), LoadSceneMode.Additive);
遷移後のHogeSceneにて OnSceneWasLoadedが呼ばれます。