2DDL的一个坑

Posted by WannaTwo on March 8, 2018

前言

IndieGameCamp2017 中,我的小组使用 2ddl 这一插件制作了《面向火焰》。用于实现篝火的光影渲染以及进出光线的检测。

问题

2ddl有一个默认的设定区域,大约是Rect(-10, -10, 20, 20)。所有发生在这个区域外的光线进出检测,都不能生效。

当时的解决方案

更改整个场景的位置及大小,保证所有的进出检测都发生在这个区域内。(好low

分析

我这里使用的光线检测是事件InsideFieldOfViewEvent,转到定义后,不难找出,其第一个参数(光线范围内的所有物体数组)传入的是类DynamicLight的成员变量objReached

那么,objReached是在什么时候被赋值的呢?

查找引用后,发现一共有两处:

1. 调用内部方法GetVerts(Line:235),将objReached作为ref参数传入,修改其内存。

2. 在私有方法GenerateColliderVerts内(Line:1592),通过方法addingObjectToObjectReachedList,插入元素。

这里我们发现一个问题:

两处中总计三种插入元素的情况,其中两种都会检测是否存在父物体,如果存在,则将父物体插入数组objReached

if(360 != Mathf.RoundToInt(RangeAngle)){ 
	if (Vector3.Angle(transform.InverseTransformPoint(hitp), Vector3.up) < RangeAngle*.5f) {	// Light angle restriction
		if(_ray.collider.gameObject.transform.parent){
			addingObjectToObjectReachedList(objReached, _ray.collider.gameObject.transform.parent.gameObject);
		}else{
			addingObjectToObjectReachedList(objReached, _ray.collider.gameObject);
		}
	}
}else{
	if(_ray.collider.gameObject.transform.parent){
		addingObjectToObjectReachedList(objReached, _ray.collider.gameObject.transform.parent.gameObject);
	}else{
		addingObjectToObjectReachedList(objReached, _ray.collider.gameObject);
	}
}
if(360 != Mathf.RoundToInt(RangeLightAngle)){ 
		if (Vector3.Angle(lightObjectTM.InverseTransformPoint(pos), Vector3.up) < RangeLightAngle*.5f) {	// Light angle restriction
				if(_ray.collider.gameObject.transform.parent){
					if(tmpGOWithinFOV == null) tmpGOWithinFOV = _ray.collider.gameObject.transform.parent.gameObject;
				}else{
					if(tmpGOWithinFOV == null) tmpGOWithinFOV = _ray.collider.gameObject;
				}
		}
}else{
		if(_ray.collider.gameObject.transform.parent){
			if(tmpGOWithinFOV == null) tmpGOWithinFOV = _ray.collider.gameObject.transform.parent.gameObject;
		}else{
			if(tmpGOWithinFOV == null) tmpGOWithinFOV = _ray.collider.gameObject;
		}
}

这里tmpGOWithinFOV 是准备插入objReached的临时变量。

而剩下一种情况是:

由于Unity的RayCastlow accurate,部分物体没有发生射线碰撞。插件中对于这种情况做了修正DynamicLight::GetVerts() line:316)

if(notifyReachedEventUsed && this.collider.type == CasterCollider.CasterType.PolygonCollider2d || this.collider.type == CasterCollider.CasterType.BoxCollider2d)
{

	//foreach(Vector2 objPoint in this.collider.points)
	for(int k = 0; k < this.collider.points.Length; k++)
	{
		if(360 != Mathf.RoundToInt(RangeLightAngle)){ 
			if (Vector3.Angle(lightObjectTM.InverseTransformPoint(pos), Vector3.up) < RangeLightAngle*.5f) {	// Light angle restriction
				if(((Vector2)pos - this.collider.points[k]).sqrMagnitude < 500f)
				{	tmpGOWithinFOV = this.collider.transform.gameObject;
					//Debug.Log("FOUND !!  " + this.collider.transform.gameObject.name);
					break;
				}
			}
		}else
		{
			if(((Vector2)pos - this.collider.points[k]).sqrMagnitude < 500f)
			{	tmpGOWithinFOV = this.collider.transform.gameObject;
				//Debug.Log("FOUND !!  " + this.collider.transform.gameObject.name);
				break;
			}
		}


	}

}

这里tmpGOWithinFOV 是准备插入objReached的临时变量。

总结

objReached记录的是无父级时碰撞体本体,有父级时碰撞体父级

相对应的,事件OnEnterFieldOfViewOnExitFieldOfView以及InsideFieldOfViewEvent中的参数也是如此。

所以,如果想检测“某特定碰撞体是否触发了事件”,需要根据该碰撞体有没有父级,而分别处理。

正确的解决方案

由于我需要检测的碰撞体是有父级的,所以在检测是否进出光线的时候,需要将判定改为父级与参数的判定

具体如下:

原错误代码

if (one.GetInstanceID() == gameObject.GetInstanceID())
{
	...
}

修改后代码

if (one.GetInstanceID() == transform.parent.gameObject.GetInstanceID())
{
	...
}

one为方法InsideFieldOfViewDelegate(GameObject[] go, DynamicLight Light)中第一个参数的单个元素的临时变量。

延伸

问题一:为什么修改前,在猜测的Rect(-10, -10, 20, 20)范围内,可以正常检测?

问题二:为什么该插件在记录“光线内物体”时,需要考虑其有没有父级?

相关链接