unity1week「ちゅう」のゲームで実装したイベントアニメーション【unity1week,Unity】

今回プロなろというグループでチーム開発を行いました!

ジャンプキングに似たやりこみのあるゲームになっています

Repositoryは公開しているので,細かい実装はRepositoryを参照ください

github.com

(注) : 実装説明により一部ゲームのネタバレ等含みます

開発環境

  • Unity 2020.3.17f1
  • Github(主にCI/CD用)

使用アセット(関連するもののに記載)

UPM

Asset Store

イベントアニメーション

各画面のイベントアニメーション

各画面にて以下のようなゲームイベントが存在します

これらを実装するために,以下を採用しました

タイトルの背景アニメーション f:id:ayousanz:20210915212114g:plain

ゲーム開始時のアニメーション(ゲーム画面) f:id:ayousanz:20210915213622g:plain

ゲームクリア時のアニメーション(ゲーム画面) f:id:ayousanz:20210915214430g:plain

クリア画面の背景アニメーション f:id:ayousanz:20210915213107g:plain

ゲームオーバー画面のアニメーション f:id:ayousanz:20210915213319g:plain

実装方法

こちら基本的には DOTweenを 以下の方法で使用してイベントアニメーションを再生しています

  1. Sequenceを生成して,任意のところで再生
  2. trasform.DO~をUniTask型に変換して,awaitで前の処理が終わるまで待機

タイトルは Sequenceで実装しているので,以下のように実装しました

_sequence = DOTween.Sequence()
                .Append(transform.DOJump(new Vector3(3.65f, _firstYPosition, 0f), 0.3f, 10, 10f))
                .AppendCallback(() => characterDialogue.SetView(true).Forget())
                .AppendCallback(() => _rigidbody2D.bodyType = RigidbodyType2D.Dynamic)
                .Append(transform.DORotate(new Vector3(0f, 0f, 360f), 5f, RotateMode.FastBeyond360))
                .AppendCallback(() =>
                {
                    characterDialogue.SetView(false).Forget();
                    //ループ対応
                    Reset();
                });

            _sequence.Restart();

ゲームの開始時のアニメーションはDOTweenをawaitしています

        public async UniTask StartEvent()
        {
            await FallHole();
            await SetStartPosition();
            await Conversation();
        }
        
        private async UniTask FallHole()
        {
            player.DORotate(new Vector3(0f, 0f, 360f + 180f), 5f, RotateMode.FastBeyond360);
            await player.DOMoveY(-911f, 5f).ToAwaiter();
        }

        private async UniTask SetStartPosition()
        {
            player.DOJump(new Vector3(-45f, -900f, 0f), 200f, 1, 3f);
            await player.DORotate(new Vector3(0f, 0f, -180f), 3f).SetRelative(true).ToAwaiter();
        }

        private async UniTask Conversation()
        {
            await playerCharacterDialogue.SetView(true);
            playerCharacterDialogue.SetContent(firstConversationContent[0]);
            await UniTask.Delay(TimeSpan.FromSeconds(WaitBetweenConversationTime));
            playerCharacterDialogue.SetContent(firstConversationContent[1]);
            await UniTask.Delay(TimeSpan.FromSeconds(WaitBetweenConversationTime));
            await playerCharacterDialogue.SetView(false);
        }

ゲームのクリア情報のツイート機能

こちらの記事を参考に実装しました

画像のツイートは,できない時とできるときがあり調査時間がなかったため文字のみになりました

unity-senpai.hatenablog.com

クリア時間のランキング機能

unity roomのランキングはnaichiさんが公開されているものを使用させていただきました

時間は 〇分〇秒で表示したかったのでフォーマットを mm:ss に設定しています

blog.naichilab.com

開発運用(CI/CD)

今回の制作ではGithub Actionsを使って,テストとビルドを自動化しました

前に記事を書いたので,そちらを参考にしてみてください

ayousanz.hatenadiary.jp

