ARアプリ オブジェクトの色カスタム機能の実装 ~前編~

AR

今回はオブジェクトを任意の色に変更できる機能を作成しようと思います

色の変更には赤青黄のスライダーを用いて好みの色を作成できるようにします

オブジェクトによっては複数のマテリアルで色が構成されているので、マテリアルごとに色を変更できるようにします



スクリプト

スクリプトを3つ作成し、名前をColorPicker、ColorApplication、RGBSliderとしてあります

ColorPickerがメインとなり、RGBSliderでスライダーを管理、ColorApplicationでボタン押下時にColorPickerから呼び出すメソッドを管理させるようなイメージです

参考までにColorPickerを以下に記します

using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.UI;

public class ColorPicker : MonoBehaviour
{
    public Image selectedColorDisplay;  // 色が表示されるUIのイメージ  
    public Button pickColorButton;       // 色を選択するためのボタン
    public GameObject colorPickerPanel;  // 色選択UIのパネル
    public Color selectedColor = Color.white; // 選択された色  
    public Dropdown materialDropdown;     // マテリアル選択のためのドロップダウン  

    private Renderer currentRenderer;  //現在のレンダラー
    private List<Material> materials = new List<Material> ();//オブジェクトのマテリアルリスト
    
    private Material [] originalMaterials;//元のマテリアルの配列
    private int lastHighlightedIndex = -1;//最後に強調表示したマテリアルのインデックス
    [SerializeField] Material hmat = default;//強調表示用マテリアル
    public Material _omats;
    private void Start()
    {
        pickColorButton.onClick.AddListener(OpenColorPicker);
        colorPickerPanel.SetActive(false); // 最初はパネルを非表示  
        materialDropdown.ClearOptions();
        materialDropdown.onValueChanged.AddListener(OnMaterialDropdownChanged);//ドロップダウンの変数
       
    }

    private void InitializeMaterialOptions()
    {
        GameObject _selectedObj = SelectedObjectManager.CurrentSelectedObject;
        
        if (_selectedObj != null)
        {
            currentRenderer = _selectedObj.GetComponent<Renderer>();
            if (currentRenderer != null)
            {
                materials.Clear();
                materials.AddRange(currentRenderer.materials);//所属するマテリアルを追加

                //元のマテリアルを保存
                originalMaterials = new Material[materials.Count];
                for (int i = 0; i < materials.Count; i++)
                {
                    originalMaterials[i] = materials[i];                   
                }

                materialDropdown.ClearOptions();//オブジェクト変更時ドロップダウンを初期化
                // ドロップダウンにオプションを追加  
                List<string> options = new List<string>();

                for (int i = 0; i < materials.Count; i++)
                
                {
                    options.Add("マテリアル " + (i + 1)); // 表示するためのオプションを表示  
                }

                materialDropdown.AddOptions(options);
                currentRenderer.material = hmat;//現在色変更可能部位の強調表示
              
            }
        }
    }
    public void OnDestroy()
    {
        GameObject _selectedObj = SelectedObjectManager.CurrentSelectedObject;
        foreach (Material mat in _selectedObj.GetComponent<Renderer>().materials)
            if (mat != null)
        {
            Destroy(mat);            
        }
    }
   

    public void OpenColorPicker()//色変更ボタン
    {
        colorPickerPanel.SetActive(true);
        InitializeMaterialOptions();//ドロップダウン生成
    }

    public void ChangeSelectedColor(Color newColor)
    {
        selectedColor = newColor;
        selectedColorDisplay.color = selectedColor; // カラーサンプル表示  
    }

    public void ApplyColorToSelectedObject(GameObject targetObject)//色決定ボタン
    {        
        if (targetObject != null && materialDropdown.value < materials.Count)
        {

            int selectedIndex = materialDropdown.value;
            //強調表示マテリアルから元のマテリアルに戻す
            RevertHighlight(selectedIndex);
            Debug.Log("強調表示解除");

            //選択されたマテリアルの色を変更
            
            Material selectedMaterial = materials[materialDropdown.value];

            //選択されたマテリアルに色を適用
            selectedMaterial.color = selectedColor;

           
        }
    }

