1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unityで電卓作成し、他の方法と比較してみた

Last updated at Posted at 2025-02-08

勉強のためにWindows Form Applicationで,以下のように電卓を作成したとき。

calculatorWFA.jpg

このとき、その手順がそのままUnityに使えるのではないか?と思いたちUnityでも電卓を作成してみた。
 確かに、UnityもWindows Form Applicationも、画面のButtonやLabel(TextField)を部品のように作成し、さらにC#でそのButtonをクリックしたときの挙動をひもづける(計算などして、Labelに表示する)という点で似通っていた。
 ただ、いずれの二つも、以前Python(モジュールがtkinker、PysimpleGUI, pygameのいずれも)やJavascriptでつくったとき
https://qiita.com/tkoji3744/items/08b71054dfba4f93f2c4
にくらべて、スクリプトの行数がとても長かった。これは、C#には、pythonやJavaScriptで使った関数eval()が用意されていないということが原因と思われた。
 すなわちPython の eval() 関数を使用すると、C# のように switch 文を使わずに 直接数式を評価できる ため、コードの簡潔さに大きく影響する。

 eval() の動作
print(eval("5 + 3"))   # 8
print(eval("10 * 2"))  # 20
print(eval("50 / 5"))  # 10.0

このように、eval() を使うと 文字列の計算式をそのまま評価して、結果を取得できる ため、C# のように switch 文や if を使わずに済む。

C#(Windows Forms)の計算処理
C# では eval() に相当する組み込み関数がなく、数式の処理を switch 文や if を使って実装する必要がある。

switch (operation)
{
    case "+":
        result = firstNumber + secondNumber;
        break;
    case "-":
        result = firstNumber - secondNumber;
        break;
    case "×":
        result = firstNumber * secondNumber;
        break;
    case "÷":
        if (secondNumber != 0)
            result = firstNumber / secondNumber;
        else
            lblResult.Text = "Error";
        return;
}

C# では 演算子ごとに処理を分岐 させなければならないため、どうしてもコードが長くなる。

(参考) Python(Tkinter)の実装例
Python (Tkinter) を使った場合、eval() を活用することで 極めて短いコードで電卓を作成 できる。

import tkinter as tk

def on_click(event):
    text = event.widget.cget("text")
    if text == "=":
        try:
            result.set(eval(result.get()))
        except:
            result.set("Error")
    elif text == "C":
        result.set("")
    else:
        result.set(result.get() + text)

root = tk.Tk()
root.geometry("300x400")

result = tk.StringVar()
entry = tk.Entry(root, textvar=result, font="Arial 20")
entry.pack(fill="both")

buttons = [
    "7", "8", "9", "+",
    "4", "5", "6", "-",
    "1", "2", "3", "*",
    "C", "0", "=", "/"
]

for i in range(0, len(buttons), 4):
    frame = tk.Frame(root)
    frame.pack()
    for text in buttons[i:i+4]:
        btn = tk.Button(frame, text=text, font="Arial 20", height=2, width=5)
        btn.pack(side="left")
        btn.bind("<Button-1>", on_click)

root.mainloop()

1.jpg

eval() の強力さ
result.set(eval(result.get()))
この1行で 全ての計算を実行 できるため、C# の switch 文のような処理が不要になる。

「これだけ」のpythonに比べて、これから以下に書いていくUnityでの電卓つくりは、むなしくなるほど圧倒的に大変である、といえる。

さて、Unity作成した電卓は図のようである。
calculatorUnity.jpg

その手順を要約する。

  1. プロジェクトの準備
    (1) Unityのインストール
    Unity Hubを使用して最新のLTSバージョンのUnityをインストール。
    (2) 新しいプロジェクトの作成
    Unity Hubを開き、「新しいプロジェクト」。テンプレート: 2D を選択。
    プロジェクト名: 「Calculator」。保存場所を選び、「作成」。

  2. UIの作成
     各UIの役割
    Button 19個:計算機のボタン(0〜9、演算子4つ:+-*/、クリア、イコール、正負逆転、%、小数点)
    Input Field 1個目 ユーザーの入力を表示
    Input Field 2個目 計算結果を表示

(1) Canvasの作成
Hierarchyウィンドウで Right Click → UI → Canvas を作成。
Canvasの Render Mode を Screen Space - Overlay に設定

(2) 入力表示用のテキストボックス
Input Field (TMP)(2個) を配置
Hierarchyで Right Click → UI → Panel
(その子として)そのPnelの上で、Right Click → UI → Input Field (TMP) を作成(TextMeshProのバージョン推奨)Rect Transform を調整して画面上部に配置。Text を 0 等にして、Font Size を適切に調整。
複製して、2つ作成。

(3) ボタンの作成

まず画面上に、Button(19個)を配置

HierarchyでRight Click → UI → Button を作成し、名前を Button_1 に変更。
Text コンポーネントを 1 等に変更。Rect Transform でサイズと位置を調整。
作成した Button を Ctrl+D(Duplicate)で複製し、19個用意
他の数字 (0~9), 記号 (+, -, *, /, =, C) も同様に作成していく。

3.スクリプトの作成

Assets フォルダ内で Right Click → Create → C# Script
スクリプト名を CalculatorScript に変更し、以下のコードを記述。

ボタンに共通のスクリプトを適用すると効率がよい。
共通スクリプト(Calculator.cs)を作成した後、
OnClick() のイベントに同じ OnButtonClick() メソッドを登録
それぞれのボタンで、引数(ボタンの値) を渡す、等、このあとでおこなっていく予定