設計

1週間の期間ですが,設計をある程度意識して実装をしています

UniRx,UniTaskを使ってMV(R)Pで実装をしています

各シーンのクラス図は以下のようになっています

タイトルシーン f:id:ayousanz:20210915220125p:plain

ゲームシーン f:id:ayousanz:20210915220137p:plain

ゲームクリアシーン f:id:ayousanz:20210915220144p:plain

ゲームオーバーシーン f:id:ayousanz:20210915220154p:plain

またフォルダは一応(めっちゃあいまいですが)ドメインごとにわけました f:id:ayousanz:20210915220213p:plain

コンピュータサイエンスやその他最近の技術を学べる/活用できるサイト【Unity,Blender,Python ...】

コンピュータサイエンス全般

コンピュータサイエンスについての無料公開をまとめたリポジトリ

github.com

ゲームによく使われるアルゴリズムがまとまっているサイト

www.redblobgames.com

gRPCについて

github.com

Backendless(BaaS)とUnityを使ったオンラインショップの画面の作成【Unity,SaaS】

今回はゲームコンテストで作ろうと思っていたオンラインゲームのショップ画面をプロトタイプで作成してみました (たぶんコンテストにはこの要素はいれないです)

Demo

開発環境

  • Unity 2019.4.28f1
  • Backendless(BaaS)

使用したアセット

あまり関係ないですが,内部で以下も使用しています

backendlessとは?

(ほかのSaaSと同じなので,SaaSの説明を引用)

SaaS」(Software as a Service:「サース」または「サーズ」)とは、ソフトウェアを利用者(クライアント)側に導入するのではなく、提供者(サーバー)側で稼働しているソフトウェアを、インターネット等のネットワーク経由で、利用者がサービスとして利用する状況を指します。

引用元

SaaSとは | クラウド・データセンター用語集/IDCフロンティア

参考にした参考書

booth.pm

公式ドキュメント

backendless.com

実装

実装した機能としては以下です

  • Moqデータとしてコード側で一定するのデータをDBに追加する関数
  • 画面上からアイテムの購入(図1)
  • 購入したアイテムの削除

f:id:ayousanz:20210707221351p:plain
図1

各種非同期部分の書きかたと改善作成

非同期部分の書き方はtwitter で KOGAさんにアドバイスいただきました

詳細は以下のtweetのスレをご確認ください (今回はアドバイスをすべて反映できていません)

クラス図

f:id:ayousanz:20210707221430p:plain

スクリプト

DBHandler

using System;
using System.Collections.Generic;
using _OnlineShop.Scripts;
using UnityEngine;
using BackendlessAPI;
using BackendlessAPI.Persistence;
using Cysharp.Threading.Tasks;
using UniRx;
using UnityEditor;
using Random = UnityEngine.Random;

public class DBHandler : IDBHandler,IDisposable
{
    private readonly CompositeDisposable _compositeDisposable = new CompositeDisposable();

    public async void DeleteItemData(string tableName,Dictionary<string, object> itemInfo)
    {
        var savedContact = await Backendless.Persistence.Of(tableName).SaveAsync(itemInfo);
        await Backendless.Persistence.Of(tableName).RemoveAsync(savedContact);
    }

    //データの新規追加(同じ名前があっても新規に追加される)
    public async void AddItemData(string itemName,int itemCount,int itemPrice)
    {
        var dict = new Dictionary<string, object>
        {
            {"itemName",itemName},
            {"itemCount",itemCount},
            {"itemPrice",itemPrice}
        };
        var result = await Backendless.Data.Of("ShopList").SaveAsync(dict);
        Debug.Log($"result:{result}");
    }

    public async void UpdateItemData(string itemGroup,string itemName,int itemCount,int itemPrice)
    {
        var result = await Backendless.Data.Of(itemGroup).FindFirstAsync();
        result[itemName] = 30;
        await Backendless.Data.Of("Person").SaveAsync(result);
    }

