引例:
Unity3D中,transform.position.x = 1;
是否能给物体成功赋值x的位置?
分析:
在使用Unity3D开发过程中,我们经常需要修改物体的坐标实现物体位置的修改。
下图是unity官网对Transform.position和Vector3的描述:
transform.position 是transform的一个属性、Vector3类型的值。
Vector3 是struct 类型。
我们知道,struct是值类型,在赋值和函数传递中都是值传递。即赋值时为拷贝而不是地址引用。举例如下:
上图将结构体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挂载到物体身上,运行结果:
我们测试了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,因此也不可以像下图这样直接改变其值。
补充:
C#中:
值类型包括:int、float、double等数值型;bool;struct 结构体;枚举;可空类型。
引用类型包括:数组,类、接口、委托,object,字符串。