    public void OnMaterialDropdownChanged(int index)
    {
        // ドロップダウンの選択が変更された時の処理  
        if (lastHighlightedIndex >= 0 && lastHighlightedIndex != index)
        {
            // 元の強調表示を戻す  
            RevertHighlight(lastHighlightedIndex);
        }

        // 新しいマテリアルを強調表示  
        HighlightMaterial(index);
    }

    private void HighlightMaterial(int index)
    {
        GameObject _selectedObj = SelectedObjectManager.CurrentSelectedObject;
        if (currentRenderer != null && index < materials.Count)
        {
            // 実際のレンダラーのマテリアルを強調表示用に変更       
            lastHighlightedIndex = index; // 現在強調表示しているインデックスを記録             
            Material[] nmats = currentRenderer.materials;//オブジェクトの全マテリアル要素を取得
            nmats[index] = hmat;//現在選択しているマテリアルの要素番号に強調表示マテリアルを指定
            currentRenderer.materials = nmats;//指定要素のマテリアルを実際に交換      

        }
    }

    public void RevertHighlight(int index)
    {
        
        GameObject _selectedObj = SelectedObjectManager.CurrentSelectedObject;
        if (currentRenderer != null && index < materials.Count && originalMaterials[index] != null)
        {
            // 元のマテリアルに戻す  
            currentRenderer.materials[index] = originalMaterials[index]; // 実際のレンダラーのマテリアルを元に戻す  
            Renderer rnd = _selectedObj.GetComponent<Renderer>();
            Material[] omats = rnd.materials;
            _omats = originalMaterials[index];
            omats[index] = _omats;
            rnd.materials = omats;
        }
    }
}

このようなUIを作成し、各スクリプトの指定部分にアタッチ

スクリプト自体は3つともキャンバスにアタッチしています

マテリアルと表示されているドロップダウンからマテリアルを選択することが出来ます

その右にある白い四角部分は、赤青黄のスライダーで構成される色をリアルタイムで表示します

初期状態ではマテリアル要素0番目が選択されるようになっています

選択されているマテリアルがオブジェクトのどの部分か分かりやすくするために強調表示用マテリアルと入れ替えるようにしました

選択マテリアルを変更した場合、強調表示用マテリアルを元のマテリアルと交換し、変更先のマテリアルを強調表示用マテリアルと入れ替えます

それを可能にするために元のマテリアルを保存するようにしてあります

各メソッドの役割

InitializeMaterialOptions()メソッドでオブジェクトのマテリアル要素を取得し、ドロップダウンオプションを生成しています

OnDestroy()メソッドでメモリーリーク対策をしています

OpenColorPicker()メソッドを「色変更ボタン」にリンクさせることで、写真のUIパネルを呼び出し、

InitializeMaterialOptions()メソッドを実行させます

ChangeSelectedColor(Color newColor)メソッドでRGBSliderスクリプトから現在のカスタムカラーを取得し、イメージを画面上に映し出します

ApplyColorToSelectedObject(GameObject targetObject)メソッドを「決定ボタン」とリンクさせることで、強調表示を終了し、オブジェクトにカスタムカラーを反映します

HighlightMaterial(int index)メソッドで現在選択されているマテリアルを強調表示用マテリアルと入れ替えています

RevertHighlight(int index)メソッドでその逆のことをしています

OnMaterialDropdownChanged(int index)メソッドで新たに選択されたマテリアルを強調表示、元のマテリアルの強調表示を解除しています

実行

やりたかった動作はすべて実現出来ているので成功です!

しかしこれでは分かりにくいかもしれないので、次回解説をしようと思います



まとめ

今回の実装は自分にとってはかなり複雑で、失敗の繰り返しでしたが、実現までたどり着けて本当によかったと思います

タイトルとURLをコピーしました