快速移动物体2D游戏(Unity3d)

时间:2015-04-10 12:58:51

标签: performance unity3d collision-detection rigid-bodies

我找到了正式的团结培训https://www.youtube.com/watch?v=D5MqLcO6A8g 并找到bug。(看分数)

我花了大约2天时间修复它并失败了。 我找到了" DontGoThroughThings"脚本并尝试重写以在2D中使用。再次失败)

请帮助我!

这是重写脚本:

public LayerMask layerMask; //make sure we aren't in this layer 
public float skinWidth; //probably doesn't need to be changed 

private float minimumExtent; 
private float partialExtent; 
private float sqrMinimumExtent; 
private Vector2 previousPosition; 
private Rigidbody2D myRigidbody; 


//initialize values 
void Awake() 
{ 
    myRigidbody = GetComponent<Rigidbody2D>(); 
    previousPosition = myRigidbody.position;
    minimumExtent = Mathf.Min(Mathf.Min(GetComponent<Collider2D>().bounds.extents.x, GetComponent<Collider2D>().bounds.extents.y)); 
    partialExtent = minimumExtent * (1.0f - skinWidth); 
    sqrMinimumExtent = minimumExtent * minimumExtent; 
} 

void FixedUpdate() 
{ 
    //have we moved more than our minimum extent? 
    Vector2 movementThisStep = myRigidbody.position - previousPosition; 
    float movementSqrMagnitude = movementThisStep.sqrMagnitude;

    if (movementSqrMagnitude > sqrMinimumExtent) 
    { 
        float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
        //RaycastHit2D hitInfo; 

        //check for obstructions we might have missed 
        if (Physics2D.Raycast(previousPosition, movementThisStep, movementMagnitude, 0, layerMask.value)) 
            myRigidbody.position = (movementThisStep/movementMagnitude)*partialExtent;
        Debug.DrawLine(myRigidbody.position, myRigidbody.position - previousPosition, Color.green);
    } 

    previousPosition = myRigidbody.position; 
}

这是unitypackage https://www.dropbox.com/s/a3n1dalbc1k0k42/Hat%20Trick.unitypackage?dl=0

P.S。抱歉我的英文,谢谢你的帮助!!

2 个答案:

答案 0 :(得分:1)

<强>解释

Unity中的连续碰撞检测不使用光线投射。结果,非常快速移动(和/或相对较小)的对象(现在让我们调用那种对象projectile)仍然可以通过事物而不会检测到碰撞。着名的DontGoThroughThings组件修复了3D。有一些不一致,但如果你知道自己在做什么,就可以完成任务。

这是我对它的二维适应。

我添加了一些功能,使每个不擅长编码或游戏物理的人都更友好。

