2018-07-25 17:53:53 weixin_42794728 阅读数 119

问题:通过携程向服务器请求数据后,通过闭包形式给控件赋值,此时如果控件优先于赋值方程完成前被destroy,会报错“尝试给一个被destroy物体赋值”,次问题可能引起软件崩溃(特别是移动端)
示例代码如下:

private void GetBalance()//调用方程
{
    for (int i = 0; i < m_curItems.Count; i++) //m_curItems存储了一堆控件对象
	{
	    m_curItems[i].getBalance();//调取控件对象自己的函数
	}
}


[SerializeField]private Text balance; //控件绑定
public void getBalance()//请求数据,闭包内赋值
{
    //此SDK数据请求方式为协程
	MarvelSDK.SDK.API.GetETHBalance(m_detail.addr, (error,x) =>
	{
		if (x != null)
		{
			balance.text = x.balance;//赋值
		}
	});
}

解决思路:
1.    控件删除时,停止所有协程        StopAllCoroutines();
2.    添加判断条件,判断控件是否还存在,如果存在则赋值,不存在则不进行操作

尝试debug时所遇到的问题:
1.    虽然停止了数据请求方程的协程,但是由于采用闭包方式在给控件赋值,因闭包特性,所以闭包内赋值方程依旧会执行,所以思路1不好使


2.    起先添加判断条件,尝试通过判断此控件是否还存在于场景中来控制赋值方程的运作

 if(balance.gameObject.activeInHierarchy)

但此时犯了一个判断错误,”activeInHierarchy“是用来判断控件是否在场景中是否可见,但如果控件被destroy了,那么此时此方法也会报“尝试访问被destroy物体”的错误。
由此,引出今天的重点,换一个思维来判断控件是否被destroy。
首先,确定一个基础概念,一个控件被实例化之后,实际上是在内存内开辟了一段内存空间,我们对控件的操作实际上是对这段内存空间的操作,既然是对内存空间的操作,也就意味之我们需要一个内存指针,而此时我们的内存指针其实就是这个预制体绑定参数:

[SerializeField]private Text balance;//这货可以理解成一个指针,指向它所绑定的控件对象

而且我们知道unity3d中销毁函数Destroy()的功能是销毁实例,释放控件所绑定的内存区域,重点来了,既然这个控件内存区域已经被释放,那么指向它的函数指针也会被销毁,但由于我们我们在闭包中使用了这个指针,因闭包的特性,它会被闭包所生成的匿名类抓取,被匿名类所引用(任何一个函数或参数如果被其他对象所引用,那么他将不会被销毁,直到引用它的对象被销毁时才会一并销毁)所以依然存在,但由于它所指的控件被destroy了,所以此时这个函数指针会指向null。
所以综上所述,最终解决这个问题的方式是判断这个函数指针是否指向null, 代码如下:

private void GetBalance()//调用方程
{
	for (int i = 0; i < m_curItems.Count; i++)
	{
	    m_curItems[i].getBalance();
	}
}

[SerializeField]private Text balance; //指向控件的内存区域
public void getBalance()//请求数据,闭包内赋值
{
	MarvelSDK.SDK.API.GetETHBalance(m_detail.addr, (error,x) =>
	{
		if (x != null)
		{
		    if(balance == null	)//如果指向null,意味着控件被删除,则强制返回上层函数
			    return;
								
			balance.text = x.balance;//赋值
		}
    });
}

 

2018-08-27 20:19:16 loongkingwhat 阅读数 1822

写在前面
阅读该文章之前,首先需要知道:
1.不同坐标系(世界坐标系、物体坐标系、惯性坐标系)之间的联系和区别;可参考博客坐标系详解
2.同一个物体基于不同的坐标系进行旋转所得结果不一样;

本文的目的是探究游戏引擎Unity3D不同的旋转API的表现,我们都知道,在Unity3D中有多种刚体旋转的API:①有对localRotation进行赋值修改的方式达到旋转的目的;②有通过Rotate方法(Space.Self)进行旋转的;③对应还有通过Rotate方法(Space.World)进行旋转的。按照网上一些教程的解释,①就是在父节点坐标系下进行旋转,②和③则分别是绕物体坐标系和世界坐标系的轴进行旋转。

在理解世界坐标系、物体坐标系和惯性坐标系的基础上,非常有必要明确各个API定义的刚体旋转到底是如何进行的,是基于世界坐标系还是基于世界坐标系平移之后的惯性坐标系;基于父节点的局部坐标系进行旋转,是否需要将父节点的物体坐标系平移到本物体坐标系原点之后再进行旋转。

这部分的内容在其他的博客或者资料中是没有详细讲解的,通常默认大家是已知的,但是这种不明确容易造成对API理解不深而使用错误的情况。这篇博客主要探究这部分的问题。

(一)Unity 3D绕轴旋转

本文之后的示例都将使用下图中的实例,以研究小臂这一段刚体为代表,去研究刚体在Unity中的旋转。

值得注意的是,人体关节刚体与普通的几何物体刚体在物体坐标系上有一定的差别。常见的几何刚体(例如立方体,球体,圆柱体)的物体坐标系的原点都在几何刚体的中心点,而人体关节刚体的物体坐标系原点一般在关节的某一端。例如,小臂关节的物体坐标系的原点就在肘关节,大臂关节的物体坐标系的原点在肩关节附近。

此处选用物体坐标系原点位于物体某一端点的例子去讨论,更加直观。
这里写图片描述

1.transform.localRotation属性控制刚体旋转

