ゲーム化!tomo_manaのブログ

ゲーム化!tomo-manaのブログ

Unityでゲームを作る方法について紹介しています

Unity×会計#2 貸借対照表の入力フィールドを追加する (Unity 2019.4.4f1)

f:id:tomo_mana:20211220005449p:plain

今回は、前回作った貸借対照表に入力フィールドを追加して、金額を変更できるようにします。

前回の内容

前回は、貸借対照表をRectTransformで作成しました。

(作成された貸借対照表

f:id:tomo_mana:20210805220153p:plain
前回作成した貸借対照表(簡易表示)

貸借対照表の各項の合計だけを抽出した「箱型バランスシート」が分かりやすそうなので、参考にさせて頂いています。「現金」「流動資産」「流動負債」「固定資産」「固定負債」「純資産」の6項目で構成します。
bookmeter.com

入力フィールドの作成

純資産を除く貸借対照表の各項目を変更できる入力フィールドを作ります。Text(説明文)、InputField(入力値)、Button(設定ボタン)で構成します。

f:id:tomo_mana:20210809091140p:plain
入力フィールド

オブジェクトの追加

Text、InputField、Buttonを追加します。文字列はすべてTextMeshPro を使用します。さらにEmpty Objectでくくります(ここではInputLineという名前にします)。

f:id:tomo_mana:20210809204501p:plain
入力フィールド

Empty Object(InputLine)に HorizontalLayoutGroupを設定します。

f:id:tomo_mana:20210808204221p:plain
InputLine - HorizontalLayoutGroupの設定

各テキストのフォント・サイズを調整後、Prefabにします。

f:id:tomo_mana:20210809205630p:plain
フォントサイズの指定

Prefabにした入力フィールドを5つ複製、Empty Objectでくくります(ここではInputWindowという名前にします)。

f:id:tomo_mana:20210809205850p:plain
InputWindow

Empty Object(InputWindow)には、VerticalLayoutGroupを設定します。

f:id:tomo_mana:20210808213551p:plain
Input - VerticalLayoutGroupの設定

コードと関連付け

入力フィールドのコードの作り方はいくつか選択肢がありますが、今回は手抜きして、全部のPrefabを一つのコードで制御します。

貸借対照表との関連付け

入力フィールドは貸借対照表のテスト的な存在なので、入力フィールドが一方的に貸借対照表を参照するように作ります。(入力フィールドを切り離しても貸借対照表を使えるようにする)

(図)

f:id:tomo_mana:20210809092236p:plain
入力フィールド(InputWindow)から貸借対照表(BalanceSheet)を参照する

入力フィールド (InputWindow)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class InputWindow : MonoBehaviour
{
    [SerializeField]
    BalanceSheet bs;
    public struct InputLine {
        public TMP_InputField field;
    }
    InputLine[] inputLine = new InputLine[5];
    
    int GetBsAreaVal(int id)
    {
        if( (id < 0) || (5 < id) ){
            return 0;
        }
        if( ReferenceEquals(bs, null) ){
            return 0;
        }
        if( id >= 3 ){
            id++;
        }
        return bs.BsAreaDefVal[id];
    }
    
    void ButtonClicked(int buttonNo)
    {
        if( ReferenceEquals(bs, null) ){
            return;
        }
        int id = buttonNo;
        if( id >= 3 ){
            id++;
        }
        int v;
        if( int.TryParse( inputLine[buttonNo].field.text, out v ) ){
            bs.ChangeAreaValue( (BS_AREA)id, int.Parse(inputLine[buttonNo].field.text) );
        }
    }
    
    // Start is called before the first frame update
    void Start()
    {
        int i = 0;
        Button button;
        // InputFieldゲームオブジェクトを取得
        foreach( Transform child in gameObject.transform ){
            inputLine[i].field = child.Find("InputField (TMP)").GetComponent<TMP_InputField>();
            // デフォルト値をフィールド初期値に設定
            inputLine[i].field.text = GetBsAreaVal(i).ToString();
            button = child.Find("Button").GetComponent<Button>();
            int ii = i;
            button.onClick.AddListener(() => ButtonClicked(ii));
            i++;
        }
    }
}

貸借対照表(BalanceSheet)

前回のコードの一部を再描画処理(RedrawBalanceSheet)として抜き出して、入力フィールドから値が設定された(ChangeAreaValue)ときに再描画するようにします。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;

// 貸借対照表の主要な項目
public enum BS_AREA {
    BS_CASH = 0,// 現金
    BS_FLA,     // 流動資産
    BS_FIA,     // 固定資産
    BS_NAN,     // 純資産(負)
    BS_FLL,     // 流動負債
    BS_FIL,     // 固定負債
    BS_NAP,     // 純資産
    BS_MAX
};

public class BalanceSheet : MonoBehaviour
{
    public struct BsAreaId {
        public int bsArea;
        public string goName;
        
        public BsAreaId(int id, string name)
        {
            this.bsArea = id;
            this.goName = name;
        }
    }
    
    private BsAreaId[] bsAreaId = {
        new BsAreaId( (int)(BS_AREA.BS_CASH), "Cash" ),
        new BsAreaId( (int)(BS_AREA.BS_FLA), "FlexibleAssets" ),
        new BsAreaId( (int)(BS_AREA.BS_FIA), "FixedAssets" ),
        new BsAreaId( (int)(BS_AREA.BS_NAN), "NetAssetsL" ),
        new BsAreaId( (int)(BS_AREA.BS_FLL), "FlexibleLiabilities" ),
        new BsAreaId( (int)(BS_AREA.BS_FIL), "FixedLiabilities" ),
        new BsAreaId( (int)(BS_AREA.BS_NAP), "NetAssetsR" ),
    };
    
    //ポインタ、名前、金額
    public struct BsArea {
        public GameObject go;
        public TextMeshProUGUI textArea;
        public string areaName;
        public int value;
        public float rate;
    }
    
    int[] BsAreaDefVal = {
        10000,      // 現金
        25000,      // 流動資産
        20000,      // 固定資産
        0,          // 純資産(負) 自動計算
        8000,       // 流動負債
        25000,      // 固定負債
        0,          // 純資産 自動計算
    };
    
    GameObject[] children;
    Component[] components;
    TextMeshProUGUI tmpobj;
    BsArea[] bsArea = new BsArea[7];
    
    TextMeshProUGUI GetTextArea( GameObject obj )
    {
        if( obj == null ){
            return null;
        }
        foreach( Transform child in obj.transform ){
            tmpobj = child.gameObject.GetComponent<TextMeshProUGUI>();
            if( tmpobj != null ){
                return tmpobj;
            }
        }
        return null;
    }
    
    public void ChangeAreaValue( BS_AREA id, int val )
    {
        if( bsArea[(int)id].value != val){
            bsArea[(int)id].value = val;
            RedrawBalanceSheet();
        }
    }
    
    private void RedrawBalanceSheet()
    {
        int i;
        
        // 資産と負債の合計を計算
        int[] sum = new int[3];
        sum[0] = bsArea[(int)(BS_AREA.BS_CASH)].value + bsArea[(int)(BS_AREA.BS_FLA)].value + bsArea[(int)(BS_AREA.BS_FIA)].value;  // 資産
        sum[1] = bsArea[(int)(BS_AREA.BS_FLL)].value + bsArea[(int)(BS_AREA.BS_FIL)].value;                                         // 負債
        
        // 純資産の向きと大きさを計算
        if( sum[0] < sum[1] ){
            sum[2] = sum[1];
        } else {
            sum[2] = sum[0];
        }
        bsArea[(int)(BS_AREA.BS_NAP)].value = sum[2] - sum[1];
        bsArea[(int)(BS_AREA.BS_NAN)].value = sum[2] - sum[0];
        
        // 領域ごとの金額(表示)を更新
        for( i = 0; i < (int)(BS_AREA.BS_MAX); i++ )
        {
            bsArea[i].textArea.text = bsArea[i].areaName + "\n" + bsArea[i].value;
        }
        
        // 貸借対照表の大きさ(今回は固定)
        int height = 300;
        
        // 箱の比率を計算
        if( sum[2] != 0 ){
            for( i = 0; i < (int)(BS_AREA.BS_FLL); i++ )
            {
                bsArea[i].rate = (float)bsArea[i].value / sum[2];
            }
            for( i = (int)(BS_AREA.BS_FLL); i < (int)(BS_AREA.BS_MAX); i++ )
            {
                bsArea[i].rate = (float)bsArea[i].value / sum[2];
            }
        } else {
            for( i = 0; i < (int)(BS_AREA.BS_MAX); i++ ){
                bsArea[i].rate = 0;
            }
        }
        
        // 箱の大きさを比率に応じて更新
        RectTransform rt;
        Vector2 v;
        for( i = 0; i < (int)(BS_AREA.BS_MAX); i++ )
        {
            if( bsArea[i].rate == 0 ){
                bsArea[i].go.SetActive(false);
            } else {
                bsArea[i].go.SetActive(true);
                rt = (RectTransform)bsArea[i].go.transform;
                v = rt.sizeDelta;
                v.y = bsArea[i].rate * height;
                rt.sizeDelta = v;
            }
        }
    }
    
    // Start is called before the first frame update
    void Start()
    {
        // 領域初期化
        int i;
        for( i = 0; i < (int)(BS_AREA.BS_MAX); i++ )
        {
            bsArea[i].go = GameObject.Find( bsAreaId[i].goName );
            bsArea[i].textArea = GetTextArea( bsArea[i].go );   //TextMeshProから取得
            bsArea[i].areaName = bsArea[i].textArea.text;   //TextMeshProから取得
            bsArea[i].value = BsAreaDefVal[i];                  //初期値リストから取得
        }
        RedrawBalanceSheet();
    }
}

動作テスト

ゲームのURL(前回までと同じURLです)
tomo-mana.hatenablog.com

次回

現金取引の仕分けを追加します。

参考

TextMeshPro の InputField

TMP_InputFieldで参照します(TextMeshProUGUIと異なるクラス)。
inputFieldのTMP版をスクリプトで使う方法。 - Qiita

null比較

== null でなく、ReferenceEquals(object, object)を使用しました。
GameObjectやComponentをnullと比較するときの落し穴 - Qiita