引例:

Unity3D中,transform.position.x = 1;
是否能给物体成功赋值x的位置?

分析:

在使用Unity3D开发过程中,我们经常需要修改物体的坐标实现物体位置的修改。

下图是unity官网对Transform.position和Vector3的描述:

unity inspector不可修改_数据


unity inspector不可修改_数据_02

transform.position 是transform的一个属性、Vector3类型的值。
Vector3 是struct 类型。

我们知道,struct是值类型,在赋值和函数传递中都是值传递。即赋值时为拷贝而不是地址引用。举例如下:

unity inspector不可修改_unity_03


上图将结构体a赋值给b,然后去分别修改a和b的值观察结果,修改a不会影响b,修改b也不会影响a

通过属性访问器或方法返回结构体等值类型数据时,返回的是目标地址的值的拷贝,即返回的是值类型的数据。对这个返回值进行修改不会影响到原值。
通过属性访问器或方法返回引用类型数据时,返回的是目标地址的地址,即返回的是地址。对这个返回值进行修改会影响原值。

举例如下:

定义部分:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public struct StructVector
{
    public float x, y, z;
}

public class ClassVector
{
    public float x, y, z;
}

public class test1
{
    Vector3 pos;
    public Vector3 position1
    {
        get
        {
            return pos;
        }
        set
        {
            pos = value;
        }
    }
    public Vector3 getPos1()
    {
        return pos;
    }


    StructVector structPos;

    public StructVector position2
    {
        get
        {
            return structPos;
        }
        set
        {
            structPos = value;
        }
    }

    public StructVector getPos2()
    {
        return structPos;
    }


    public Vector3 position3;
    public StructVector position4;

    private ClassVector classPos = new ClassVector();

    public ClassVector position5
    {
        get
        {
            return classPos;
        }
        set
        {
            classPos = value;
        }
    }

    public ClassVector getPos5()
    {
        return classPos;
    }
}

测试部分:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test2 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        test1 testPos = new test1();
        testPos.position1 = new Vector3(1, 1, 1);
        testPos.position1.x = 2; //无法修改test1.position1的返回值,因为它不是变量。
        testPos.getPos1().x = 2;//无法修改test1.position1的返回值,因为它不是变量。
        //-------------
        testPos.position2.x = 1;//无法修改test2.position1的返回值,因为它不是变量。
        testPos.getPos2().x = 1;//无法修改test2.position1的返回值,因为它不是变量。
        //--------------
        print("position3.x ===== "+testPos.position3.x);
        testPos.position3.x = 10;
        print("position3.x ===== " + testPos.position3.x);
        //-------------
        print("position14x ===== " + testPos.position4.x);
        testPos.position4.x = 20;
        print("position14x ===== " + testPos.position4.x);
        //---------------
        print("position15x ===== " + testPos.position5.x);
        testPos.position5.x = 30;
        print("position15x ===== " + testPos.position5.x);
        testPos.getPos5().x = 40;
        print("position15x ===== " + testPos.position5.x);
    }
}

将Test2挂载到物体身上,运行结果:

unity inspector不可修改_数据_04

我们测试了5中获取修改值的方式,分别为:
1)通过属性访问器获得Vector3类型数据,并修改其值 — 报错
2)通过函数获得Vector3类型数据,并修改其值 ----报错
3) 通过属性访问器获得结构体类型数据,并修改其值 — 报错
4)通过函数获得结构体类型数据,并修改其值 ----报错
5)直接获得Vector3的值并修改— 成功
6)直接获得结构体struct的值并修改— 成功
7)通过属性访问器获得类对象,并修改其对象成员变量值 — 成功
8)通过函数获得类对象,并修改其成员变量值 ---- 成功

因此,当我们直接给返回的position.x赋值时,这时候并不能影响真正的坐标值,因为只是一个拷贝值而已。

那么我们应该怎么给坐标赋值?
使用属性的set访问器,
即 transform.position = new Vector3(1,1,1);
或者:
Vector3 pos = game.transform.position;
pos.x = pos.x + 20;
game.transform.position = pos;

总结:

这个技术点讨论的本质是值类型数据和引用类型数据的传递方式
1)修改值类型数据的返回值不会影响到原值;
2)修改引用类型数据的返回值,因为是地址,就会修改到原值。

Vector3属于Struct类型
属性访问器和函数返回值对于值类型返回拷贝值;对于引用类型返回地址;
因此开头说的例子的写法是错的,无法直接通过修改transform.position.x改变gameobject的位置。
想要改变一个值类型的变量,需要直接改变其地址的值,比如Vector3 a = new Vector3(1,1,1)
a.x = 2;
这样可以改变a的x为2;

举一反三:

rotation也是transform的属性,其为Quaternion,也属于struct,因此也不可以像下图这样直接改变其值。

unity inspector不可修改_数据_05

补充:

C#中:
值类型包括:int、float、double等数值型;bool;struct 结构体;枚举;可空类型。

引用类型包括:数组,类、接口、委托,object,字符串。