我找到了官方的统一训练https://www.youtube.com/watch?v=D5MqLcO6A8g
并找到错误。(看分数)
我花了大约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
附言对不起,我的英语,谢谢您的帮助!
最佳答案
说明
Unity中的连续碰撞检测不使用光线投射。结果,非常快速移动(和/或相对较小)的对象(现在将其称为projectile
这种对象)仍然可以穿过物体,而不会检测到碰撞。著名的DontGoThroughThings组件可修复3D问题。有一些不一致之处,但是如果您知道自己在做什么,就可以完成工作。
这是我对其的2D改编。
我添加了一些功能,以使其对每个不太擅长编码或游戏物理的人都更加友好。
如何使用它
将此组件添加到快速移动的对象中,当它们碰到某些东西时,它们将始终触发OnTriggerEnter2D
事件。
您也可以选择发送其他自定义消息(通过更改MessageName
变量)。实际上,由于以下说明,我建议这样做。
发送消息是脚本的主要用例。它不会神奇地使弹丸在物理意义上正确表现。triggerTarget
变量确定是否将消息发送给自身(如果您将打击处理脚本附加到了弹丸上),发送给被打击的对象(如果您将打击处理附加到了应该被打击的对象上)被弹丸击中),或两者的任何变体。
与原始版本不同,此脚本还允许在可以通过momentumTransferFraction
变量进行调整的冲击力上施加力。当两个物体碰撞时,产生的力是两个物体之间动量(质量乘以速度)传递的结果。我这样做的方法很初级,缺少很多促成因素,但是足以让弹丸在撞击时推动物体。就像在现实世界中一样,弹丸越快或越重,施加的力就越大。
还有一些警告(大多数也适用于原始版本)
仅适用于快速移动的物体。使用的次数越少越好,因为它比正常的碰撞检测要昂贵得多。
虽然碰撞检测更准确,但是碰撞分辨率只是非常基本的。它不如物理引擎默认执行的操作好。
在当前版本中,事后总是会发现冲突。这就是为什么您可能会在碰撞记录时看到弹丸穿过物体的原因。我想在不久的将来解决此问题。
如果您在诸如子弹或其他形式的弹丸之类的物体上使用此功能,而这些物体在第一次命中后基本上停止工作,则可以将momentumTransferFraction
设置为1,以使子弹以物理方式推动该对象(通过将其所有动量应用于第一个命中的对象)而无需子弹本身会受到影响。
由于某些原因,您不能仅对一个对象禁用默认的碰撞检测。这意味着,如果您如此(不幸),并且碰巧通过Unity的默认冲突检查记录了碰撞,则可能对同一对象多次触发OnTriggerEnter2D
,或者(如果碰撞器不是触发器)施加对命中目标施加强制作用(除了此脚本施加的目标外)。但是,由于这是随机的并且非常不一致,因此除了在弹丸的对撞机上打开IsTrigger
之外,我建议使用自定义消息名称来处理弹丸的影响。这样,默认冲突检测随机检测到的冲突不会有任何意外的副作用[请记住,默认冲突检测对于此类对象不一致是您添加此脚本的实际原因]。仅供参考:从Unity 5开始,防止默认冲突检测的仅有两种方法是IgnoreCollision和IgnoreLayerCollision。
码
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;
}
}
关于performance - 快速移动物体2D游戏(Unity3d),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29562108/