    public async UniTask<IList<Dictionary<string, object>>> GetTableData(string tableName)
    {
        var query = DataQueryBuilder.Create();
        query.AddSortBy("created desc");
        query.SetRelationsDepth( 0 );
        return await Backendless.Data.Of(tableName).FindAsync(query).AsUniTask();
    }

    public void AddMoqData(string tableName)
    {
        for (var index = 1; index <= 6; index++)
        {
            AddItem(index);
        }
        async void AddItem(int itemNo)
        {
            var itemName = $"{tableName}" + itemNo;
            var dict = new Dictionary<string, object>
            {
                {"itemName",itemName},
                {"itemCount",Random.Range(0,30) * 5},
                {"itemPrice",Random.Range(2,100) * 10}
            };
            await Backendless.Data.Of(tableName).SaveAsync(dict);
        }
    }
    
    #if UNITY_EDITOR
    void HandleOnPlayModeChanged( PlayModeStateChange stateChange )
    {
        // This method is run whenever the playmode state is changed.
        if ( stateChange == PlayModeStateChange.ExitingEditMode || stateChange == PlayModeStateChange.ExitingPlayMode )
        {
            Backendless.RT.Disconnect();
        }
    }
    #endif
    public DBHandler()
    {
#if UNITY_EDITOR
        EditorApplication.playModeStateChanged += HandleOnPlayModeChanged;
#endif
    }

    public void Dispose()
    {
        _compositeDisposable?.Dispose();
    }
}

GameLifetimeScope

using _OnlineShop.Scripts;
using UnityEngine;
using VContainer;
using VContainer.Unity;

public class GameLifetimeScope : LifetimeScope
{
    [SerializeField] private ShopShopView shopShopView;

    protected override void Configure(IContainerBuilder builder)
    {
        builder.RegisterComponent(shopShopView).AsImplementedInterfaces();
        builder.Register<DBHandler>(Lifetime.Singleton).AsImplementedInterfaces();
        builder.RegisterEntryPoint<GameManager>();
    }
}

GameManager

using System;
using System.Linq;
using _OnlineShop.Scripts;
using UniRx;
using UnityEngine;
using VContainer.Unity;

public class GameManager : IStartable,IDisposable
{
    private readonly IDBHandler _dbHandler;
    private readonly IShopView _shopView;
    private readonly CompositeDisposable _compositeDisposable = new CompositeDisposable();
    private GameManager(IDBHandler dbHandler,IShopView shopView)
    {
        _dbHandler = dbHandler;
        _shopView = shopView;
    }

    public async void Start()
    {
        // _dbHandler.AddMoqData("Food");
        var d = await _dbHandler.GetTableData("Food");
        foreach (var (item,index) in d.Select((item,index) => (item,index)))
        {
            _shopView.SetText(item["itemName"].ToString(),item["itemCount"].ToString(),item["itemPrice"].ToString(),index);
        }

        _shopView.BuyItemObservable().Subscribe(value =>
        {
            _shopView.SetBuy(true,value);
            Debug.Log($"food:{value}を購入");
            _dbHandler.DeleteItemData("Food",d[value]);
        }).AddTo(_compositeDisposable);
    }

    public void Dispose()
    {
        _compositeDisposable?.Dispose();
    }
}

Item

using System;
using UniRx;
using UnityEngine;
using UnityEngine.UI;

namespace _OnlineShop.Scripts
{
    public class Item : MonoBehaviour
    {
        [SerializeField] private Text itemNameText;
        [SerializeField] private Text itemCountText;
        [SerializeField] private Text itemPriceText;
        [SerializeField] private Image itemIcon;
        [SerializeField] private Button buyButton;
        [SerializeField] private Image backgroundImage;

        public IObservable<Unit> OnClickBuyButton()
        {
            return buyButton.OnClickAsObservable();
        }

