今回はゲームコンテストで作ろうと思っていたオンラインゲームのショップ画面をプロトタイプで作成してみました
(たぶんコンテストにはこの要素はいれないです)
Demo
開発環境
Unity 2019.4.28f1
Backendless(BaaS)
使用したアセット
あまり関係ないですが,内部で以下も使用しています
(ほかのSaaS と同じなので,SaaS の説明を引用)
「SaaS 」(Software as a Service:「サース」または「サーズ」)とは、ソフトウェアを利用者(クライアント)側に導入するのではなく、提供者(サーバー)側で稼働しているソフトウェアを、インターネット等のネットワーク経由で、利用者がサービスとして利用する状況を指します。
引用元
SaaSとは | クラウド・データセンター用語集/IDCフロンティア
参考にした参考書
booth.pm
公式ドキュメント
backendless.com
実装
実装した機能としては以下です
Moqデータとしてコード側で一定するのデータをDBに追加する関数
画面上からアイテムの購入(図1)
購入したアイテムの削除
図1
各種非同期部分の書きかたと改善作成
非同期部分の書き方はtwitter で KOGAさんにアドバイス いただきました
詳細は以下のtweet のスレをご確認ください
(今回はアドバイス をすべて反映できていません)
クラス図
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 )
{
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;
}
}
}