Unity 子弹做法总结


纯粹 "子弹" 的话. Unity自带的例子 AngryBots 中给出了一种做法.发射时利用射线检测射击到的目标点.直接给予伤害及特效音效表现.而子弹只是作为一道快速的直线飞过.并没有什么判断逻辑.代码如下( JS版 ):

发射 : 

			// Spawn visual bullet
			var coneRandomRotation = Quaternion.Euler (Random.Range (-coneAngle, coneAngle), Random.Range (-coneAngle, coneAngle), 0);
			var go : GameObject = Spawner.Spawn (bulletPrefab, spawnPoint.position, spawnPoint.rotation * coneRandomRotation) as GameObject;
			var bullet : SimpleBullet = go.GetComponent.<SimpleBullet> ();

			lastFireTime = Time.time;

			// Find the object hit by the raycast
			var hitInfo : RaycastHit = raycast.GetHitInfo ();
			if (hitInfo.transform) {
				// Get the health component of the target if any
				var targetHealth : Health = hitInfo.transform.GetComponent.<Health> ();
				if (targetHealth) {
					// Apply damage
					targetHealth.OnDamage (damagePerSecond / frequency, -spawnPoint.forward);
				}

				// Get the rigidbody if any
				if (hitInfo.rigidbody) {
					// Apply force to the target object at the position of the hit point
					var force : Vector3 = transform.forward * (forcePerSecond / frequency);
					hitInfo.rigidbody.AddForceAtPosition (force, hitInfo.point, ForceMode.Impulse);
				}

				// Ricochet sound
				var sound : AudioClip = MaterialImpactManager.GetBulletHitSound (hitInfo.collider.sharedMaterial);
				AudioSource.PlayClipAtPoint (sound, hitInfo.point, hitSoundVolume);

				bullet.dist = hitInfo.distance;
			}
			else {
				bullet.dist = 1000;
			}

子弹 : 

function Update () {
	tr.position += tr.forward * speed * Time.deltaTime;
	dist -= speed * Time.deltaTime;
	if (Time.time > spawnTime + lifeTime || dist < 0) {
		Spawner.Destroy (gameObject);
	}
}


这样虽然逻辑上看是子弹表现和伤害表现有先后.但只要子弹速度够快.是感受不出来的.

这里的 够快 指的速度至少是1秒内子弹能飞到目标点.然后伤害表现上稍微做个缓冲效果.就没有问题.


但有时我们想要的"子弹"表现不仅仅是这样快速飞过去的.还可能是稍慢速的炮弹.曲线轨迹的导弹.穿透型的导弹.这样一来预先算好爆炸点的方式就不适用了.只能加Collider做实时碰撞.


在各种尝试之后发现,想使用Unity的RigidBody来控制子弹运动.是不合适的.有以下问题.

1.Unity的物理运动是放在FixUpdate中计算的.与渲染的帧数不同步.会导致物体运动看起来一卡一卡.可以通过设置 Rigidbody.interpolation 为 RigidbodyInterpolation.Interpolate来解决.但发现碰撞检测有延迟.即已经穿透一段距离后才发出碰撞消息.

2.可能穿透物体.

3.碰到后会被撞开.无法做穿透效果.

单个问题来看.

2可以通过设置 Rigidbody.detectCollisions = Continuous来解决

3可以通过设置Rigidbody为IsKinematic为ture.自己控制transform来解决

但一旦设置了IsKinematic.则1和2的设置等于无效.不过因为是自己控制.放在Update中改变位置.则1的问题不复存在.主要是2.


假设认为慢速子弹不存在穿透问题.那么将快速子弹使用射线的方式.慢速子弹使用Collider的方式.似乎就解决了问题.但如果是一个曲线轨迹的快速子弹.似乎两种都不合适.

难道每帧检测飞行方向上的射线?1可能有效率问题.2在曲线拐弯处的路径补偿计算会比较复杂.


综上所述.最后尝试使用了一种动态调整Collider大小的方式来解决以上问题.即根据当前的deltaTime和自身速度来动态调整Collider大小.将自身大小调整到与一帧移动距离相同.则可以避免穿透问题

	void ReCalculeteSize( bool bFront )
	{
		BoxCollider bc = gameObject.GetComponent<BoxCollider>();
		if( bc == null )
			return;
		float fNewSize = m_BulletType.fSpeed * Time.deltaTime;
		fNewSize -= 0.4f;
		if( fNewSize < m_BulletType.fSize )
			fNewSize = m_BulletType.fSize;

		bc.size = new Vector3( m_BulletType.fSize, 5f, fNewSize * transform.localScale.z );

		float fNewCenterZ = 0f;
		if( bFront )
		{
			fNewCenterZ = -fNewSize / 2 + m_BulletType.fSize;
			if( fNewCenterZ > 0 )
				fNewCenterZ = 0f;
		}
		else
		{
			fNewCenterZ = fNewSize / 2 - m_BulletType.fSize;
			if( fNewCenterZ < 0 )
				fNewCenterZ = 0f;
		}

		bc.center = new Vector3( 0f, 0f, fNewCenterZ);
	}


最后.如果想自己控制position.要设置 IsKinematic 为 true. interpolation 一定要设为None


你可能感兴趣的:(unity)