        public void SetText(string itemName, int itemCount, int itemPrice,int iconNo)
        {
            itemNameText.text = itemName;
            itemCountText.text = $"{itemCount}個";
            itemPriceText.text = $"${itemPrice}";
            itemIcon.sprite = GetIcon(iconNo);
        }

        private static Sprite GetIcon(int iconNo)
        {
            return Resources.Load<Sprite>($"Food/Icon{iconNo}");
        }

        public void SetBuy(bool isBuy)
        {
            backgroundImage.color = new Color(115, 115, 115);
            buyButton.interactable = false;
        }
    }
}

ShopShopView

using System;
using System.Collections.Generic;
using System.Linq;
using UniRx;
using UnityEngine;

namespace _OnlineShop.Scripts
{
    public class ShopShopView : MonoBehaviour,IShopView
    {
        [SerializeField] private List<Item> itemList = new List<Item>();

        private readonly Subject<int> _buySubject = new Subject<int>();
        
        private void Awake()
        {
            foreach (var (item,index) in itemList.Select((item,index) => (item,index)))
            {
                item.OnClickBuyButton().Subscribe(_ =>
                {
                    _buySubject.OnNext(index);
                }).AddTo(this);
            }
        }

        public void SetView(bool isView)
        {
            gameObject.SetActive(isView);
        }

        public void SetText(string itemName, string itemCount, string itemPrice,int itemNo)
        {
            if (itemNo < itemList.Count)
            {
                itemList[itemNo].SetText(itemName,int.Parse(itemCount), int.Parse(itemPrice),itemNo+1);
            }
        }

        public void SetBuy(bool isBuy,int itemNo)
        {
            itemList[itemNo].SetBuy(isBuy);
        }

        public IObservable<int> BuyItemObservable()
        {
            return _buySubject;
        }
    }
}

MessagePipeを使って簡単なアクションゲームを作る【MessagePipe,Unity】

最近注目を集めている MessagePipeを使ってみようと思いいろいろ調べて,自分なりにアクションゲームを作ってみました! (ゲームといってもMessagePipeを使うことが目的なので,ゲームにすらなっていないです)

成果物

Demo

キーボードのAを押すことで,左側のUnity chanに攻撃することができます
また,左のUnity chanからは定期的に(5秒後ごと)攻撃を受けます どちらかのHPが0になったら勝敗モーションに移行後,ゲーム終了します

リポジトリ

リポジトリはpublicにしていますので,詳細はリポジトリをご確認ください

github.com

WebGL

ayutaz.github.io

MessagePipeとは?

公式のGithubページより

MessagePipe is a high-performance in-memory/distributed messaging pipeline for .NET and Unity. It supports all cases of Pub/Sub usage, mediator pattern for CQRS, EventAggregator of Prism(V-VM decoupling), etc.

  • Dependency-injection first
  • Filter pipeline
  • better event
  • sync/async
  • keyed/keyless
  • buffered/bufferless
  • broadcast/response(+many)
  • in-memory/distributed

MessagePipe is faster than standard C# event and 78 times faster than Prism's EventAggregator.

簡単に言うと,型でイベントの発行等を管理するライブラリみたいです(理解が怪しい)

環境

© Unity Technologies Japan/UCL

(注) VContainerとMessagePipeを使うときは,こちらのMessagePipe.VContainerを入れないと動かない?気がします

f:id:ayousanz:20210601161227p:plain

MessagePipeでの値の受け渡し方

関係する部分のみ記載しています.実際に使用方法は, #MessagePipe部分のコード もしくはリポジトリをご確認ください

イベントPublish側

//使用するPublisherとDisposeを定義
[Inject] private IPublisher<PlayerData> PlayerData { get; set; }
private IDisposable _disposable;

//イベントの発行

PlayerData.Publish(new PlayerData(){AttackValue = value,IsLose = isLose});

イベントSubscribe側

