今回はプレイヤーが数値を入力するとオブジェクトの大きさが変動する機能を実装しようと思います
考察
後ほど記載するコードは完成形なのですが、そこに到達するまでの過程で発生した問題に少し触れようと思います
本アプリではオブジェクトの寸法を実寸で表現しており、ボックスコライダーのサイズを表示する事でその寸法を可視化しています
詳細はこちらにて
今回は入力した寸法を反映させるのが目的となるので、オブジェクトにそのまま入力した値を反映させればいいだろうと考えておりました
試験的なスクリプトを作成し、オブジェクトに偏りのない普通の数値を与えて実行してみたところ、オブジェクトはとてつもなく巨大になってしまいました
ボックスコライダーもオブジェクトの変動に付随して巨大化したものの、表示されるボックスコライダーサイズには変動がありませんでした

なぜそうなったかといと、オブジェクトの大きさに関わる要素は、赤線部分の「Scale」しかなく、ここに任意の数値を与えたところで期待通りの大きさになる分けがないのです
しかも何故かは分かりませんがデフォルトでスケールがすべて0.01になっているのも巨大化の要素になっていました
これを解決するには入力した数値をスケールに換算して取り扱う必要があると仮定し、簡単な方程式を作成しました
A:B=C:D → A*D=B*C
の法則を利用し
元の寸法:0.01=入力した寸法:新しいスケール
元の寸法*新しいスケール=0.01*入力した寸法
新しいスケール=0.01*入力した寸法/元の寸法
としてスクリプトに反映することにしました
スクリプト
using UnityEngine;
using UnityEngine.UI;
public class BoxSizeAdjuster : MonoBehaviour
{
public static BoxSizeAdjuster Instance { get; private set; } // シングルトンのインスタンス
public InputField widthInput; // 幅を入力するInputField
public InputField heightInput; // 高さを入力するInputField
public InputField depthInput; // 奥行きを入力するInputField
public Text warningText; // エラーメッセージ用のText
public GameObject resizePanel;
private GameObject targetObject; // サイズを変更するオブジェクト
public Text _SizeText;//サイズ表示用
BoxCollider m_Collider;
private void Awake()
{
// シングルトンのインスタンス設定
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject); // すでにインスタンスが存在する場合は削除
}
}
public void SetTargetObject(GameObject selectedObject)
{
targetObject = selectedObject; // 選択されたオブジェクトを設定
}
public void OnResizeButtonClicked()
{
// 警告メッセージをリセット
warningText.text = "";
if (ValidateInput())
{
float width = float.Parse(widthInput.text);
float height = float.Parse(heightInput.text);
float depth = float.Parse(depthInput.text);
AdjustBoxSize(width, height, depth);
resizePanel.gameObject.SetActive(false);
//入力値リセット
widthInput.text = "";
heightInput.text = "";
depthInput.text = "";
}
}
private bool ValidateInput()
{
// 各InputFieldのテキストが空でないことを確認
if (string.IsNullOrWhiteSpace(widthInput.text) || string.IsNullOrWhiteSpace(heightInput.text) || string.IsNullOrWhiteSpace(depthInput.text))
{
warningText.text = "すべてのフィールドに数値を入力してください。";
return false;
}
// 数値として解析できるかを確認
if (!float.TryParse(widthInput.text, out float width) || width <= 0)
{
warningText.text = "幅は正の数でなければなりません。";
return false;
}
if (!float.TryParse(heightInput.text, out float height) || height <= 0)
{
warningText.text = "高さは正の数でなければなりません。";
return false;
}
if (!float.TryParse(depthInput.text, out float depth) || depth <= 0)
{
warningText.text = "奥行きは正の数でなければなりません。";
return false;
}
return true;
}
private float RoundSize(float size)
{
float fractionalPart = size - Mathf.Floor(size); // 小数部分を取得
if (fractionalPart < 0.05f)
{
return Mathf.Floor(size); // 小数部分が0.05未満なら切り捨て
}
else if (fractionalPart < 0.6f)
{
return Mathf.Floor(size) + 0.5f; // 小数部分が0.05以上0.7未満であれば0.5を足す
}
else
{
return Mathf.Ceil(size); // 小数部分が0.95以上なら切り上げ
}
}
private void AdjustBoxSize(float width, float height, float depth)
{
// MeshFilterからメッシュを取得
MeshFilter meshFilter = targetObject.GetComponent<MeshFilter>();
if (meshFilter == null)
{
warningText.text = "選択されたオブジェクトにはメッシュがないか、MeshFilterが設定されていません。";
return;
}
// 現在のメッシュのバウンディングボックスを取得
Bounds bounds = meshFilter.sharedMesh.bounds; // ローカル空間でのバウンディングボックス
Vector3 localSize = bounds.size; // ローカルサイズを取得
// 現在のスケールを考慮して実際のサイズを計算(ワールドサイズ)
Vector3 worldSize = Vector3.Scale(localSize, targetObject.transform.localScale);
m_Collider = targetObject.GetComponent<BoxCollider>();//クリックしたオブジェクトのコライダーを取得
// 新しいスケールを計算
float newScalex = width * 0.01f/ m_Collider.size.x;
float newScaley = height * 0.01f/ m_Collider.size.y;
float newScalez = depth * 0.01f/ m_Collider.size.z;
Vector3 newScale = new Vector3(newScalex, newScaley, newScalez);
targetObject.transform.localScale = newScale;
// 新しいサイズの取得
Vector3 newMeshSize = Vector3.Scale(bounds.size, newScale);
float newWidth = newMeshSize.x * 100f; // cmに変換
float newHeight = newMeshSize.y * 100f; // cmに変換
float newDepth = newMeshSize.z * 100f; // cmに変換
// サイズを丸める
newWidth = RoundSize(newWidth);
newHeight = RoundSize(newHeight);
newDepth = RoundSize(newDepth);
Debug.Log(newWidth);
Debug.Log(newHeight);
Debug.Log(newDepth);
string _str1 = "cm ";
string _str2 = "高さ:";
string _str3 = "奥行:";
float _str4 = newWidth;
float _str5 = newHeight;
float _str6 = newDepth;
_SizeText.text = string.Format("幅:{3}{0}{1}{4}{0}{2}{5}{0}", _str1, _str2, _str3, _str4, _str5, _str6);
}
}
数値入力にはインプットフィールドのUIを使用しました
作成した方程式は
// 新しいスケールを計算
float newScalex = width * 0.01f/ m_Collider.size.x;
float newScaley = height * 0.01f/ m_Collider.size.y;
float newScalez = depth * 0.01f/ m_Collider.size.z;
こちらで行っています
Vector3 newScale = new Vector3(newScalex, newScaley, newScalez);
それらの値をこちらに代入し、下のコードで実際のサイズを変更しています
targetObject.transform.localScale = newScale;
また、万が一端数が出た際の対策として数値を丸めるようにしてあります
寸法を3か所入力する際に不備がある場合実行はされず、エラーを表示するようにしてあります
実行

オブジェクト選択状態時に表示される「サイズ変更ボタン」をクリック

インプットフィールドが3つ表示されるのでサイズを入力します

入力が完了したら決定をクリックすると・・・


ちっちゃ(;゚Д゚)
入力値に忠実にオブジェクトサイズが変動し、表示される数値も入力値の物が反映されているので成功です!
まとめ
オブジェクトを厳密に実寸でサイズ変動させたい場合は
希望の数値をスケールに換算する必要がある