LoginSignup
11
9

More than 5 years have passed since last update.

UnityでスクリプトからUVを操作する

Posted at

初めに

 例えば、キャラクタの表情をテクスチャで貼って、UV座標を変更することで表情を変える、というようなケースがあるかと思います。最近、このようなものに取り組んでいたのですが、自分が引っ掛かった点をご紹介いたします。

1.ボーンの入ったキャラクタはSkinnedMeshRendererのsharedMeshを使う。
2.インスタンス化したsharedMeshを使う。
という2点です。

MeshRendererがアタッチされているオブジェクトの場合

 例えば、CubeのようなプリミティブをUnityのエディタ上で作成すると、このようにMesh FilterとMeshRendererが自動的にアタッチされています。201803282220.png
 この場合は、MeshFilterコンポーネントからmeshを取得することができます。meshクラスのメンバにUVがVector2の配列として定義されているので、それを操作すればよいです。

meshUV.cs
一部抜粋
private Mesh myM;
void Start () {
        myMesh = gameObject.GetComponent<MeshFilter>().mesh;
}
void Update(){
        Vector2[] nUV = myMesh.uv;
        for (int i=0; i < myMesh.uv.Length; i++)
        {
            nUV[i].x *= 0.99f;
            nUV[i].y *= 0.99f;
        }
        myMesh.uv = nUV;
}

201803282256.png
201803282257.png
このように貼られたテクスチャが変化していきます。

SkinnedMeshRendererがアタッチされているオブジェクトの場合

 多くの場合、キャラクターとして使用したいオブジェクトはボーンで動かすものです。例えば、キャラクターのFBXファイルをインポートして、そのメッシュオブジェクトを見ると、先ほどのMeshFilterやMeshRendererは見当たりません。201803282242.png
よって、先ほどのように、MeshFilterのMeshからUVを操作することはできません。
そのかわり、ボーンで変形されるキャラクタでは、このSkinnedMeshRendererが用いられます。SkinnedMeshRendererからSharedMeshを取得することができ、このSharedMeshのメンバにUVがあるので、それを操作します。

meshUV.cs
    private Mesh myM;
    private Mesh Imesh;
    // Use this for initialization
    void Start()
    {
        myMesh = gameObject.GetComponent<SkinnedMeshRenderer>().sharedMesh;
    }

    // Update is called once per frame
    void Update()
    {
        Vector2[] nUV = myMesh.uv;
        for (int i = 0; i < myMesh.uv.Length; i++)
        {
            nUV[i].x *= 0.99f;
            nUV[i].y *= 0.99f;
        }
        myMesh.uv = nUV;
    }

ところが今回は、このようにUVを操作すると問題が起きます。
UVを操作できるのですが、スクリプトを実行して一度UVを変更してしまうと、実行を停止しても元には戻りません。先ほどのCubeの場合は停止して再実行するとスクリプトによるUVの変更がリセットされているのですが、SkinnedMeshRendererの場合はそうはいかないようです。

インスタンス化したものを使用することで、元のデータを操作せずに保存しておく

 そこで、先ほどのスクリプトに少し手を加えます。

meshUV.cs
    private Mesh myM;
    private Mesh Imesh;
    // Use this for initialization
    void Start()
    {
        myMesh = gameObject.GetComponent<SkinnedMeshRenderer>().sharedMesh;
        Imesh = Instantiate(myMesh);
        gameObject.GetComponent<SkinnedMeshRenderer>().sharedMesh = Imesh;
    }

    // Update is called once per frame
    void Update()
    {
        Vector2[] nUV = Imesh.uv;
        for (int i = 0; i < Imesh.uv.Length; i++)
        {
            nUV[i].x *= 0.99f;
            nUV[i].y *= 0.99f;
        }
        Imesh.uv = nUV;
    }

sharedMeshをInstantiate()でインスタンス化して、sharedMeshの参照先をインスタンスに変更します。
こうしてから、インスタンスを操作すれば先ほどのような問題は起こらず、実行を停止すれば、UVへの変更はリセットされます。
 余談ですが、これで、UVだけでなく頂点も弄り放題です。
 また、UVを変更する場合に限らず、色々な場合に応用できると思われます。

11
9
1

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
11
9