查看: 1688|回复: 1
打印 上一主题 下一主题

[经验分享] 刨根问底U3D---Vector3 你到底是蔬菜呢还是水果呢?

[复制链接]

100

主题

3

听众

7683

积分

高级设计师

Rank: 6Rank: 6

纳金币
2378
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

跳转到指定楼层
楼主
发表于 2015-4-21 17:33:43 |只看该作者 |倒序浏览
事情的起因还是因为一段代码,因为在做一个2D TileBase的游戏 所以需要有一个简单的 Tile坐标到世界坐标的变换
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
public static Vector3 GetTileWorldPosByTileIndex(int _tileIndexX, int _tileIndexY , Vector3 _result)
{
        if(_result == null)
        {
                _result = new Vector3();
        }
        _result.x = TileConst.TILE_WIDTH * _tileIndexX;
        _result.y = TileConst.TILE_HEIGHT * _tileIndexY;
        _result.z = 0;
        return _result;
}


代码逻辑很简单,特殊的地方就是后面传入的Vector3,因为函数会被经常调用 所以不想每次都New出来一个新的Vector3. OK 运行..

Warning CS0472: The result of comparing value type `unityEngine.Vector3' with null is `false'
Unreachable code detected


WTF?! 哪里错了? Vector3 居然不能和null 判等?  嘿经过我一通测试 果真发现一些问题
来看如下的代码
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
public class Test01 : MonoBehaviour
{
        void Start ()
        {
                int inputInt = 500;
                int outputInt = SetIntWithRandom (inputInt);
                Debug.Log (inputInt);
        }
         
        public  int SetIntWithRandom(int _input)
        {
                _input = Random.Range(-300,300);
                return _input;
        }
}


这段应该很简单,整出来一个int 类型然后传入函数内部, 然后在Log出来 看看是否有发生改变。 Ok 运行
Log结果 500, 说明没有发生任何改变。 也就是说 int 类型的变量是 传值不是传址的

再换下一组
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
public class Test01 : MonoBehaviour
{

        void Start ()
        {
                int[] inputIntArray = new int[2];
                inputIntArray [0] = 500;
                int[] outputIntArray = SetIntArrayWithRandom (inputIntArray);
                Debug.Log (inputIntArray [0]);
        }
         
        public int[] SetIntArrayWithRandom(int[] _inputIntArray)
        {
                _inputIntArray[0] = Random.Range(-300,300);
                return _inputIntArray;
        }

}


Log结果 -89 发生改变. 对于Array来说 是传址不是传值的.

Ok 来看 Vector3
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
public class Test01 : MonoBehaviour
{

        void Start ()
        {
                Vector3 inputV3 = new Vector3 ();
                inputV3.x = 500;
                Vector3 outputV3 =SetV3ValueWithRandom (inputV3);
                Debug.Log (inputV3.x);
        }
         
        public Vector3 SetV3ValueWithRandom (Vector3 _result)
        {
                _result.x = Random.Range (-300, 300);
                return _result;
        }

}


Log结果 500.
也就是说呢, 虽然Vector3 初始化时候 需要用New 操作符, 但是Vector3 却是一个基础类型 和 float,int 一样

之前有很多类似的地方都是想节约内存不每次进行new操作,于是类中做了一个引用,然后调用函数时候将引用传过去。
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
Vector3 inputV3 = new Vector3 ();
inputV3 =SetV3ValueWithRandom (inputV3)


现在看来,其实一点都没有省...

这个也解释了 为什么再给 transfrom的position赋值时候不能 transform.position.x = 100; 这样去做 会报错说

Error CS1612: Cannot modify a value type return value of `UnityEngine.Transform.position'. Consider storing the value in a temporary variable (CS1612)

我又做了几个相关的测试,实在懒得写了 就把相关结果说一下吧(有兴趣可以私聊哈)


1· 每次去New Vector3 对性能开销大么?

我Profile了一下, 在一个Update里面 循环去new 10w个 Vector3, CPU和内存都没有任何的波动.
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
vod Update()
{
        Vector3 tmp;
        for(int i=0 ; i<100000;i++)
        {
                Vector3 tmp = new Vector3();
                tmp.x = Random.Range (-300, 300);
        }
        transform.position = tmp
}


也就是完全把它当int来看就好了,虽然使用的是New操作符 总感觉 要有很大动静似的...
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
vod Update()
{
        int tmp;
        for(int i=0 ; i<100000;i++)
        {
                tmp = Random.Range (-300, 300);
        }
}


2· 虽然开销很小 但是我还是想类中保留一个引用,然后不用每次去New出来 应该怎么做?

直接在函数的参数中改为ref即可, 感觉ref是C# 中很变态的东西 int啊 float啊什么的 都能ref (之前接触到得As3,Java是不行的 从C++上面继承来的特性吧 这个应该是)
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
public static void GetTileWorldPosByTileIndex(int _tileIndexX, int _tileIndexY , ref Vector3 _result)
{
        _result.x = TileConst.TILE_WIDTH * _tileIndexX;
        _result.y = TileConst.TILE_HEIGHT * _tileIndexY;
        _result.z = 0;
}



3· 注意一下 Nullable Type

可以看下这篇文章 http://unitypatterns.com/nullable-types/
两个问题,一个是说
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
Vector3 tmp;

Debug.Log(tmp.x) //  这里会有结果,结果是0



也就是说 Vector3 在没有new操作时候 是有默认值的 和 布尔默认值是false, int默认值是0 一个道理

第二个 如果不希望这样的话 那就要使用 牛逼操作符 问号..
[C#] [color=rgb(51, 102, 153) !important]纯文本查看 [color=rgb(51, 102, 153) !important]复制代码
[color=white !important]
[color=white !important]?
Vector3? tmp;
if(tmp.HasValue)
{
        Debug.Log(tmp.Value);
}


在Vector3后面加一个问号 将其转变为Nullable Type 然后就可以用HasValue判断是否有值 然后用 xxx.Value获得这个值了



补充:
感谢miter..  

Vector3 是 Struct类型 具体可以看:

http://www.cnblogs.com/haofaner/archive/2011/12/08/2281100.html
https://msdn.microsoft.com/zh-cn/library/ms229017(v=vs.100).aspx
https://msdn.microsoft.com/zh-cn/library/ah19swz4.aspx

这几篇文章...

万分感谢哈.

======================
再更新两篇 感谢amazonove
transfrom不能更改position xyz的原因
https://msdn.microsoft.com/zh-cn/library/wydkhw2c.aspx

Structs 介绍
https://msdn.microsoft.com/en-us/library/aa288471%28v=vs.71%29.aspx

摘录两段

Structs vs. Classes

Structs may seem similar to classes, but there are important differences that you should be aware of. First of all, classes are reference types and structs are value types. By using structs, you can create objects that behave like the built-in types and enjoy their benefits as well.


Heap or Stack?

When you call the New operator on a class, it will be allocated on the heap. However, when you instantiate a struct, it gets created on the stack. This will yield performance gains. Also, you will not be dealing with references to an instance of a struct as you would with classes. You will be working directly with the struct instance. Because of this, when passing a struct to a method, it's passed by value instead of as a reference.



分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

9903

主题

126

听众

7万

积分

首席设计师

Rank: 8Rank: 8

纳金币
53488
精华
316

最佳新人 热心会员 灌水之王 活跃会员 突出贡献 荣誉管理 论坛元老

沙发
发表于 2015-4-21 17:35:12 |只看该作者
字都偏了啊...
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2025-7-23 10:08 , Processed in 0.217209 second(s), 28 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部