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

      【Unity】「ゆるいオセロ」をリリース!工夫点や反省点をざっと振り返る

      Unity制作日記ゆるいオセロ
      2022-10-14

      マイケル
      マイケル
      みなさんこんにちは!
      マイケルです!
      エレキベア
      エレキベア
      こんにちクマ〜〜〜
      マイケル
      マイケル
      本日は一点ご報告です!
      我々都会のエレキベアは先週ついに・・・

      ゆるいオセロ
      MOLEGORO 無料
      ゆるいキャラクター達と楽しむ3Dオセロゲーム

      マイケル
      マイケル
      ここ最近ずっと作っていた
      オセロゲームをリリース
      しました!!
      エレキベア
      エレキベア
      おおお〜〜〜
      やったクマ〜〜〜〜〜〜
      マイケル
      マイケル
      これでゲームアプリは5本目になるわけだけど、
      中々綺麗に作れた気がするよ!(面白いかは置いといて・・・)
      エレキベア
      エレキベア
      (そこは置いたらだめなんじゃ・・・)
      マイケル
      マイケル
      3ヶ月くらい作っていたせいで、夢の中にまでオセロが出てきた始末・・・
      そんな苦しい開発を振り返っていきます!!
      興味のある方はお付き合いくださいませ!
      エレキベア
      エレキベア
      地獄のオセロ開発クマ〜〜〜
      マイケル
      マイケル
      なおスクリプトだけにはなりますが、GitHubの方にも挙げていますので、
      こちらも是非ご参照ください!!

      GitHub – unity-reversi-game-scripts / masarito617

      エレキベア
      エレキベア
      コードも中々の量になったクマね〜〜

      工夫・挑戦した点

      マイケル
      マイケル
      まずは 工夫・挑戦した点 から!
      理由もなくこんなオセロゲームを作っていたわけではなく、スキルアップのために様々なことに挑戦しました!
      エレキベア
      エレキベア
      (こんなって言っちゃった・・・)
      マイケル
      マイケル
      具体的には下記のような点になります!


      ・DIコンテナの使用と依存関係の整理
      ・様々なオセロAIの実装
      ・Cinemachineでのカメラワーク実装
      ・DOTweenでのアニメーション実装
      ・UIの効率化と統一
      ・キャラクターモデルの作成

      エレキベア
      エレキベア
      このオセロ開発でいろんなことを学んだクマね〜〜(しみじみ)
      マイケル
      マイケル
      一つ一つ見ていくぜ!

      DIコンテナの使用と依存関係の整理

      マイケル
      マイケル
      まずはDIコンテナの使用と依存関係の整理について!
      これは開発前から決めていたことで、
      ・VContainer
      ・UniTask
      ・UniRx

      といったUnity定番のライブラリを使用して、依存関係を意識しながら開発を進めました。
      エレキベア
      エレキベア
      確か前回はあえて何もライブラリを使わずに作っていたクマね
      マイケル
      マイケル
      その通り!
      前回は何が何を呼び出しているのかが非常に分かりにくかったけど、DIコンテナ(VContainer)を使用することで依存関係がかなり明確になった気がするよ!
      それからサービスクラスに関してもサービスロケータでなくDIコンテナでの登録に置き換えることもできたんだ!
                  // サービス登録
                  builder.Register<IAudioService, AudioService>(Lifetime.Singleton);
                  builder.Register<IAssetsService, AssetsService>(Lifetime.Singleton);
                  builder.Register<ILogService, LogDebugService>(Lifetime.Singleton);
                  builder.Register<ITransitionService, TransitionService>(Lifetime.Singleton);
                  builder.Register<IDialogService, DialogService>(Lifetime.Singleton);
                  // モバイルの場合、Admobを適用する
                  if (gameSettings.IsMobilePlatform)
                  {
                      builder.Register<IAdmobService, AdmobService>(Lifetime.Singleton);
                  }
                  else
                  {
                      builder.Register<IAdmobService, AdmobDummyService>(Lifetime.Singleton);
                  }
      ↑サービスクラスの登録
      エレキベア
      エレキベア
      DIコンテナを使用することで
      コンストラクタ経由での受け取りにすることができるクマね
      マイケル
      マイケル
      DIコンテナやVContainerについての詳細は、下記の記事をご参照ください!
      マイケル
      マイケル
      それからもう一つ、UniRxのReactivePropertyを使用することで
      UI側からオブジェクトの状態を監視するようにしたのも、実装しやすかったように感じます!
      ざっくりですが各パッケージの関係性は下記のようになっています。
      ↑UI側から状態を監視している
      エレキベア
      エレキベア
      前回はUI周りの呼び出しも複雑になっていたクマからね〜〜
      マイケル
      マイケル
      具体的な実装は下記のようになっています!
      ReactivePropertyで定義した変数を監視することで、UIの更新を行なっています。
          /// <summary>
          /// ストーン管理クラス
          /// </summary>
          public class StoneManager
          {
      ・・・略・・・
              /// <summary>
              /// 各色ごとのストーンの数
              /// </summary>
              private readonly ReactiveProperty<int> _whiteStoneCount = new(0);
              private readonly ReactiveProperty<int> _blackStoneCount = new(0);
              public IReadOnlyReactiveProperty<int> WhiteStoneCount => _whiteStoneCount;
              public IReadOnlyReactiveProperty<int> BlackStoneCount => _blackStoneCount;
      ↑ReactivePropertyの変数を定義
              /// <summary>
              /// ステート監視設定
              /// </summary>
              private void OnSetStateSubscribe()
              {
      ・・・略・・・
                  // ストーン数表示
                  _stoneManager
                      .WhiteStoneCount
                      .Subscribe(count => gameInfoView.SetWhiteCount(count))
                      .AddTo(this);
                  _stoneManager
                      .BlackStoneCount
                      .Subscribe(count => gameInfoView.SetBlackCount(count))
                      .AddTo(this);
                  // ゲーム状態による切替
                  _gameManager
                      .State
                      .Subscribe(x =>
                      {
                          switch (x)
                          {
                              // 開始表示
                              case GameState.Play:
                                  OnShowStart();
                                  break;
                              // 結果表示
                              case GameState.Result:
                                  OnShowResult();
                                  break;
                          }
                      }).AddTo(this);
              }
      ↑定義した変数を監視する
      エレキベア
      エレキベア
      これならUIを取り外すのも簡単そうクマね

      様々なオセロAIの実装

      マイケル
      マイケル
      そして次は何と言ってもオセロAIの実装です!
      これはもう・・・定番なアルゴリズムから強化学習までいろんなものを作りましたよ!
      詳しくは下記記事をご覧ください!!
      エレキベア
      エレキベア
      いや〜〜〜感慨深いクマ〜〜〜〜
      マイケル
      マイケル
      これらを組み合わせて最終的には 5(+3)キャラ分のAIを作成 しました。
      こうしてみるとかなり賑やかになりましたね!
      ↑作ったキャラ達
      マイケル
      マイケル
      詳細な内容は各記事に書いてあるので省きますが、下記のように感情によってアルゴリズムを切り替えているのがミソですね!
      using System;
      using System.Threading;
      using Cysharp.Threading.Tasks;
      using Reversi.Stones.Stone;
      namespace Reversi.Players.AI
      {
          /// <summary>
          /// エレキベア
          /// </summary>
          public class ElekiBearPlayer : Player
          {
              public ElekiBearPlayer(StoneState myStoneState, Action<StoneState, int, int> putStoneAction) : base(myStoneState, putStoneAction)
              {
              }
              protected override void StartThink()
              {
                  StartThinkAsync(CancellationTokenSource.Token);
              }
              /// <summary>
              /// 選択するストーンを考える
              /// </summary>
              private async void StartThinkAsync(CancellationToken token)
              {
                  // 考える時間
                  await WaitSelectTime(200, token);
                  // 早すぎると上手くいかないので1フレームは待つ
                  await UniTask.DelayFrame(1, cancellationToken: token);
                  // 感情によって選択手法を変える
                  switch (Emotion)
                  {
                      // 通常、焦り:MiniMax
                      case PlayerEmotion.Normal:
                      case PlayerEmotion.Heat:
                          SelectStoneIndex = await AIAlgorithm.SearchMultiThreadNegaAlphaStoneAsync(StoneStates, MyStoneState, 3, true, token);
                          break;
                      // 悲しい:ランダム
                      case PlayerEmotion.Sad:
                          SelectStoneIndex = AIAlgorithm.GetRandomStoneIndex(StoneStates, MyStoneState);
                          break;
                  }
              }
          }
      }
      
      ↑エレキベアのAI実装例
      エレキベア
      エレキベア
      もう少し賢く作ってほしかったクマ・・・

      Cinemachineでのカメラワーク実装

      マイケル
      マイケル
      それから今回はCinemachineも使用していて、
      カメラワークの実装にも挑戦してみました!
      例えばタイトル画面のボードをいろんな角度から切り替えて写している箇所ですね!
      ↑何か既視感のあるカメラワーク
      エレキベア
      エレキベア
      何かそれっぽいクマ〜〜
      マイケル
      マイケル
      これは下記のように一定時間ごとに各カメラのPriorityを切り替えることで表現しています。
              /// <summary>
              /// アニメーション開始処理
              /// </summary>
              /// <param name="token"></param>
              private async void StartAnimationAsync(CancellationToken token)
              {
                  _isPlayAnimation = true;
                  virtualCameraRound.Priority = 1;
                  // 順番にカメラを切り替える
                  while (_isPlayAnimation)
                  {
                      // Round
                      dollyCartRound.m_Position = 0.0f;
                      await UniTask.Delay(500, cancellationToken: token);
                      SetAllPriority(-1);
                      virtualCameraRound.Priority = 1;
                      dollyCartRound.m_Speed = 0.3f;
                      await UniTask.Delay(3000, cancellationToken: token);
                      // Dolly
                      dollyCartDolly.m_Position = 0.0f;
                      await UniTask.Delay(500, cancellationToken: token);
                      SetAllPriority(-1);
                      virtualCameraDolly.Priority = 1;
                      await UniTask.Delay(3000, cancellationToken: token);
                      // Up
                      dollyCartUp.m_Position = 0.0f;
                      await UniTask.Delay(500, cancellationToken: token);
                      SetAllPriority(-1);
                      virtualCameraUp.Priority = 1;
                      await UniTask.Delay(3000, cancellationToken: token);
                  }
              }
      ↑タイトル画面のカメラワーク
      マイケル
      マイケル
      Cinemachineの使い方については、下記記事にまとめてありますので
      こちらもよければご参照ください!
      エレキベア
      エレキベア
      Cinemachineは使いやすくて楽しかったクマね

      DOTweenでのアニメーション実装

      マイケル
      マイケル
      そして次はDOTweenアニメーション
      これは前回も使用していましたが、今回も思いつきでいろんな箇所のアニメーションを実装しています。
      マイケル
      マイケル
      まずはタイトルロゴのアニメーション!
      これは白背景の上に黒い円を配置して、ランダムでスケールを変更することで表現しています。
      ↑タイトルロゴのアニメーション
              [SerializeField] private Image whiteBackImage;
              [SerializeField] private Image frontTextImage;
              [SerializeField] private Image backThunderImage;
              [SerializeField] private List<Image> backCircleImageList;
              private List<Tween> _loopTweenList;
              private static readonly float MinCircleImageScale = 0.95f;
              private static readonly float MaxCircleImageScale = 1.06f;
              private static readonly Vector3 ThunderRotateVector3 = new Vector3(0.0f, 0.0f, 8.0f);
              private void Awake()
              {
                  // Alpha値を上げるSequenceを作成
                  var appearSequence = DOTween.Sequence();
                  appearSequence.AppendCallback(() => backThunderImage.gameObject.SetActive(false)); // 最初はイナズマ非表示
                  appearSequence.Append(CreateToAlphaTween(whiteBackImage, 1.0f));
                  appearSequence.Join(CreateToAlphaTween(frontTextImage, 1.0f));
                  backCircleImageList.ForEach(backCircleImage =>
                  {
                      appearSequence.Join(CreateToAlphaTween(backCircleImage, 1.0f));
                  });
                  // イナズマを遅れてひょいと出す
                  var thunderAppearSequence = DOTween.Sequence();
                  thunderAppearSequence.AppendInterval(0.6f);
                  thunderAppearSequence.AppendCallback(() =>
                  {
                      backThunderImage.transform.localEulerAngles = ThunderRotateVector3;
                      backThunderImage.gameObject.SetActive(true);
                  });
                  thunderAppearSequence.Join(CreateToAlphaTween(backThunderImage, 0.5f));
                  thunderAppearSequence.Join(backThunderImage.transform.DOLocalRotate(Vector3.zero, 0.5f));
                  appearSequence.Join(thunderAppearSequence);
                  // 大きさを徐々に変えるループアニメーションを作成
                  _loopTweenList = new List<Tween>();
                  foreach (var backCircleImage in backCircleImageList)
                  {
                      // 間隔と開始時間を適当にばらまく
                      var randomDelayTime = Random.Range(0.0f, 0.5f);
                      var randomDurationTime = Random.Range(1.0f, 1.5f);
                      backCircleImage.transform.localScale = Vector3.one * MinCircleImageScale;
                      _loopTweenList.Add(
                          backCircleImage.transform.DOScale(MaxCircleImageScale, randomDurationTime)
                              .SetDelay(randomDelayTime).SetLoops(-1, LoopType.Yoyo));
                  }
                  // イナズマのループアニメーション
                  var thunderLoopSequence = DOTween.Sequence();
                  thunderLoopSequence.AppendInterval(0.8f);
                  thunderLoopSequence.Append(backThunderImage.transform.DOLocalRotate(ThunderRotateVector3, 0.2f));
                  thunderLoopSequence.Append(backThunderImage.transform.DOLocalRotate(Vector3.zero, 0.2f));
                  thunderLoopSequence.SetLoops(-1, LoopType.Restart);
                  thunderLoopSequence.Pause();
                  DOTween.Sequence().AppendCallback(() => thunderLoopSequence.Restart()).SetDelay(1.0f);
                  _loopTweenList.Add(thunderLoopSequence);
              }
      ↑円とイナズマをアニメーションさせている
      エレキベア
      エレキベア
      長いクマ・・・
      マイケル
      マイケル
      それから下記のようなメッセージ表示にも使用しています。
      今回初めて DOTween.ToDOTween.ToAlpha を使用してみましたが、これらも中々汎用性が高そうだなと思いました。
      ↑メッセージ表示アニメーション
              public void ShowMessage(string text, UnityAction callback, float displayDelayTime = 0.0f)
              {
                  // テキスト設定
                  messageText.SetTextForDynamic(text);
                  // 背景がスライドしてテキストが浮かび上がる
                  var sequence = DOTween.Sequence();
                  sequence.AppendInterval(displayDelayTime);
                  sequence.AppendCallback(() =>
                  {
                      // 最初は非表示
                      messageBackgroundImage.fillAmount = 0.0f;
                      var c = messageText.color;
                      c.a = 0.0f;
                      messageText.color = c;
                      // オブジェクトを表示する
                      SetActiveMessageArea(true);
                  });
                  sequence.Append(DOTween.To(
                      () => messageBackgroundImage.fillAmount,
                      (x) => messageBackgroundImage.fillAmount = x,
                      1.0f,
                      0.5f
                  ));
                  sequence.Append(DOTween.ToAlpha(
                      () => messageText.color,
                      color => messageText.color = color,
                      1.0f,
                      0.2f
                      ));
                  // コールバックが設定されている場合
                  if (callback != null)
                  {
                      sequence.AppendInterval(1.0f);
                      sequence.AppendCallback(() =>
                      {
                          callback();
                      });
                  }
              }
      ↑アルファ値や特定の変数を変化させている
      エレキベア
      エレキベア
      任意の変数を変化させられるのは便利クマね
      マイケル
      マイケル
      そして最後は石がひっくり返るアニメーション
      これは挟んだ方向に対して順番に回転する演出を加えています!
      ↑挟んだ方向に対して回転する
              /// <summary>
              /// ひっくり返すアニメーション
              /// </summary>
              /// <param name="putStoneIndex"></param>
              /// <param name="callback"></param>
              private void StartTurnAnimation(StoneIndex putStoneIndex, Action callback)
              {
                  // アニメーションさせない場合
                  if (!IsDisplayAnimation)
                  {
                      callback();
                      return;
                  }
                  // 色が変わる場合
                  var putVec = Index - putStoneIndex;   // 置いた位置からのベクトル
                  var waitTime = putVec.GetLength() * 0.08f; // アニメーションを遅らせる時間(遠いほど開始を遅らせる)
                  var q = Quaternion.AngleAxis(180.0f, new Vector3(-putVec.Z, 0.0f, putVec.X)); // 90度回転させたクォータニオン(-Z,Xで指定)
      
                  // ひっくり返るアニメーションを実行
                  var sequence = DOTween.Sequence();
                  sequence.AppendInterval(waitTime);
                  sequence.Append(stoneObject.transform.DOLocalRotateQuaternion(q, 0.5f).SetRelative().SetEase(Ease.OutQuart));
                  sequence.Join(DOTween.Sequence()
                      .Append(stoneObject.transform.DOLocalMoveY(1, 0.25f).SetRelative().SetEase(Ease.OutQuart))
                      .Append(stoneObject.transform.DOLocalMoveY(-1, 0.2f).SetRelative().SetEase(Ease.InOutBounce)));
                  sequence.OnComplete(() =>
                  {
                      callback();
                  });
              }
      エレキベア
      エレキベア
      これは確かに気持ちいいかもしれないクマ
      マイケル
      マイケル
      DOTweenはマジで楽しいからこれからもバンバン使っていこう!

      UIを極力シンプルに統一

      マイケル
      マイケル
      そしてここからは技術的な話とは少し離れますが、
      UIに関して下記のProcedural UI Imageを使用することで全体のイメージを統一、素材作成の手間を軽減しました。
      マイケル
      マイケル
      これが中々シンプルで使い勝手がいいんですよね〜!
      具体的には下記のようなレイアウトになります。
      エレキベア
      エレキベア
      確かに統一感あって悪くないクマね〜〜
      むしろエンジニアが下手に作るよりは全然いいクマ・・・
      マイケル
      マイケル
      ちょこっとしたゲームを作るには丁度いいね!
      (オインクゲームズさんのUIに影響を受けてるのは内緒です)
      エレキベア
      エレキベア
      言っちゃったクマ・・・

      キャラクターモデルの作成

      マイケル
      マイケル
      そして最後に、今回はBlenderでキャラモデルを作ることにも挑戦しました!
      全7キャラ分のモデルについて全て手作業で作っています!
      ↑復習しながら謎のキャラが完成
      ↑エレキベアやマイケルもついに作成した
      エレキベア
      エレキベア
      ついにゲームに出演する時がきたクマか・・・
      マイケル
      マイケル
      流用できるから今後も活用していくつもりだよ!
      そして収穫として大きかったのは、しっかり記事にBlenderの使い方をまとめることができたことです!
      前回は面倒くさすぎてメモも残しておらず、結果しばらくしたら忘れてしまっていたのですが、今回はメモしたから大丈夫なはず!!
      エレキベア
      エレキベア
      細かく残して偉いクマ〜〜〜
      これでモデル作成も困ることはないクマね

      反省点

      マイケル
      マイケル
      たくさんのことを学んだオセロ開発でしたが、当然反省点もあります・・・。
      こちらもいくつか振り返っていきましょう!

      3D描画が少し重い

      マイケル
      マイケル
      一つ目は描画処理が割と重たいことです。
      URPに移行したりテクスチャサイズ、マテリアルを削減したり等の対処は行いましたが、
      それでも古い端末だと表示がカクつく場面が多々ある状態です・・・。
      ↑オセロするだけなのにオブジェクトも多い気がする
      エレキベア
      エレキベア
      これは中々難しいクマね・・・
      なるべく古い端末でも動くようにしたいところクマが・・・
      マイケル
      マイケル
      この辺はこれから描画周りを勉強していく予定なので、
      少しずつ改善していけたらと思います!

      リリース直前のWebGL対応で一苦労

      マイケル
      マイケル
      そして次はリリース直前のWebGL対応で一苦労したことです!
      実際にどのような対応を行ったのかは下記にまとめてあります。
      マイケル
      マイケル
      不具合が出ること自体は仕方ないのですが、問題はリリース直前でWebGL確認を行ったことです。
      もう少し早い段階で確認しておけばよかったなと反省しています・・・。
      エレキベア
      エレキベア
      リリース前の不具合は萎えるクマね・・・

      非同期処理のキャンセル処理は忘れずに

      マイケル
      マイケル
      次はコーディングの話になるのですが、UniTaskやDOTweenで処理を行なった際に、キャンセル処理を忘れてしまった箇所がいくつかありました。
      終盤にまとめて対処しましたが、これは実装時にキャンセル処理も合わせて書くよう意識した方がいいなと思いました。
                  private async void StartGameAsync(CancellationToken token)
                  {
                      // 一定時間待機
                      await UniTask.Delay(2300, cancellationToken: token);
                      // ゲーム開始
                      Owner._playerManager.StartGame();
                      Owner._playerManager.StartTurn();
                      _isGameStart = true;
                  }
      ↑キャンセル処理は忘れず書きましょう
      エレキベア
      エレキベア
      まあ確かに面倒くさいクマ・・・

      開発者都合なところも多い

      マイケル
      マイケル
      そして最後に重大な問題ですが、
      そもそもオセロで3Dにする必要はあったのか? という点です。
      エレキベア
      エレキベア
      全てをぶち壊す問題がきたクマ・・・
      マイケル
      マイケル
      モデル作成したかったり3Dを扱いたいという理由で3Dに振り切りましたが、
      オセロをすることがメインであることを考えると描画負荷も考慮して2Dでもよかったのでは?と思います・・・
      エレキベア
      エレキベア
      開発者都合が入ってしまったクマね
      マイケル
      マイケル
      この辺りは企画時に考えるべきことなので、次回はしっかりゲームの仕様や需要を考えた上で作ろうと思います!!

      おわりに

      マイケル
      マイケル
      というわけでオセロゲーム開発の振り返りでした!
      どうだったかな??
      エレキベア
      エレキベア
      割と長い時間をかけたから疲れたクマ〜〜〜
      マイケル
      マイケル
      俺も正直完成した時は「やっと解放される・・・」という嬉しさの方が勝っていたよ・・・
      マイケル
      マイケル
      でもやっぱりすごく楽しかったしよくやり切ったと思う!!
      早く次のゲームも作りたい!!!
      エレキベア
      エレキベア
      ゲーム開発はやはり奥が深いクマね
      マイケル
      マイケル
      アセット周りや入力周りはまだレガシーな状態で作ってしまったから、
      今後は描画周りの学習に加えて
      ・Addressable Asset System
      ・InputSystem

      あたりも触っていきたいな!
      エレキベア
      エレキベア
      今回もとにかくおつかれさまクマ
      次回もやったるクマ〜〜〜〜
      マイケル
      マイケル
      それでは今日はこの辺で!!
      アデューーー!!
      エレキベア
      エレキベア
      クマ〜〜〜

      【Unity】「ゆるいオセロ」をリリース!工夫点や反省点をざっと振り返る 〜完〜

      ゆるいオセロ
      MOLEGORO 無料
      ゆるいキャラクター達と楽しむ3Dオセロゲーム

      ↑よければ遊んでください!!!!


      Unity制作日記ゆるいオセロ
      2022-10-14

      関連記事
      【Unity】Timeline × Excelでスライドショーを効率よく制作する
      2024-10-31
      【Unity】Boidsアルゴリズムを用いて魚の群集シミュレーションを実装する
      2024-05-28
      【UE5】第三回 ミニゲーム制作で学ぶUnrealC++ 〜UI・仕上げ実装 編〜
      2024-05-18
      【UE5】第二回 ミニゲーム制作で学ぶUnrealC++ 〜キャラクター・ゲーム実装 編〜
      2024-05-18
      【UE5】第一回 ミニゲーム制作で学ぶUnrealC++ 〜UnrealC++の概要 編〜
      2024-05-18
      【Unity】GoでのランキングAPI実装とVPSへのデプロイ方法についてまとめる【Go言語】
      2024-04-14
      【Unity】第二回 Wwiseを使用したサウンド制御 〜インタラクティブミュージック編〜
      2024-03-30
      【Unity】第一回 Wwiseを使用したサウンド制御 〜基本動作編〜
      2024-03-30