【Unity】スクリプトでコンポーネントを追加/削除する

投稿者: | 2021-03-08

今までは同じメッシュのオブジェクトに別のコンポーネントやタグなどが付いていた場合に、それをプレハブ化しておいて、置き換えるようにしていましたが、同じオブジェクトにコンポーネント等を追加、削除するようにしてみました。

もとのテレビはメッシュとマテリアル、Box Colliderが付いたシンプルなものです。

これにスクリプトを付けると、Startメソッドでオーディオソースなどのコンポーネントが付けられて、テレビを付けるメソッドで再生されるようにしました。

using UnityEngine;
using UnityEngine.Video;

public class TV1 : MonoBehaviour
{
    AudioSource audioSource;
    MeshRenderer meshRenderer;
    Texture defaultTexture;
    VideoPlayer videoPlayer;
    TV1Assets tv1Assets;
    Light pointLight;

    bool isOn;

    public bool GetIsOn()
    {
        return isOn;
    }
    // Start is called before the first frame update
    void Start()
    {
        tv1Assets = Instantiate(ItemManager.GetInstance().GetItem("TV1Assets").GetPrefab()).GetComponent<TV1Assets>();
        meshRenderer = gameObject.GetComponent<MeshRenderer>();

        // VideoPlayerを追加
        videoPlayer = gameObject.AddComponent<VideoPlayer>();
        videoPlayer.playOnAwake = false;
        videoPlayer.isLooping = true;
        videoPlayer.renderMode = VideoRenderMode.RenderTexture;
        videoPlayer.audioOutputMode = VideoAudioOutputMode.None;
        videoPlayer.clip = tv1Assets.GetVideoClip();
        videoPlayer.Prepare();

        // オーディオソースを追加
        audioSource = gameObject.AddComponent<AudioSource>();
        audioSource.volume = 0.5f;
        audioSource.spatialBlend = 0.9f;
        audioSource.playOnAwake = false;
        audioSource.loop = false;
        audioSource.clip = tv1Assets.GetAudioClip();

        // ポイントライトを作る
        GameObject g = new GameObject();
        g.transform.position = transform.position - transform.forward * 0.8f;
        pointLight = g.AddComponent<Light>();
        pointLight.type = LightType.Point;
        pointLight.intensity = 15f;
        pointLight.range = 40f;
        pointLight.enabled = false;
        pointLight.shadows = LightShadows.Soft;
    }

    public void TVON()
    {
        isOn = true;
        // ライトをオン
        pointLight.enabled = true;

        // 音を再生
        audioSource.Play();

        // 画面を光らせる
        Material mat = meshRenderer.materials[1];
        mat.SetColor("_EmissiveColor", new Color(0.1f, 0.1f, 0.1f));
        mat.SetFloat("_EmissiveIntensity", 0.1f);
        defaultTexture = mat.GetTexture("_BaseColorMap");

        // 画面に動画を再生
        RenderTexture rt = new RenderTexture(256, 256, 24, RenderTextureFormat.ARGB32);
        videoPlayer.targetTexture = rt;
        mat.SetTexture("_BaseColorMap", rt);
        videoPlayer.Play();
    }

    public void TVOFF()
    {
        isOn = false;
        

        // 画面の色を戻す
        Material mat = meshRenderer.materials[1];
        mat.SetColor("_EmissiveColor", Color.black);
        mat.SetFloat("_EmissiveIntensity", 0f);
        mat.SetTexture("_BaseColorMap", defaultTexture);

        audioSource.Stop();

        // 追加したライトやコンポーネントを削除
        Destroy(audioSource);
        Destroy(videoPlayer);
        Destroy(this);
        Destroy(tv1Assets.gameObject);
        Destroy(pointLight.gameObject);
    }

}

オーディオクリップやビデオクリップは、それを持っておくクラスを作って、プレハブ化して置きました。

using UnityEngine;
using UnityEngine.Video;


public class TV1Assets : MonoBehaviour
{
    [SerializeField] VideoClip videoClip;
    public VideoClip GetVideoClip()
    {
        return videoClip;
    }
    [SerializeField] AudioClip audioClip;

    public AudioClip GetAudioClip()
    {
        return audioClip;
    }
}

テレビを消すメソッドを呼ぶと、このスクリプトを含むコンポーネントやライトが削除されて、元の状態に戻ります。

テレビはアタッチしておいたり、タグや名前で検索できます。

// アタッチする
[SerializeField] GameObject tv1;

// ---
// タグや名前から探す
foreach (var g in GameObject.FindGameObjectsWithTag("Props"))
{
    if (g.name == "TV1")
    {
        tv1 = g;
        break;
    }
}

// ---
if (Input.GetKeyDown(KeyCode.Alpha1))
{
    tv1.AddComponent<TV1>();

}
else if (Input.GetKeyDown(KeyCode.Alpha2))
{
    TV1 t = tv1.GetComponent<TV1>();
    if (t.GetIsOn())
    {
        tv1.GetComponent<TV1>().TVOFF();

    }
    else { 
        tv1.GetComponent<TV1>().TVON();
    }
}

変更を加えたいものが複数あるときは、同じメソッドの引数にオブジェクトを渡して、名前やインデックスなどで場合分けしたりすると便利です。

コメントを残す

メールアドレスが公開されることはありません。