如何使用

  1. 将此组件添加到快速移动的对象中,并且在击中某些内容时始终会触发OnTriggerEnter2D事件。
  2. 您也可以选择发送不同的自定义消息(通过更改MessageName变量)。我实际上建议,由于下面解释的警告。
  3. 发送消息是脚本的主要用例。它将神奇地使射弹在物理意义上表现正确。
  4. triggerTarget变量确定它是否将消息发送给自身(如果你有一个附加到射弹的命中处理脚本),则命令被击中的对象(如果你有附加的命中处理)应该被射弹击中的物体,或两者的任何变化。
  5. 与原始版本不同,此脚本还允许在影响时应用强制,可以通过momentumTransferFraction变量进行调整。当两个物体碰撞时,产生的力是两个物体之间的动量传递(质量乘以速度)的结果。我这样做的方式非常简陋,并且缺少很多促成因素,但这足以使射弹对物体产生影响。就像在现实世界中一样,你的弹丸越快或越重,施加的力就越大。
  6. 还有一些警告(大多数也适用于原始版本)

    1. 仅适用于非常快速移动的物体。你使用它越少越好,因为它比正常的碰撞检测计算成本更高。
    2. 虽然碰撞检测更准确,但碰撞分辨率只是非常简陋。它没有物理引擎默认的那样好。
    3. 在当前版本中,事后总是会检测到碰撞。这就是为什么你可能会看到在碰撞登记时射弹穿过物体的原因。我想在不久的将来解决这个问题。
    4. 如果你在第一次击中基本停止工作的子弹或其他形式的射弹等物体上使用它,你可以将momentumTransferFraction设置为1让子弹物理推动物体(通过将其所有动量施加到物体上)第一个命中对象)没有子弹受到影响。
    5. 由于某种原因,您无法仅为一个对象禁用默认碰撞检测。这意味着,如果您是如此(非)幸运并且碰巧碰巧通过Unity的默认碰撞检查进行了注册,则可能会在同一对象上多次OnTriggerEnter2D触发,或者(如果碰撞是不是触发器)对命中目标施加一个力(除了这个脚本所施加的力)。但是,由于这在某种程度上是随机的并且非常不一致,除了在您的射弹对撞机上打开IsTrigger之外,我建议使用自定义消息名称来处理射弹撞击。这样,通过默认碰撞检测随机检测到的碰撞不会产生任何意外的副作用[请记住,对于这些类型的对象,默认碰撞检测不一致是添加此脚本的实际原因]。仅供参考:从Unity 5开始,防止默认碰撞检测的唯一两种方法是IgnoreCollisionIgnoreLayerCollision
    6. <强>代码

      using UnityEngine;
      using System.Collections;
      using System.Linq;
      
      
      /// <summary>
      /// 2D adaption of the famous DontGoThroughThings component (http://wiki.unity3d.com/index.php?title=DontGoThroughThings).
      /// Uses raycasting to trigger OnTriggerEnter2D events when hitting something.
      /// </summary>
      /// <see cref="http://stackoverflow.com/a/29564394/2228771"/>
      public class ProjectileCollisionTrigger2D : MonoBehaviour {
          public enum TriggerTarget {
              None = 0,
              Self = 1,
              Other = 2,
              Both = 3
          }
      
          /// <summary>
          /// The layers that can be hit by this object.
          /// Defaults to "Everything" (-1).
          /// </summary>
          public LayerMask hitLayers = -1;
      
          /// <summary>
          /// The name of the message to be sent on hit.
          /// You generally want to change this, especially if you want to let the projectile apply a force (`momentumTransferFraction` greater 0).
          /// If you do not change this, the physics engine (when it happens to pick up the collision) 
          /// will send an extra message, prior to this component being able to. This might cause errors or unexpected behavior.
          /// </summary>
          public string MessageName = "OnTriggerEnter2D";
      
          /// <summary>
          /// Where to send the hit event message to.
          /// </summary>
          public TriggerTarget triggerTarget = TriggerTarget.Both;
      
          /// <summary>
          /// How much of momentum is transfered upon impact.
          /// If set to 0, no force is applied.
          /// If set to 1, the entire momentum of this object is transfered upon the first collider and this object stops dead.
          /// If set to anything in between, this object will lose some velocity and transfer the corresponding momentum onto every collided object.
          /// </summary>
          public float momentumTransferFraction = 0;
      
          private float minimumExtent;
          private float sqrMinimumExtent;
          private Vector2 previousPosition;
          private Rigidbody2D myRigidbody;
          private Collider2D myCollider;
      
      
          //initialize values 
          void Awake()
          {
              myRigidbody = GetComponent<Rigidbody2D>();
              myCollider = GetComponents<Collider2D> ().FirstOrDefault();
              if (myCollider == null || myRigidbody == null) {
                  Debug.LogError("ProjectileCollisionTrigger2D is missing Collider2D or Rigidbody2D component", this);
                  enabled = false;
                  return;
              }
      
              previousPosition = myRigidbody.transform.position;
              minimumExtent = Mathf.Min(myCollider.bounds.extents.x, myCollider.bounds.extents.y);
              sqrMinimumExtent = minimumExtent * minimumExtent;
          }
      
          void FixedUpdate()
          {
              //have we moved more than our minimum extent? 
              var origPosition = transform.position;
              Vector2 movementThisStep = (Vector2)transform.position - previousPosition;
              float movementSqrMagnitude = movementThisStep.sqrMagnitude;
      
              if (movementSqrMagnitude > sqrMinimumExtent) {
                  float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);
      
                  //check for obstructions we might have missed 
                  RaycastHit2D[] hitsInfo = Physics2D.RaycastAll(previousPosition, movementThisStep, movementMagnitude, hitLayers.value);
      
                  //Going backward because we want to look at the first collisions first. Because we want to destroy the once that are closer to previous position
                  for (int i = 0; i < hitsInfo.Length; ++i) {
                      var hitInfo = hitsInfo[i];
                      if (hitInfo && hitInfo.collider != myCollider) {
                          // apply force
                          if (hitInfo.rigidbody && momentumTransferFraction != 0) {
                              // When using impulse mode, the force argument is actually the amount of instantaneous momentum transfered.
                              // Quick physics refresher: F = dp / dt = m * dv / dt
                              // Note: dt is the amount of time traveled (which is the time of the current frame and is taken care of internally, when using impulse mode)
                              // For more info, go here: http://forum.unity3d.com/threads/rigidbody2d-forcemode-impulse.213397/
                              var dv = myRigidbody.velocity;
                              var m = myRigidbody.mass;
                              var dp = dv * m;
                              var impulse = momentumTransferFraction * dp;
                              hitInfo.rigidbody.AddForceAtPosition(impulse, hitInfo.point, ForceMode2D.Impulse);
      
                              if (momentumTransferFraction < 1) {
                                  // also apply force to self (in opposite direction)
                                  var impulse2 = (1-momentumTransferFraction) * dp;
                                  hitInfo.rigidbody.AddForceAtPosition(-impulse2, hitInfo.point, ForceMode2D.Impulse);
                              }
                          }
      
                          // move this object to point of collision
                          transform.position = hitInfo.point;
      
                          // send hit messages
                          if (((int)triggerTarget & (int)TriggerTarget.Other) != 0 && hitInfo.collider.isTrigger) {
                              hitInfo.collider.SendMessage(MessageName, myCollider, SendMessageOptions.DontRequireReceiver);
                          }
                          if (((int)triggerTarget & (int)TriggerTarget.Self) != 0) {
                              SendMessage(MessageName, hitInfo.collider, SendMessageOptions.DontRequireReceiver);
                          }
                      }
                  }
              }
      
              previousPosition = transform.position = origPosition;
          }
      }
      