[Inject] private ISubscriber<EnemyData> EnemyData { get; set; }
var d = DisposableBag.CreateBuilder();
            EnemyData.Subscribe(e =>
            {
                playerHp -= e.AttackValue;
                isPlay = e.IsLose;
                _animator.SetTrigger("IsDamage");
                playerHpText.text = $"enemy hp:{playerHp}";

                if (playerHp <= 0)
                {
                    _animator.SetBool("IsLose",true);
                    PublishData(0,true);
                }
            }).AddTo(d);

            _disposable = d.Build();

MessagePipe部分のコード

Player.cs

using System;
using MessagePipe;
using UnityEngine;
using UnityEngine.UI;
using VContainer;

namespace Model
{
    public class Player : MonoBehaviour
    {
        [SerializeField] private int playerHp;
        [SerializeField] private int attackValue;
        [SerializeField] private Text playerHpText;
        [Inject] private ISubscriber<EnemyData> EnemyData { get; set; }
        [Inject] private IPublisher<PlayerData> PlayerData { get; set; }
        private IDisposable _disposable;

        private Animator _animator;
        private bool isPlay;

        private void Awake()
        {
            playerHpText.text = $"player hp:{playerHp}";
            _animator = GetComponent<Animator>();
        }

        private void Start()
        {
            var d = DisposableBag.CreateBuilder();
            EnemyData.Subscribe(e =>
            {
                playerHp -= e.AttackValue;
                isPlay = e.IsLose;
                _animator.SetTrigger("IsDamage");
                playerHpText.text = $"enemy hp:{playerHp}";

                if (playerHp <= 0)
                {
                    _animator.SetBool("IsLose",true);
                    PublishData(0,true);
                }
            }).AddTo(d);

            _disposable = d.Build();
        }


        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.A) && !isPlay)
            {
                Debug.Log("プレイヤーが攻撃");
                _animator.SetTrigger("IsAttack");
                PublishData(3,false);
            }
        }

        private void PublishData(int value, bool isLose)
        {
            PlayerData.Publish(new PlayerData(){AttackValue = value,IsLose = isLose});
        }

        private void OnDestroy()
        {
            _disposable?.Dispose();
        }
    }
    
}

Enemy.cs

using System;
using MessagePipe;
using UniRx;
using UnityEngine;
using UnityEngine.UI;
using VContainer;
using VContainer.Unity;

namespace Model
{
    public class Enemy : MonoBehaviour,IStartable
    {
        [SerializeField] private int enemyHp;
        [SerializeField] private Text enemyHpText;
        private bool _isPlay;

        [Inject] private ISubscriber<PlayerData> PlayerData { get; set; }
        [Inject] private IPublisher<EnemyData> EnemyData { get; set; }

        private Animator _animator;
        private IDisposable _disposable;

        private void Awake()
        {
            enemyHpText.text = $"enemy hp:{enemyHp}";
            _animator = GetComponent<Animator>();
        }

        public void Start()
        {
            var d = DisposableBag.CreateBuilder();
            PlayerData.Subscribe(e =>
            {
                enemyHp -= e.AttackValue;
                _isPlay = e.IsLose;
                _animator.SetTrigger("IsDamage");
                enemyHpText.text = $"enemy hp:{enemyHp}";

                if (enemyHp <= 0)
                {
                    PublishData(0,true);
                    _animator.SetBool("IsLose",true);
                }
            }).AddTo(d);

            _disposable = d.Build();

            Observable.Interval(TimeSpan.FromSeconds(5f))
                .Where(_=>!_isPlay)
                .Subscribe(_ =>
                {
                    Debug.Log("敵が攻撃");
                    _animator.SetTrigger("IsAttack");
                    PublishData(3,false);
                }).AddTo(this);
        }
        
        private void PublishData(int value, bool isLose)
        {
            EnemyData.Publish(new EnemyData(){AttackValue = value,IsLose = isLose});
        }

        private void OnDestroy()
        {
            _disposable?.Dispose();
        }
    }
}

DI(VContainer)

