ゲーム開発
Unity
UnrealEngine
C++
Blender
ゲーム数学
ゲームAI
グラフィックス
サウンド
アニメーション
GBDK
制作日記
IT関連
ツール開発
フロントエンド関連
サーバサイド関連
WordPress関連
ソフトウェア設計
おすすめ技術書
音楽
DTM
楽器・機材
ピアノ
ラーメン日記
四コマ漫画
その他
おすすめアイテム
おもしろコラム
  • ゲーム開発
    • Unity
    • UnrealEngine
    • C++
    • Blender
    • ゲーム数学
    • ゲームAI
    • グラフィックス
    • サウンド
    • アニメーション
    • GBDK
    • 制作日記
  • IT関連
    • ツール開発
    • フロントエンド関連
    • サーバサイド関連
    • WordPress関連
    • ソフトウェア設計
    • おすすめ技術書
  • 音楽
    • DTM
    • 楽器・機材
    • ピアノ
  • ラーメン日記
    • 四コマ漫画
      • その他
        • おすすめアイテム
        • おもしろコラム
      1. ホーム
      2. 20241031_01_unity_movie_maker

      【Unity】Timeline × Excelでスライドショーを効率よく制作する

      Unityツール開発DOTWeenTimelineUnity Excel Importer Maker
      2024-10-31

      マイケル
      マイケル
      みなさんこんにちは! マイケルです!
      エレキベア
      エレキベア
      こんにちクマ〜〜〜
      マイケル
      マイケル
      今回、急遽スライドショーの動画を作ることになったのですが、 写真の数が何百枚もあり、一つずつ配置、調整していっては時間がいくらあっても足りない・・・! といった状況に陥っていました。
      エレキベア
      エレキベア
      スライドショー作成はあるあるクマね・・・
      マイケル
      マイケル
      制作方法として、よくあるスライドショー制作アプリでは自由度が少ないし、 PremireやFinalCutPro用のツールを作ろうにも学習している時間もない! そういうわけで、Unityを使用して効率よく動画作成が行えないか?ということでTimelineを使用した動画制作にトライしてみました。
      エレキベア
      エレキベア
      そこでUnityを使うとは思い切ったクマね・・・ まあ確かにシステムさえ作ってしまえば制御は自由に行えそうクマ
      マイケル
      マイケル
      今回使用したUnityのバージョン、GitHubリポジトリについては下記になります! 同じようにTimelineを活用して動画作成したい方は、よければご参考ください!

      [Unityバージョン]
      6000.0.25f1

      GitHub - unity-photo-movie-maker / plasmo310

      エレキベア
      エレキベア
      解説が楽しみクマ〜〜〜

      スライドショーの作成イメージ

      マイケル
      マイケル
      実際には5分程度の動画を作成したのですが、今回のサンプルでは下記のようなショートムービーを用意しています。
      20241031_01_unity_movie_maker
      ▲サンプル用に作成したスライドショー

      エレキベア
      エレキベア
      家のネコクマ・・・
      マイケル
      マイケル
      入力データとしてExcelを使用して、どの画像を使用するかなどの情報も全てここから指定できるようにしています。 こちらはUnity Excel Importer Makerというライブラリを使用して、ExcelからScriptableObjectに変換することで使用しています!
      20241031_01_unity_movie_maker_01
      ▲入力データはExcelを使用してパラメータ調整する

      エレキベア
      エレキベア
      なるほどクマ これなら調整やパターンの複製もやりやすそうクマね
      マイケル
      マイケル
      そしてこのデータを元に、最終的にはUnityのTimeline上にキーフレームを生成して動画を制作するという流れです!
      20241031_01_unity_movie_maker_02
      ▲最終的にはUnityのTimeline形式で出力する

      マイケル
      マイケル
      アニメーションはDOTweenを使用して作っています。 今回はフェード、移動しか実装していないですが、工夫によって様々なアニメーションも追加できると思います!
      エレキベア
      エレキベア
      DOTweenがあれば何でも出来たようなもんクマね

      ScriptableObjectを使用したTimeline制御

      マイケル
      マイケル
      まずExcelを使用する前に、ScriptableObjectからデータを読み込んで指定フレームにキーを打ってスライド表示出来るようにしてみます。
      エレキベア
      エレキベア
      ScriptableObjectからスライドを制御する方法になるクマね

      スライド制御用データの準備

      マイケル
      マイケル
      まずはスライド制御用のデータをScriptableObjectとして用意します。 こちらは後ほど Unity Excel Importer Marker の使用で自動で作成されますが、その前に手動で用意して試してみるのもよいでしょう。
      using UnityEngine;
      using System.Collections;
      using System.Collections.Generic;
      
      public class SlideData : ScriptableObject
      {	
      	public List<Sheet> sheets = new List<Sheet> ();
      
      	[System.SerializableAttribute]
      	public class Sheet
      	{
      		public string name = string.Empty;
      		public List<Param> list = new List<Param>();
      	}
      
      	[System.SerializableAttribute]
      	public class Param
      	{
      		
      		public double Frame;
      		public string TexturePath;
      		public string Message;
      		public double PositionX;
      		public double PositionY;
      		public double MoveSpeedX;
      		public double MoveSpeedY;
      		public double Scale;
      		public double ShowDuration;
      		public double FadeInDuration;
      		public double FadeOutDuration;
      	}
      }
      
      
      ▲スライド制御用データ
      パラメータ名
      概要
      Frame
      キーフレームを生成するフレーム(sec)
      TexturePath
      表示するテクスチャパス
      Message
      (テクスチャを使用しない場合)表示するテキスト
      PositionX、PositionY
      表示する位置(スクリーン座標)
      MoveSpeedX、MoveSpeedY
      移動速度
      Scale
      テクスチャ、テキストの大きさ
      ShowDuration
      表示時間(sec)
      FadeInDuration
      フェードイン時間(sec)
      FadeOutDuration
      フェードアウト時間(sec)
      エレキベア
      エレキベア
      最低限のパラメータという感じクマね このデータを使用してスライドを制御するということクマか

      データからスライドを制御する

      マイケル
      マイケル
      それでは用意したデータを使用してスライドの制御処理を実装してみます。 スライド画像用のUIのImageコンポーネントを用意して、下記のようなスクリプトをアタッチします。
      20241031_01_unity_movie_maker_03
      ▲Imageコンポーネントを用意

      using DG.Tweening;
      using UnityEngine;
      using UnityEngine.UI;
      
      namespace MovieMaker.Slide.UI
      {
          /// <summary>
          /// スライド画像UI
          /// </summary>
          public class SlideImage : MonoBehaviour
          {
              /// <summary>
              /// スライドImageUI
              /// </summary>
              [SerializeField] private Image _slideImage;
      
              /// <summary>
              /// スライドRectTransform
              /// </summary>
              [SerializeField] private RectTransform _slideRectTransform;
      
              /// <summary>
              /// 表示中か?
              /// </summary>
              private bool _isShowing = false;
              public bool IsShowing => _isShowing;
      
              private void Start()
              {
                  // 最初は非表示
                  Hide();
              }
      
              /// <summary>
              /// スライドデータの設定
              /// </summary>
              /// <param name="slideData">画像データ</param>
              public void SetData(SlideData.Param slideData)
              {
                  ClearData();
      
                  // 名前からテクスチャを取得
                  var newTexture = Resources.Load<Texture2D>(slideData.TexturePath);
                  if (newTexture == null)
                  {
                      Debug.LogError($"not found texture => {slideData.TexturePath}");
                      return;
                  }
      
                  // スプライトの設定
                  var newSprite = Sprite.Create(
                      newTexture,
                      new Rect(0, 0, newTexture.width, newTexture.height),
                      new Vector2(0.5f, 0.5f));
                  _slideImage.sprite = newSprite;
                  _slideRectTransform.localPosition = new Vector3(
                      (float) slideData.PositionX * (Screen.width / 2.0f),
                      (float) slideData.PositionY * (Screen.height / 2.0f),
                      0.0f);
                  _slideRectTransform.sizeDelta = new Vector2(
                      (float) slideData.Scale * newTexture.width,
                      (float) slideData.Scale * newTexture.height);
              }
      
              /// <summary>
              /// 画像データのクリア
              /// </summary>
              public void ClearData()
              {
                  _slideImage.sprite = null;
                  _slideRectTransform.localPosition = Vector3.zero;
                  _slideRectTransform.sizeDelta = Vector2.one;
              }
      
              /// <summary>
              /// 表示処理
              /// </summary>
              /// <param name="slideData">画像データ</param>
              public void Show(SlideData.Param slideData)
              {
                  _isShowing = true;
      
                  // 画像データ設定
                  SetData(slideData);
      
                  // アニメーション開始
                  var sequence = DOTween.Sequence();
      
                  // 移動処理
                  var moveSpeedX = (float) slideData.MoveSpeedX;
                  var moveSpeedY = (float) slideData.MoveSpeedY;
                  var movePosition = new Vector3(moveSpeedX, moveSpeedY, 0f) * (float) slideData.ShowDuration;
                  if (movePosition.sqrMagnitude > Mathf.Epsilon)
                  {
                      transform.DOMove(movePosition, (float)slideData.ShowDuration)
                          .SetRelative()
                          .SetEase(Ease.Linear);
                  }
      
                  // フェードイン
                  var tmpColor = _slideImage.color;
                  tmpColor.a = 0.0f;
                  _slideImage.color = tmpColor;
                  sequence.Append(DOTween.ToAlpha(
                      () => _slideImage.color,
                      color => _slideImage.color = color,
                      1.0f,
                      (float) slideData.FadeInDuration
                      ));
      
                  // 表示待機時間
                  var showDuration = slideData.ShowDuration - slideData.FadeInDuration - slideData.FadeOutDuration;
                  sequence.AppendInterval((float) showDuration);
      
                  // フェードアウト
                  sequence.Append(DOTween.ToAlpha(
                      () => _slideImage.color,
                      color => _slideImage.color = color,
                      0.0f,
                      (float) slideData.FadeOutDuration
                  ));
      
                  // 表示フラグをオフにする
                  sequence.AppendCallback(() =>
                  {
                      _isShowing = false;
                  });
              }
      
              /// <summary>
              /// 非表示処理
              /// </summary>
              public void Hide()
              {
                  ClearData();
      
                  // 画像非表示
                  var tmpColor = _slideImage.color;
                  tmpColor.a = 0.0f;
                  _slideImage.color = tmpColor;
              }
          }
      }
      
      
      ▲スライド画像用のUI制御クラス
      マイケル
      マイケル
      初めは非表示にしておき、Show関数を呼び出した際にデータ設定、アニメーションを行うといった内容になっています。
      エレキベア
      エレキベア
      まあシンプルな実装クマね フェード等のDOTweenでのアニメーション制御はここで行なっているクマね
      マイケル
      マイケル
      こちらはスライド画像用の処理になりますが、今回はテキスト表示用の処理も別途用意しました。
      エレキベア
      エレキベア
      これでスライド画像、テキストの2種類のUIが出来たクマね

      Timelineのキーフレームからデータを渡す

      マイケル
      マイケル
      そしてTimelineからUIにデータを渡すはにどうすればよいかというと、 PlayableBehaviour、PlayableAssetクラスをoverrideして専用のクラスを作成する必要があります。 PlayableBehaviourはトラック再生時などの挙動を実装するクラスで、PlayableAssetはTimeline上にトラックとして生成するクラスです。 今回実装した内容は下記のようになっています。

      参考:
      Playables.PlayableBehaviour - Unity Documentation

      using System.Linq;
      using MovieMaker.Slide.UI;
      using UnityEngine;
      using UnityEngine.Playables;
      
      namespace MovieMaker.Slide
      {
          /// <summary>
          /// スライドデータPlayableBehavior
          /// </summary>
          public class SlidePlayableBehaviour : PlayableBehaviour
          {
              /// <summary>
              /// スライドデータ
              /// </summary>
              public SlideData.Param SlideData { set; private get; }
      
              /// <summary>
              /// 文字列未設定時の値
              /// </summary>
              private const string StringNoneValue = "-";
      
              /// <summary>
              /// PlayableAsset再生時処理
              /// </summary>
              /// <param name="playable"></param>
              /// <param name="info"></param>
              public override void OnBehaviourPlay(Playable playable, FrameData info)
              {
                  base.OnBehaviourPlay(playable, info);
      
                  if (SlideData == null)
                  {
                      Debug.LogError($"not set image data => {info.frameId}");
                      return;
                  }
      
                  if (SlideData.TexturePath != StringNoneValue)
                  {
                      // ========== 画像の場合 ==========
                      // Scene上からSlideImageオブジェクトを取得
                      var slideImageArray = GameObject.FindObjectsByType<SlideImage>(FindObjectsSortMode.None);
      
                      // 表示中でないオブジェクトに対してデータを設定して表示する
                      var slideImage = slideImageArray.FirstOrDefault(s => !s.IsShowing);
                      if (slideImage == null)
                      {
                          Debug.LogError($"all image showing => {info.frameId}");
                          return;
                      }
                      slideImage.Show(SlideData);
                  }
                  else if (SlideData.Message != StringNoneValue)
                  {
                      // ========== テキストの場合 ==========
                      // Scene上からSlideImageオブジェクトを取得
                      var slideTextArray = GameObject.FindObjectsByType<SlideText>(FindObjectsSortMode.None);
      
                      // 表示中でないオブジェクトに対してデータを設定して表示する
                      var slideText = slideTextArray.FirstOrDefault(s => !s.IsShowing);
                      if (slideText == null)
                      {
                          Debug.LogError($"all text showing => {info.frameId}");
                          return;
                      }
                      slideText.Show(SlideData);
                  }
              }
          }
      }
      
      
      ▲トラック再生時の挙動を実装
      using UnityEngine;
      using UnityEngine.Playables;
      
      namespace MovieMaker.Slide
      {
          /// <summary>
          /// スライドデータPlayableAsset
          /// </summary>
          public class SlidePlayableAsset : PlayableAsset
          {
              /// <summary>
              /// スライド情報(初期設定用)
              /// </summary>
              [SerializeField] private SlideData.Param _slideData;
      
              /// <summary>
              /// スライド情報の設定
              /// </summary>
              /// <param name="slideData"></param>
              public void SetSlideData(SlideData.Param slideData)
              {
                  _slideData = slideData;
              }
      
              public override Playable CreatePlayable(PlayableGraph graph, GameObject owner)
              {
                  // playableのBehaviourにデータを設定して返却
                  var playable = ScriptPlayable<SlidePlayableBehaviour>.Create(graph);
                  var playableBehaviour = playable.GetBehaviour();
                  playableBehaviour.SlideData = _slideData;
                  return playable;
              }
          }
      }
      
      ▲Timeline上にトラックとして生成するクラス
      マイケル
      マイケル
      今回はPlayableAssetに設定したデータをPlayableBehaviour側で受け取り 再生時にスライド用オブジェクトを取得して設定する処理になっています。
      マイケル
      マイケル
      ここまででPlayableTrack上で右クリックすると作成したPlayableAssetを追加できるようになっているはずです。
      20241031_01_unity_movie_maker_04
      ▲PlayableTrack上で作成したPlayableAssetを追加できるようになっている

      20241031_01_unity_movie_maker_05
      ▲パラメータも編集可能

      エレキベア
      エレキベア
      やったクマ〜〜〜 これで土台が整ったクマね

      Excelからのキーフレーム自動生成

      マイケル
      マイケル
      それではここから制作効率化のために ・ExcelからのScriptableObjectデータ生成 ・データからのキーフレーム自動生成 の仕組みを用意していきます。
      エレキベア
      エレキベア
      効率よく進めるためにはここが重要クマね

      Excelからデータを生成する

      マイケル
      マイケル
      ExcelからScriptableObjectに変換する処理に関しては、今回は外部ライブラリの Unity Excel Importer Maker を使用させていただきました。 導入、使用方法については下記記事にまとめてありますので、こちらをご参照ください!
      【Unity】「Unity Excel Importer Maker」を使ってExcelからScriptableObjectに変換する
      2022-08-25
      エレキベア
      エレキベア
      いつもお世話になってるクマ〜〜〜
      マイケル
      マイケル
      データ作成、変換に関しては、下記のようにExcel上にデータを用意し Unity上で 右クリック > XLS Import Settings を選択することで ScriptableObjectが生成されるといった流れになっています。
      20241031_01_unity_movie_maker_01
      ▲Excelデータを用意する

      20241031_01_unity_movie_maker_06
      ▲右クリックから生成

      20241031_01_unity_movie_maker_07
      ▲ScriptableObjectが生成される

      エレキベア
      エレキベア
      これでパラメータ調整がだいぶ楽になるクマね

      データからキーフレームを自動生成する

      マイケル
      マイケル
      最後に今回の肝となる仕組みとして、データからキーフレームを自動生成する処理を実装します。 下記のようにTimelineアセットのInspector内に生成用の項目をエディタ拡張で追加しました。
      20241031_01_unity_movie_maker_08
      ▲TimelineアセットのInspector内に生成用の項目を追加

      エレキベア
      エレキベア
      ここに設定したデータからスライド用のキーフレームを生成するクマね
      マイケル
      マイケル
      実装内容は下記のようになっています。 CreateTrack関数でPlayableTrack、CreateInstance関数でPlayableAssetを生成し、 CreateClip関数で生成したクリップにPlayableAssetを設定するといった内容になっています。
      using System.Collections.Generic;
      using System.IO;
      using MovieMaker.Slide;
      using UnityEditor;
      using UnityEngine;
      using UnityEngine.Timeline;
      
      namespace MovieMaker.Editor
      {
          /// <summary>
          /// スライドTimelineエディタ拡張
          /// ScriptableObjectデータからKeyframe用のGUIを表示する
          /// </summary>
          [CustomEditor(typeof(TimelineAsset))]
          public class SlideTimelineEditor : UnityEditor.Editor
          {
              /// <summary>
              /// スライド情報(内部保持用)
              /// </summary>
              private SlideData _slideData;
      
              /// <summary>
              /// Timeline格納パス
              /// </summary>
              private static readonly string TimelineDirPath = "Assets/MovieMaker/Timeline";
      
              /// <summary>
              /// Slideトラック格納パス
              /// ※トラック生成時に保存される
              /// </summary>
              private static readonly string SlideTracksDirPath = $"{TimelineDirPath}/SlideTracks";
      
              /// <summary>
              /// DummyTimelineアセットパス
              /// ※更新後のタイムライン表示を切り替えるために一時的に切り替える
              /// </summary>
              private static readonly string DummyTimelineAssetPath = $"{TimelineDirPath}/Dummy/DummyTimeline.playable";
      
              /// <summary>
              /// 生成対象のトラック名
              /// </summary>
              private const string SlideTrackName = "SlidePlayableTrack";
      
              public override void OnInspectorGUI()
              {
                  base.OnInspectorGUI();
      
                  // キーフレーム作成用のGUI表示
                  EditorGUILayout.Separator();
                  EditorGUILayout.LabelField("Slide Track Generate", EditorStyles.boldLabel);
      
                  // TODO: ObjectFieldだと保存されないので、本当はSerializedFieldを使いたい.
                  // TODO: しかしその場合にはCustomのTimelineAssetを作成する必要があり、Timelineアセットの生成など面倒なことになりそうなので保留.
                  _slideData = (SlideData) EditorGUILayout.ObjectField("Slide Data", _slideData, typeof(SlideData), false);
      
                  // Generateボタン
                  if (GUILayout.Button("Generate"))
                  {
                      // Timelineの内容が動的に更新されないため、一時的に他のアセットを選択
                      var dummy = AssetDatabase.LoadAssetAtPath<TimelineAsset>(DummyTimelineAssetPath);
                      Selection.activeObject = dummy;
      
                      // トラック生成
                      var timelineAsset = (TimelineAsset) target;
                      GenerateTracks(timelineAsset, _slideData);
                      EditorUtility.SetDirty(timelineAsset);
      
                      // 選択しなおすことでTimelineを強制再描画
                      Selection.activeObject = timelineAsset;
                  }
              }
      
              /// <summary>
              /// PlayableTrack生成
              /// 管理を簡潔にするために 1スライド/1トラック で生成する
              /// </summary>
              /// <param name="playableTrack">PlayableTrack</param>
              /// <param name="slideDaraParam">スライド情報データ</param>
              private static void GenerateTrack(PlayableTrack playableTrack, SlideData.Param slideDaraParam)
              {
                  // SlidePlayableAssetの作成
                  var slidePlayableAsset = ScriptableObject.CreateInstance<SlidePlayableAsset>();
                  slidePlayableAsset.SetSlideData(slideDaraParam);
                  var slidePlayableAssetPath = Path.Join(SlideTracksDirPath, $"{playableTrack.name}.asset");
                  AssetDatabase.CreateAsset(slidePlayableAsset, slidePlayableAssetPath);
      
                  // Clipに設定
                  var clip = playableTrack.CreateClip<SlidePlayableAsset>();
                  clip.asset = slidePlayableAsset;
                  clip.start = slideDaraParam.Frame;
                  clip.duration = slideDaraParam.ShowDuration;
              }
      
              /// <summary>
              /// 全てのPlayableTrack生成
              /// </summary>
              /// <param name="timelineAsset">Timelineアセット</param>
              /// <param name="slideData">スライド情報データ</param>
              private static void GenerateTracks(TimelineAsset timelineAsset, SlideData slideData)
              {
                  if (slideData == null)
                  {
                      Debug.LogError("please set slide data.");
                      return;
                  }
      
                  // 作成済のキーフレームを削除する
                  var deleteTracks = new List<TrackAsset>();
                  foreach (var outputTrack in timelineAsset.GetOutputTracks())
                  {
                      if (outputTrack.name.Contains(SlideTrackName))
                      {
                          deleteTracks.Add(outputTrack);
                      }
                  }
                  foreach (var deleteTrack in deleteTracks)
                  {
                      var deleteTrackName = deleteTrack.name;
                      timelineAsset.DeleteTrack(deleteTrack);
                      Debug.Log($"delete track => {deleteTrackName}");
                  }
      
                  // 作成済のスライド情報アセットを削除
                  var slideDataAssetGuids = AssetDatabase.FindAssets("t:ScriptableObject", new[] { SlideTracksDirPath });
                  foreach (var slideDataAssetGuid in slideDataAssetGuids)
                  {
                      var assetPath = AssetDatabase.GUIDToAssetPath(slideDataAssetGuid);
                      AssetDatabase.DeleteAsset(assetPath);
                      Debug.Log($"delete asset => {assetPath}");
                  }
      
                  // AssetDatabaseを更新
                  AssetDatabase.Refresh();
      
                  // パラメータごとにTrackを生成する
                  var slideTrackIndex = 1;
                  foreach (var sheet in slideData.sheets)
                  {
                      // パラメータからTrackを生成
                      foreach (var param in sheet.list)
                      {
                          var trackName = $"{SlideTrackName}_{slideTrackIndex}";
                          var track = timelineAsset.CreateTrack<PlayableTrack>(null, trackName);
                          GenerateTrack(track, param);
                          slideTrackIndex++;
                          Debug.Log($"create track => {trackName}");
                      }
                  }
              }
          }
      }
      
      
      ▲データからキーフレームを生成する
      エレキベア
      エレキベア
      いろいろ生成しているクマが、まあやっていることは分かるクマ
      マイケル
      マイケル
      今回の実装の注意点として、 ・ObjectFieldとして表示しているため、設定したScriptableObjectが毎回外れてしまう ・表示中のTimelineの内容が動的に更新されない といった問題がありました。
      マイケル
      マイケル
      Timelineの更新については、DummyのTimelineアセットを用意して選択し直すことで無理やり更新させることができましたがもっと上手い方法もあるかもしれません。 今回は時間の都合上、深く調査していないですが気になる方はこの辺りの改善にもトライしてみてください!
      エレキベア
      エレキベア
      まあ使う上で最低限の部分だけ実装した感じクマね

      おわりに

      マイケル
      マイケル
      というわけで今回はTimelineを使用したスライドショー作成についてでした! どうだったかな?
      エレキベア
      エレキベア
      ゲーム開発に使うイメージだったクマが、案外動画編集にも使えるクマね
      マイケル
      マイケル
      凝った動画演出はやはり専用のソフトの方がいいかもしれないけど、 無料で使えるかつ最低限の機能が揃っている & スクリプトが書ければ自由に制御できる というのは大きなメリットだと思いました! そして今回は5分程度の動画を作ったのですが、普通に作るよりも圧倒的に早く思い描いたものを作れたので成功だったと思います!
      マイケル
      マイケル
      またExcelベースで作る手法に関しては、Premiere等の動画編集ソフトでも同様にツールが作れると思うのでぜひトライしてみたいですね!
      マイケル
      マイケル
      それでは今回はこの辺で・・・
      エレキベア
      エレキベア
      アデュークマ〜〜〜〜

      【Unity】Timeline × Excelでスライドショーを効率よく制作する 〜完〜


      Unityツール開発DOTWeenTimelineUnity Excel Importer Maker
      2024-10-31

      関連記事
      【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する
      2024-05-28
      【Unity】GoでのランキングAPI実装とVPSへのデプロイ方法についてまとめる【Go言語】
      2024-04-14
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30
      【Unity】第二回 CRI ADXを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-28
      【Unity】第一回 CRI ADXを使用したサウンド制御 〜基本動作、周波数解析編〜
      2024-03-28
      【Unity】サウンドミドルウェアに依存しない設計を考える【CRI ADX・Wwise】
      2024-03-27
      【Unity】第三回 UnityAudioを使いこなす 〜オーディオスペクトラム描画編〜
      2024-01-22