API手册中Description 描述
The rotation of the transform relative to the parent transform’s rotation.
该变换的旋转角度相对于父级变换的旋转角度。
·
Unity stores rotations as Quaternions internally. To rotate an object, use Transform.Rotate. Use Transform.localEulerAngles for modifying the rotation as euler angles.
Untiy使用四元数来存储旋转角度。要旋转一个物体,可以使用Transform.Rotate,也可以使用Transform.localEulerAngles为设置作为欧拉角的旋转角度。

这个API解释对不熟悉Unity3D和图形学的同学来说,看到就会觉得什么都说了,什么都没说。看懂了也不太会用。下面来做具体的测试看看到底这个API是怎么在父坐标系下控制刚体旋转的。

通过以下代码控制人物:

    GameObject upperarm_l;
    GameObject lowerarm_l;
    public float Local_X =0.0f;
    public float Local_Y = 0.0f;
    public float Local_Z = 30.0f;

    // Use this for initialization
    void Start () {
        upperarm_l = GameObject.Find("upperarm_l");
        lowerarm_l = GameObject.Find("lowerarm_l"); 
    }

    // Update is called once per frame
    void Update () {
        lowerarm_l.transform.localRotation = Quaternion.Euler(Local_X, Local_Y, Local_Z);
    }
}

这里写图片描述
从图中可以看出结论,rotation of the transform relative to the parent transform's rotation的意思就是子物体的物体坐标系相对于平移之后父物体物体坐标系之间的旋转角度。读起来可以很绕,解释以下主要是两个点。首先,父物体的物体坐标系要平移,使得父子两个物体坐标系的坐标原点重合;然后,旋转rotation是父子两个物体坐标系之间的关系,也就是说,欧拉角(或者四元数)描述的是如何旋转父物体坐标系,会使得其与子物体坐标系重合。以上就是API手册中英文解释的全部意思。

当然,把父物体的坐标系作为参考系,localRotation这个属性也描述了子物体坐标系的旋转角度。所以,通过给这个属性赋值,可以达到控制刚体旋转的目的。

2.transform.Rotate()中的Space.Self和Space.World

transform.Rotate是正儿八经用来控制刚体旋转的方法,其参数除了欧拉角之外,还有一个可选参数Space.Self/Space.World,默认是前者。在API手册中对这两个的解释也不是特别清晰。下面我们来进行简单的梳理和理解。
下面两张图片载自大佬博客,可以通过看图直观理解Space.Self和Space.World的区别和联系。联系就是两者都是将自身的物体坐标系进行旋转。区别在于旋转的参考系不一样。前者(Space.Self)的参考系是旋转前一刹那时的自身物体坐标系;而后者(Space.World)是评议之后的世界坐标系。此处注意仍然是平移之后的世界坐标系(经过平移,与物体坐标系坐标原点重合的世界坐标系又称惯性坐标系),不懂可以参考博客坐标系详解

左图和右图都是绕Y轴进行旋转。左图是Space.Self参数控制下的情况,可以看到圆柱体是绕本地坐标系的Y轴进行旋转的。右图是Space.World参数控制下的情况,可以看出圆柱体是绕惯性坐标系(平移之后的世界坐标系)进行旋转的。
这里写图片描述这里写图片描述

(二)结论总述

1.在Unity 3D中控制刚体旋转的主要方法可以分为三个类别:①以父物体的物体坐标系为参考系进行旋转;②以世界坐标系为参考系进行旋转;③以自身物体坐标系为参考系进行旋转。当然还有绕点旋转和绕轴旋转,这些此处暂不讨论。

2.不论是以哪一个坐标系为参考系进行旋转,都需要在旋转之前把参照系平移,使得参照系与带旋转物体的物体坐标系坐标原点重合

3.旋转的欧拉角是两个坐标系之间的旋转角度,具体来说就是将参照系旋转到和物体坐标系重合需要的欧拉角。

2014-11-26 17:39:21 jingjingcrystal 阅读数 1645
unity教程unity3d教程;今天我们开始学习新的内容数组遍历器
先让我们来复习一下数据类型,为以后的代码书写来打基础。

数组

定义:数组是同意数据类型的一组值。

           数组属于引用类型,因此存储在堆内存中。

          数组元素初始化或给数组元素赋值都可以在声明数组时或在程序的后面阶段中进行。

           数组主要用于对同一类型的数据进行批量处理,它包含多个数据类型,这些对象叫做“数据元素”。

数组有很多种,接下来我们就来讲一维数组和二维数组

一维数组:定义:如果一个数组的所有元素都不是数组,那么该数组称为一维数组。

                 语法:数据类型    数组名[常量表达式]

                 其中,数据类型是一种基本数据类型或构造数据类型。

                           数组名是用户定义的数组标识符。方括号中的常量表达式表示数据元素的个数,也称数组的长度。

在代码书写中Console.ReadKey();表示让结果停留在屏幕上。

write和writeline的区别write输出数据不换行,writeline换行。

例:静态赋值:int [] a={1,2,3};

       动态赋值:int [] a=new int [] {1,2,3};(new可以作为动态赋值的标志)

注:数组下标a[0]是从0开始的

二维数组:与一维数组十分相似

               声明和初始化:数据类型[] [] 数组名  (数组元素没有内存空间,必须初始化才可以用)

                   

遍历器---foreach:

语法:foreach(数据类型   变量  in  集合或数组名称)

          {

                  语句块

          }

例:string [] e=new string[3]{"张三","李四",“王五”};

       foreach(string  f  in  e){

               Console.WriteLine(f);

           }

          Console.ReadKey(); 

其中string  b=Console.ReadLine();是输入一串字符。

更多精彩内容请关注:http://www.gopedu.com/

unity3d脚本编程

阅读数 19763

没有更多推荐了,返回首页