using MessagePipe;
using Model;
using VContainer;
using VContainer.Unity;


public class GameLifetimeScope : LifetimeScope
{
    protected override void Configure(IContainerBuilder builder)
    {
        var options = builder.RegisterMessagePipe();
        builder.RegisterMessageBroker<PlayerData>(options);
        builder.RegisterMessageBroker<EnemyData>(options);

    }
}

受け渡しているデータクラス

namespace Model
{
    public class Data
    {
        public int AttackValue;
        public bool IsLose;
    }

    public class PlayerData : Data
    {
        
    }

    public class EnemyData : Data
    {
        
    }
}

参考にしたサイト

piffett.hateblo.jp

qiita.com

OSS(SaveGameFree)を使ってUnityでセーブ・ロード機能を実装する【Unity,SaveGameFree】

今回は,ゲーム制作で無料ものでセーブ・ロード機能を実装してほしいと要望があったので実装してみました

使用したライブラリ

github.com

有料版もありましたので,気になる方は確認してみてください

assetstore.unity.com

ライブラリの機能(GithubPageより)

The below features made Save Game Free excellent:

セーブ・ロード機能の実装

以下のスクリプトでは,以下の機能を実装しています

  • カスタムクラスのデータをデーブ
  • パスワード付きデータで保存
  • デーブデータの暗号化
using _Project.Scripts.Interface;
using BayatGames.SaveGameFree;

namespace _Project.Scripts.Model
{
    public class SaveHandler : ISaveData
    {
        public void Save(SaveDataClass saveDataClass)
        {
            SaveGame.Encode = true;
            SaveGame.Save("SaveData",saveDataClass,"pass");
        }

        public SaveDataClass Load()
        {
            return SaveGame.Load<SaveDataClass>("SaveData",true,"pass");
        }
    }
}

備考

セーブデータの保存場所

デフォルトでは,Application.persistentDataPath に保存されるみたいです

各種プラットフォームのパスの詳細

qiita.com

データの暗号化

データの暗号化

暗号化は SaveGame.Encode = true; で有無を変更できるみたいです

暗号化されているのかの確認

まず,データが保存されていくところまで行きます f:id:ayousanz:20210531152440p:plain

メモ帳で開いてみます f:id:ayousanz:20210531152524p:plain

とりあえずソフトとかを使わない限りは見えてないと思います

パスワード付きデータ

セーブ時

パスワードは,SaveGame.Save("SaveData",saveDataClass,"pass"); という感じで引数にいれれば指定できます

ロード時

セーブデータをロードするときは,SaveGame.Load<SaveDataClass>("SaveData",true,"pass"); という感じで自分で設定したパスワードを入れます

パスワードが間違っている場合は以下のようにロードできないみたいです.

パスワードが違うときに表示されるエラー文

CryptographicException: Padding is invalid and cannot be removed.

f:id:ayousanz:20210531153005p:plain

個人開発でGithub ActionsでUnityのCI/CDを使ってみた 【Unity,Github Actions(game-ci)】

TechTrainさんのほうでゲームの課題?をやらせていただいたときにGithub Actionsを使ったUnityのCI/CDを取り組みました(特に課題内容とかではないです)

Github Actionsを使ったUnityのCI/CDはいろいろ詰まるところがあったので,今後使う方/未来の自分に向けてまとめています

成果

  • UnityTestからのUnityビルドの実行(1枚目画像) : テストが成功しないとビルドが実行されない
  • テスト結果とBuildファイルをアーティストに保存
  • Unity TestのPlayModeTestの実行と結果の表示(2枚目画像)

f:id:ayousanz:20210526174305p:plain f:id:ayousanz:20210526174333p:plain

リポジトリは公開していますので,詳細はリポジトリをご確認ください また,テスト用にシンプルな実装でも同じことを行っています.こちらのリポジトリもご参考ください.

環境