答案 1 :(得分:0)

以下是我重写此脚本的2D版本(适用于Unity 4.6):

using UnityEngine;
using System.Collections;

public class DontGoThroughThings : MonoBehaviour
{
    public delegate void CollidedDelegate(Collider2D collider);
    public event CollidedDelegate Collided;

    public LayerMask layerMask; //make sure we aren't in this layer 
    public float skinWidth = 0.1f; //probably doesn't need to be changed 

    private float minimumExtent;
    private float partialExtent;
    private float sqrMinimumExtent;
    private Vector2 previousPosition;
    private Rigidbody2D myRigidbody;



    //initialize values 
    void Awake()
    {
        myRigidbody = rigidbody2D;
        previousPosition = myRigidbody.transform.position;
        minimumExtent = Mathf.Min(BoundsOf(collider2D).extents.x, BoundsOf(collider2D).extents.y);
        partialExtent = minimumExtent * (1.0f - skinWidth);
        sqrMinimumExtent = minimumExtent * minimumExtent;
    }

    void FixedUpdate()
    {
        //have we moved more than our minimum extent? 
        Vector2 movementThisStep = (Vector2)myRigidbody.transform.position - previousPosition;
        float movementSqrMagnitude = movementThisStep.sqrMagnitude;

        if (movementSqrMagnitude > sqrMinimumExtent)
        {
            float movementMagnitude = Mathf.Sqrt(movementSqrMagnitude);

            //check for obstructions we might have missed 
            RaycastHit2D[] hitsInfo = Physics2D.RaycastAll(previousPosition, movementThisStep, movementMagnitude, layerMask.value);

            //Going backward because we want to look at the first collisions first. Because we want to destroy the once that are closer to previous position
            for (int i = hitsInfo.Length-1; i >= 0; i--)
            {
                var hitInfo = hitsInfo[i];
                if (hitInfo && hitInfo.rigidbody != rigidbody2D)
                {
                    if (Collided != null)
                    {
                        Collided(hitInfo.collider);
                    }
                }
            }
        }

        previousPosition = myRigidbody.transform.position;
    }

    // compute bounds in local space
    public static Bounds BoundsOf(Collider2D collider) {
        var bounds = new Bounds();

        var bc = collider as BoxCollider2D;
        if (bc) {
            var ext = bc.size * 0.5f;
            bounds.Encapsulate(new Vector3(-ext.x, -ext.y, 0f));
            bounds.Encapsulate(new Vector3(ext.x, ext.y, 0f));
            return bounds;
        }

        var cc = collider as CircleCollider2D;
        if (cc) {
            var r = cc.radius;
            bounds.Encapsulate(new Vector3(-r, -r, 0f));
            bounds.Encapsulate(new Vector3(r, r, 0f));
            return bounds;
        }


        // others :P
        //Debug.LogWarning("Unknown type "+bounds);

        return bounds;
    }

    // return bounds in world space
    public static Bounds BoundsColliders(GameObject obj) {
        var bounds = new Bounds(obj.transform.position, Vector3.zero);

        var colliders = obj.GetComponentsInChildren<Collider2D>();
        foreach(var c in colliders) {
            var blocal = BoundsOf(c);
            var t = c.transform;
            var max = t.TransformPoint(blocal.max);
            bounds.Encapsulate(max);
            var min = t.TransformPoint(blocal.min);
            bounds.Encapsulate(min);
        }

        return bounds;
    }


}

如果它适合您,请告诉我。

谢谢, 利胆