GMOあおぞらネット銀行のsunabarというサンドボックス環境を使って、初音ミクの銀行アプリのプロトタイプを作ってみました。
なお、sunabarを利用するためにはGMOあおぞらネット銀行の口座が必要です。(口座開設は面倒な書類のやりとりは無く、ネットだけで完結します。
#GMOあおぞらネット銀行 さんには #sunabar というAPIサンドボックス環境があるので、試しに初音ミクの銀行アプリのプロトタイプを作ってみた。
— たつや (@tatsuya1970) October 3, 2021
こんな銀行アプリあったら売れるかしら? pic.twitter.com/JIl9tGDx4d
##動作環境
OS: Windows10
使用ソフト:Unity
・Text Mesh Pro をインストール
※Window > Package Manager
・JSON Object アセットをインストール
https://assetstore.unity.com/packages/tools/input-management/json-object-710?locale=ja-JP
必要アカウント
・sunabar(あおぞら銀行のAPI環境)
##sunabar について
API開発者ポータルでアカウント作成
https://api.gmo-aozora.com/ganb/developer/api-docs/#/STOP0101
GMOあおぞら銀行の口座開設
https://gmo-aozora.com/
銀行口座を作成すると、
サンドボックス環境であるsunabarポータルへのログインが可能になります。
ログインID,パスワードは、リアル口座画面から確認できます。
「お客さま情報(申込・設定)」タブ > 「開発者向け」
※パスワードは定期的に変更されています。
sunabarポータルへのログイン
https://portal.sunabar.gmo-aozora.com/
自分のサンドボックス口座は、
左側をクリック
ログインすると
本物のGMOあおぞら銀行のインターネットバンキングと同じUIです。
開発中に本物かsunabarか混乱したことあるほど。
なお、この画像では残高1万円ですが、デフォルトでは残高はゼロです。
では、どうやって入金するかというと、先ほどの画面の右側から入金できます。
仮想的なセブン銀行のATMという設定です。
Unity
###Live2DのキャラをUnity へ
まずは、Unityで "2D" のプロジェクトを新規作成し、
2次元のイラストを自由に動かすことができるLive2DのSDKである「Cubism SDK for Unity」をプロジェクトにインポートします。
https://www.live2d.com/download/cubism-sdk/download-unity/
こんなエラーでたけど
ArgumentNullException: Value cannot be null.
Parameter name: shader
ほっといても、よさげ
Live2Dのウェブサイトのサンプルデータを見に行きます。 https://www.live2d.com/download/sample-data/
今回は初音ミクを選択
ダウンロードした初音ミクを
Unity の Project のどっかにフォルダをそのままドラッグします。
Prefab が生成されるので、Sceneに適当においてみます。
###アニメーション設定
笑顔、怒り、悲しみという3感情を設定。
通常の返答は「笑顔」、残高少ないのに出金しようとしたら「怒り」、間違った操作をしたら「悲しみ」というふうに設定。
Idle -> Smile
Has Exit Time のチェックをはずす
Conditions
Smile -> Idle
Has Exit Time のチェックをする。
Conditions は何も入れない
###UI設定
この画像のように残高照会ボタン、振込ボタン、メッセージ欄をつくる
残高照会ボタン、振込ボタン
クリックしたらスクリプトanswer.cs を呼び出すために
onClick の設定とスクリプトanswer.csをアタッチする
スクリプトanswer.csはこちら
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System;
using TMPro; //UIテキストにハイパーリンク表示
public class answer : MonoBehaviour
{
private Animator animator;
public GameObject TransferInputObject;
public Button button;
public GameObject BankCodeInputField;
public GameObject BranchCodeInputField;
public GameObject AccountNumberInputField;
public GameObject NameInputField;
public GameObject TransferAmountInputField;
[SerializeField]
private TextMeshProUGUI LinkText;
[Serializable]
public class body
{
public string accountId;
public string transferDesignatedDate;
public Transfers[] transfers;
}
[Serializable]
public class Transfers
{
public string transferAmount;
public string beneficiaryBankCode;
public string beneficiaryBranchCode;
public string accountTypeCode;
public string accountNumber;
public string beneficiaryName;
}
public void OnClick()
{
animator = GameObject.Find("miku").GetComponent<Animator>();
string order = this.gameObject.name;
//残高照会ボタンが押された場合
if (order == "BalanceButton")
{
string url = "https://api.sunabar.gmo-aozora.com/personal/v1/accounts/balances";
StartCoroutine(get(url, order));
}
//振込ボタンが押された場合
else if (order == "TransferButton")
{
TransferInputObject.SetActive(true);
}
//振込OKボタンが押された場合
else if (order == "TransferOkButton") {
string transferAmount = TransferAmountInputField.GetComponent<Text>().text;
string bankCode = BankCodeInputField.GetComponent<Text>().text;
string branchCode = BranchCodeInputField.GetComponent<Text>().text;
string accountNumber = AccountNumberInputField.GetComponent<Text>().text;
string name = NameInputField.GetComponent<Text>().text;
var url = "https://api.sunabar.gmo-aozora.com/personal/v1/transfer/request";
string date = DateTime.Now.ToString("yyyy-MM-dd");
string itemJson = "{ \"accountId\":\"301010003162\", \"transferDesignatedDate\":\"" + date + "\", " +
"\"transfers\":[{ \"transferAmount\" :\""+ transferAmount + "\", \"beneficiaryBankCode\": \""+ bankCode + "\" , \"beneficiaryBranchCode\": \"" + branchCode + "\", " +
"\"accountTypeCode\": \"1\", \"accountNumber\": \""+ accountNumber + "\", \"beneficiaryName\": \"" + name + "\" }] }";
body item = JsonUtility.FromJson<body>(itemJson);
string serialisedItemJson = JsonUtility.ToJson(item);
Debug.Log("serialisedItemJson " + serialisedItemJson);
StartCoroutine(post(url, serialisedItemJson));
}
}
private IEnumerator get(string url, string order)
{
UnityWebRequest request = UnityWebRequest.Get(url);
request.SetRequestHeader("x-access-token", "GMOあおぞらネット銀行のトークン");
yield return request.SendWebRequest();
// 通信エラーチェック
if (request.isNetworkError)
{
Debug.Log(request.error);
}
else
{
Debug.Log(request.responseCode);
if (request.responseCode == 200)
{
// UTF8文字列として取得する
string text = request.downloadHandler.text;
Debug.Log(text);
JSONObject json = new JSONObject(text);
if (order == "BalanceButton")
{
JSONObject balances = json.GetField("balances");
string balance = balances[0].GetField("balance").str;
int balanceInt = int.Parse(balance);
if (balanceInt < 10000)
{
animator.SetBool("angry", true);
string mes = "今の残高は" + balanceInt.ToString("N0") + "円しかないよー\n無駄使いしたらダメだよ";
GameObject MesObj = GameObject.Find("MessageText");
MesObj.GetComponent<Text>().text = mes;
//アニメーションが終わる時間にもとにもどす
yield return new WaitForSeconds(0.8f);
animator.SetBool("angry", false);
}
else
{
animator.SetBool("smile", true);
string mes = "今の残高は" + balanceInt.ToString("N0") + "円だよー";
GameObject MesObj = GameObject.Find("MessageText");
MesObj.GetComponent<Text>().text = mes;
//アニメーションが終わる時間にもとにもどす
yield return new WaitForSeconds(0.8f);
animator.SetBool("smile", false);
}
}
}
}
}
private IEnumerator post(string url,string body)
{
var request = new UnityWebRequest(url, "POST");
byte[] postData = System.Text.Encoding.UTF8.GetBytes(body);
request.uploadHandler = new UploadHandlerRaw(postData);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("x-access-token", "GMOあおぞらネット銀行のトークン");
yield return request.SendWebRequest();
// 通信エラーチェック
if (request.isNetworkError)
{
Debug.Log(request.error);
animator.SetBool("sad", true);
yield return new WaitForSeconds(0.8f);
animator.SetBool("sad", false);
}
else
{
Debug.Log(request.responseCode);
if (request.responseCode == 200 || request.responseCode == 201)
{
TransferInputObject.SetActive(false);
animator.SetBool("smile", true);
GameObject MesObj = GameObject.Find("MessageText");
MesObj.GetComponent<Text>().text = "受け付けました。\nあとはこのリンク先から振込承認してね";
GameObject LinkObj = GameObject.Find("LinkText_TMP");
string LinkURL = "https://bank.sunabar.gmo-aozora.com/bank/notices/important";
string mes = "<link=\"" + LinkURL + "\">" + LinkURL + "</link>";
LinkText.text = mes;
yield return new WaitForSeconds(3.0f);
animator.SetBool("smile", false);
}
else
{
animator.SetBool("angry", true);
GameObject MesObj = GameObject.Find("MessageText");
MesObj.GetComponent<Text>().text = request.responseCode+ "エラーだよ!!\nどこかが間違ってるよ。";
yield return new WaitForSeconds(0.8f);
animator.SetBool("angry", false);
yield return new WaitForSeconds(3.0f);
MesObj.GetComponent<Text>().text = "";
}
}
}
}
振込ボタンが押されたら表示する振込情報のUIを作っておく
振込を受付後に 振込承認の画面に誘導するため、ハイパーリンクを表示する必要がある。
冒頭の準備でTextMesh Pro をインストールしたのはこのため
このサイトのスクリプトを拝借した
https://baba-s.hatenablog.com/entry/2019/09/03/075000
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
[RequireComponent(typeof(TextMeshProUGUI))]
public class HyperlinkUI : MonoBehaviour, IPointerClickHandler
{
public void OnPointerClick(PointerEventData e)
{
var text = GetComponent<TextMeshProUGUI>();
var pos = Input.mousePosition;
var canvas = text.canvas;
var camera = canvas.renderMode == RenderMode.ScreenSpaceOverlay ? null : canvas.worldCamera;
var index = TMP_TextUtilities.FindIntersectingLink(text, pos, camera);
if (index == -1) return;
var linkInfo = text.textInfo.linkInfo[index];
var url = linkInfo.GetLinkID();
Application.OpenURL(url);
}
}
以上、自分好みの銀行アプリを作ることは技術的には可能です。
##参考サイト
https://note.com/ume_white/n/nd3c4498d7a5e