CI/CDの実行の流れ

  1. テストを実行するマシーンの作成
  2. リポジトリをcheckout
  3. テストの実行
  4. テスト結果をアーティストをしてアップロード
  5. ビルドを実行するマシーンの作成
  6. リポジトリをcheckout
  7. cacheの取得(これがないと初回と同じ時間毎回かかるらしい)
  8. ビルドの実行
  9. ビルドファイルをアーティストにアップロード

Unityのシリアルキーを取得する

詳しい説明はこちらの記事を参考にさせていただきました

注意ポイント 公式のライセンスを取得できるサイトからダウンロードできるファイルの中身は全部そのまま Github secretのvalueに入れましょう!

license.unity3d.com

TestをActions上で実行する

Unityのテストを作成する

テストフレームのライブラリを導入

今回はUnityでUIテストを簡単に作成するために,Unity UI Test Automation Frameworkを導入しました 詳細は,こちらで詳しく説明されています

baba-s.hatenablog.com

unityUITestライブラリから,DependencyInjector.csUITest.cs を導入します

f:id:ayousanz:20210527172314p:plain

テストの作成

テストファイルと asmdefファイルを作成します

f:id:ayousanz:20210527174213p:plain

実行されるようにymlファイルを設定する

test:
    name: Run EditMode and PlayMode Test
    runs-on: ubuntu-latest
    steps:
      - name: Check out my unity project.
        uses: actions/checkout@v2
      - name: Run EditMode and PlayMode Test
        uses: game-ci/unity-test-runner@v2
        with:
          projectPath: .
          githubToken: ${{ secrets.GITHUB_TOKEN }}
          unityVersion: 2020.3.9f1
      # テストの実行結果をアーティファクトにアップロードして後から参照可能にする
      - uses: actions/upload-artifact@v2
        if: always()
        with:
          name: Test results
          path: artifacts

テストの結果を保存/簡単に確認できるようにする

テスト結果の表示

githubToken: ${{ secrets.GITHUB_TOKEN }} を設定すると,以下のようにテストの結果が簡単に見れます. 詳細はこちらに記載されています f:id:ayousanz:20210527175815p:plain

テスト結果の保存

- uses: actions/upload-artifact@v2
        if: always()
        with:
          name: Test results
          path: artifacts

を入れることで,テスト結果をアーティストとして保存することができます f:id:ayousanz:20210527175609p:plain

BuildをActions上で実行する

ビルドのプラットフォームは,以下のものが設定できるみたいです(公式サイトより)

targetPlatform:
          - StandaloneOSX # Build a macOS standalone (Intel 64-bit).
          - StandaloneWindows # Build a Windows standalone.
          - StandaloneWindows64 # Build a Windows 64-bit standalone.
          - StandaloneLinux64 # Build a Linux 64-bit standalone.
          - iOS # Build an iOS player.
          - Android # Build an Android .apk standalone app.
          - WebGL # WebGL.

ビルドの設定は以下になります.

build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        projectPath:
          - .
        unityVersion:
          - 2020.3.9f1
        targetPlatform:
         - Android # Build an Android player.
    needs: test
    steps:
    - name: Checkout
      uses: actions/checkout@v2
      with:
        lfs: false
        clean: false
        
    # Cache
    - uses: actions/cache@v2
      with:
        path: Library
        key: Library

    # Build
    - name: Build project
      uses: game-ci/unity-builder@v2.0-alpha-6
      with:
        unityVersion: ${{ matrix.unityVersion }}
        targetPlatform: ${{ matrix.targetPlatform }}

結論

game-ciのライブラリを使うことですごく楽にUnityのCI/CDを構築できることができました~ 他にもいろいろ機能があるみたいなので,使っていきたいです

ymlファイル全体の構成

# This is a basic workflow to help you get started with Actions

name: Test and Build,Release APK

env:
  UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}