(1)スクリプトを1つ作成

using UnityEngine;
using UnityEngine.UI;
using TMPro;

public class Calculator : MonoBehaviour
{
    public TMP_InputField inputField;
    public TMP_InputField resultField;

    private string currentInput = ""; // 数式を保持
    private bool isResultDisplayed = false;

    // ボタンを押したときの処理
    public void OnButtonClick(string value)
    {
        Debug.Log($"Button Pressed: {value}");

        // 直前に計算結果が表示されていた場合
        if (isResultDisplayed)
        {
            // 数字が押された場合は currentInput をリセット
            if (char.IsDigit(value[0]) || value == ".")
            {
                currentInput = value;
            }
            // 演算子が押された場合は、計算結果を継続
            else
            {
                currentInput += value;
            }
            isResultDisplayed = false;
        }
        else
        {
            currentInput += value;
        }

        inputField.text = currentInput;
        Debug.Log($"Updated Input: {currentInput}");
    }

    // クリアボタンの処理
    public void OnClearClick()
    {
        Debug.Log("Clear Pressed");
        currentInput = "";
        inputField.text = "";
        resultField.text = "";
        isResultDisplayed = false;
    }

    // 計算結果を表示
    public void OnEqualClick()
    {
        Debug.Log($"Evaluating: {currentInput}");

        try
        {
            float result = EvaluateExpression(currentInput);
            Debug.Log($"Final Result: {result}");

            // 計算結果を currentInput に保存(次の計算に使えるようにする)
            currentInput = result.ToString();
            inputField.text = currentInput; // 入力フィールドに結果を表示
            resultField.text = currentInput; // 結果フィールドにも表示

            isResultDisplayed = true; // 計算完了フラグをセット
        }
        catch
        {
            resultField.text = "Error";
            Debug.LogError("Calculation Error");
        }
    }

    // 数式の評価
    private float EvaluateExpression(string expression)
    {
        Debug.Log($"Computing: {expression}");

        try
        {
            expression = expression.Replace("\"", "");

            object result = new System.Data.DataTable().Compute(expression, "");
            Debug.Log($"Computed Result: {result}");
            return float.Parse(result.ToString());
        }
        catch (System.Exception ex)
        {
            Debug.LogError($"Error in computation: {ex.Message}");
            return 0;
        }
    }

    // +/- ボタンの処理
    private void ToggleSign()
    {
        if (!string.IsNullOrEmpty(currentInput))
        {
            if (currentInput[0] == '-')
                currentInput = currentInput.Substring(1);
            else
                currentInput = "-" + currentInput;

            inputField.text = currentInput;
        }
    }

    // % ボタンの処理
    private void ApplyPercentage()
    {
        if (!string.IsNullOrEmpty(currentInput))
        {
            try
            {
                float value = float.Parse(currentInput) / 100f;
                currentInput = value.ToString();
                inputField.text = currentInput;
            }
            catch
            {
                resultField.text = "Error";
            }
        }
    }
}

(2)ボタンにスクリプトを適用

スクリプトのアタッチ先について
スクリプト(Calculator.cs)は、Canvas や Button、Text にはアタッチせず。
代わりに、空の GameObject(例: CalculatorManager) を作成し、そこにアタッチする。

① 空のオブジェクトを作成
Unityの Hierarchy で右クリック、あるいはGameObject → Create Empty を選択。名前を CalculatorManager に変更。

② CalculatorManagerのInspector で
Add Component をクリック
Calculator スクリプトを追加。スクリプトに UI 要素を紐づける。

Calculator スクリプトの inputField に、Input Field (TMP) を設定
Calculator スクリプトの resultField に、結果表示用 Input Field (TMP) を設定

③各ボタンの OnClick() にスクリプトのメソッドを登録
次に、ボタンの OnClick() に Calculator スクリプトの OnButtonClick() を設定。

Hierarchy で Button 1 などを選択
Inspector の Button コンポーネントを確認
Inspector内に表示されているOnClick() のリストを開く
+ボタンを押して、新しいイベントを追加
イベントの対象を CalculatorManager に設定 (AssetsでなくSceneにある)

None (Object) の部分をクリック
CalculatorManager を選択
(メソッドを Calculator.OnButtonClick(string) に設定、すなわち)
No Function → Calculator → OnButtonClick(string) と選んでいく

引数にボタンの値を設定(下記表)

各ボタンに設定する引数の対応表

ボタン 設定するメソッド OnClick() の引数(“”をつけること)
0 OnButtonClick(string) "0"
1 OnButtonClick(string) "1"
2 OnButtonClick(string) "2"
3 OnButtonClick(string) "3"
4 OnButtonClick(string) "4"
5 OnButtonClick(string) "5"
6 OnButtonClick(string) "6"
7 OnButtonClick(string) "7"
8 OnButtonClick(string) "8"
9 OnButtonClick(string) "9"
. OnButtonClick(string) "."
+ OnButtonClick(string) "+"
- OnButtonClick(string) "-"
* OnButtonClick(string) "*"
/ OnButtonClick(string) "/"
% OnButtonClick(string) "%"
+/- OnButtonClick(string) "+/-"

特別なボタンの設定

ボタン 設定するメソッド OnClick() の引数
= OnEqualClick() (不要)
C OnClearClick() (不要)
4.動作確認
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?