# Controls when the action will run. 
on: [push]
  # Triggers the workflow on push or pull request events but only for the main branch

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:  
  test:
    name: Run EditMode and PlayMode Test
    runs-on: ubuntu-latest
    steps:
      - name: Check out my unity project.
        uses: actions/checkout@v2
      - name: Run EditMode and PlayMode Test
        uses: game-ci/unity-test-runner@v2
        with:
          projectPath: .
          githubToken: ${{ secrets.GITHUB_TOKEN }}
          unityVersion: 2020.3.9f1
      # テストの実行結果をアーティファクトにアップロードして後から参照可能にする
      - uses: actions/upload-artifact@v2
        if: always()
        with:
          name: Test results
          path: artifacts
  build:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        projectPath:
          - .
        unityVersion:
          - 2020.3.9f1
        targetPlatform:
         - Android # Build an Android player.
    needs: test
    steps:
    - name: Checkout
      uses: actions/checkout@v2
      with:
        lfs: false
        clean: false
        
    # Cache
    - uses: actions/cache@v2
      with:
        path: Library
        key: Library

    # Build
    - name: Build project
      uses: game-ci/unity-builder@v2.0-alpha-6
      with:
        unityVersion: ${{ matrix.unityVersion }}
        targetPlatform: ${{ matrix.targetPlatform }}

    # Output
    - uses: actions/upload-artifact@v2
      with:
        name: Build-${{ matrix.targetPlatform }}
        path: build/${{ matrix.targetPlatform }}

Unity・ゲームの開発でのおすすめ/よく使用するアセット・参考サイト(その他あり)【Unity,UnityAssets,素材リンク】

自分用またほかの学習者の方が何を使っていいのかがわからなくなったときにご活用ください

アセットストア経由

(OpenUPMでも入れれるものはあります)

1. DoozyUI: Complete UI Management System

assetstore.unity.com

使い方などが紹介されているサイト

www.slideshare.net

bibinbaleo.hatenablog.com

2. Odin - Inspector and Serializer

assetstore.unity.com

使い方などが紹介されているサイト

kan-kikuchi.hatenablog.com

www.midnightunity.net

3. UniRx - Reactive Extensions for Unity

公式より

Provides an efficient allocation free async/await integration for Unity.

assetstore.unity.com

使い方などが紹介されているサイト

qiita.com

こちらはUniTaskも合わせてこちらの本がおすすめです

3. DOTween Pro

assetstore.unity.com

無料版もあります(正直無料版で十分だと思います)

assetstore.unity.com

使い方などが紹介されているサイト

qiita.com

amagamina.jp

Easy Save - The Complete Save & Load Tool for Unity

assetstore.unity.com

使い方などが紹介されているサイト

ayousanz.hatenadiary.jp

kan-kikuchi.hatenablog.com

Open UPM経由

unity-reference-viewer

📦 unity-reference-viewer - jp.amagamina.reference-viewer | OpenUPM

UniTask

github.com

VContainer

公式より

The extra fast DI (Dependency Injection) library running on Unity Game Engine.

Zenjectよりも速度が速く,機能も欲しいところだけになっています

github.com

使い方などが紹介されているサイト light11.hatenadiary.com

qiita.com

フリー素材(Unity使用OK)

ドット素材

damagedgold.wp.xdomain.jp

フリー3Dモデル

3dyasan.com ]

www.kenney.nl

お寿司3Dモデル(商用利用OK)

ddd.pink

quaternius.com

パワポとか会議資料風

kage-design.com

アイソメトリックの画像・マップの作成サイト

その他

www.shigureni.com

soco-st.com

woobro.design

www.openpeeps.com

mixkit.co

pixelbuddha.net

www.manypixels.co

www.manypixels.co

フォント

dotcolon.net

Sound

splice.com

UI/UXの参考サイト

www.gameuidatabase.com

interfaceingame.com

www.designnotes.co

Unity uGUI アドバンスド・リファレンス

github.com

参考記事・サイト

無料で使えるツールをまとめているサイト

freestuff.dev