在我开始之前,如果这不是高度专业的写法,我想说声抱歉,我一直在努力解决这个问题好几个小时,试图在没有运气的情况下解决这个问题,我很累而且压力很大。
我试图让一个移动的物体在特定点通过 1 个门户进入,并在它进入的同一点从另一个门户出来。所以如果球进入传送门的顶部,物体会从传送门的顶部出来,如果物体从传送门的底部进入,它就会离开另一个传送门的底部。我不是最好的插图,但这是我想要做的:
在这里,您可以在两个图像中看到对象进入蓝色门户并在它进入的点退出橙色,因此从上到下,从下到下。
我实际上已经让它正常工作了,但现在我需要再做一次,但这一次,门户之一需要是水平的而不是垂直的:
所以我所做的是,当两个都是垂直时,我不选中一个名为“exitIsHorizontal”的 bool 值(假),当其中一个在水平面上的天花板上时,它将垂直轴转换为水平轴一。
我什至让它起作用了,但是它有一个需要修复的可复制夸克。当对象进入门户底部时,它会像您期望的那样正常工作,就像上图一样。但是,当您击中门户的顶部时,该对象会像您预期的那样从门户的另一侧出来,但该对象开始向相反的方向移动,如下图所示:
正确的导出位置,由于某种原因错误的导出方向。我还需要这个函数是动态的,这样如果说,对象从另一个方向击中蓝色门户,退出方向也会像这样切换边:
这是我的脚本:
public GameObject otherPortal;
public PortalController otherPortalScript;
private BallController ballController;
public bool exitIsHorizontal = false;
List<PortalController> inUseControllers = new List<PortalController>();
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.tag == "Ball")
{
ballController = other.GetComponent<BallController>();
if (inUseControllers.Count == 0)
{
inUseControllers.Add(otherPortalScript);
var offset = other.transform.position - transform.position;
if(exitIsHorizontal)
{
offset.x = offset.y;
offset.y = 0;
}
else
{
offset.x = 0;
}
other.transform.position = otherPortal.transform.position + offset;
}
}
}
void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.tag == "Ball")
{
inUseControllers.Clear();
}
}
此脚本附加到两个门户,以便两个门户都可以处理进入和退出。并且您没有看到在脚本中使用任何内容声明的任何变量使其本质上为空(例如“otherPotal”),我在编辑器中声明。
我敢打赌这是我一直想念的非常简单的东西,我只是不知道有些东西是。
最佳答案
所以门户基本上是一个虫洞。进入的对象将保留其 本地 位置和方向。
职能:
Unity 具有从世界空间转换到本地空间的功能,反之亦然。
位置:
Transform.InverseTransformPoint
Transforms position from world space to local space.
Transform.TransformPoint
Transforms position from local space to world space.
方向:
对于转换方向,您必须使用:
Transform.InverseTransformDirection
Transforms a direction from world space to local space. The opposite of Transform.TransformDirection.
Transform.TransformDirection
Transforms direction from local space to world space.
简单示例:
您附加到两个门户的一个脚本。它移动带有标签 的对象“球”转至
exitPortal
.public class Portal : MonoBehaviour
{
[SerializeField] Portal exitPortal;
void OnTriggerEnter2D(Collider2D collider)
{
if (collider.CompareTag("Ball"))
{
GameObject ball = collider.gameObject;
Rigidbody2D rigidbody = ball.GetComponent<Rigidbody2D>();
Vector3 inPosition = this.transform.InverseTransformPoint(ball.transform.position);
inPosition.x = -inPosition.x;
Vector3 outPosition = exitPortal.transform.TransformPoint(inPosition);
Vector3 inDirection = this.transform.InverseTransformDirection(rigidbody.velocity);
Vector3 outDirection = exitPortal.transform.TransformDirection(inDirection);
ball.transform.position = outPosition;
rigidbody.velocity = -outDirection;
}
}
}
你得到这个:
复杂示例:
您需要 3 个脚本才能工作:
鬼魂的样子是这样的:
您将需要两个额外的层 - Portal 和 Ghost,碰撞矩阵设置为图像中。
脚本:
我在代码中添加了足够多的注释,让您了解它在做什么。
传送门:
public class Portal : MonoBehaviour
{
[SerializeField] Portal exitPortal;
void OnTriggerEnter2D(Collider2D collider)
{
// When a warpable enters a portal create a ghost
if (collider.TryGetComponent(out Warpable warpable))
{
// Create a ghost only if we haven't already
if (warpable.Ghost == null) warpable.CreateGhost(this, exitPortal);
}
}
void OnTriggerExit2D(Collider2D collider)
{
// When a warpable exist a portal; check if it has a ghost
if (collider.TryGetComponent(out Warpable warpable))
{
// Teleport to the ghost; apply its position, rotation, velocity
if (warpable.Ghost != null)
{
// Create vectors to compare dot product
Vector3 portalToWarpable = warpable.transform.position - this.transform.position;
Vector3 portalDownwards = -this.transform.up;
// If warpable is on the other side of the portal you get a value that's more than zero
float dot = Vector3.Dot(portalDownwards, portalToWarpable);
bool passedThroughPortal = dot >= 0f;
// If we passed through the portal then teleport to the ghost; otherwise just continue
if (passedThroughPortal)
{
warpable.Position = warpable.Ghost.warpable.Position;
warpable.Rotation = warpable.Ghost.warpable.Rotation;
warpable.Velocity = warpable.Ghost.warpable.Velocity;
}
// Destroy the ghost
warpable.DestroyGhost();
}
}
}
void OnDrawGizmos()
{
Gizmos.color = Color.magenta;
Gizmos.DrawRay(this.transform.position, this.transform.up);
}
}
可变形:
public class Warpable : MonoBehaviour
{
[SerializeField] new Rigidbody2D rigidbody;
public Ghost Ghost { get; private set; }
public void CreateGhost(Portal inPortal, Portal outPortal)
{
// Move the ghost object to the Ghost layer, this is so that ghost can collide with real objects, other ghosts, but not with the portal.
// Ghost/Ghost = TRUE
// Ghost/Default = TRUE
// Ghost/Portal = FALSE
GameObject original = this.gameObject;
GameObject duplicate = GameObject.Instantiate(original);
duplicate.layer = LayerMask.NameToLayer("Ghost");
Physics2D.IgnoreCollision(
original.GetComponent<Collider2D>(),
duplicate.GetComponent<Collider2D>()
);
// Add the ghost component
Ghost = duplicate.AddComponent<Ghost>();
Ghost.observing = original.GetComponent<Warpable>();
Ghost.warpable = duplicate.GetComponent<Warpable>();
Ghost.inPortal = inPortal;
Ghost.outPortal = outPortal;
}
public void DestroyGhost()
{
GameObject.Destroy(Ghost.gameObject);
Ghost = null;
}
public Vector3 Position
{
get { return transform.position; }
set { transform.position = value; }
}
public Quaternion Rotation
{
get { return transform.rotation; }
set { transform.rotation = value; }
}
public Vector3 Velocity
{
get { return rigidbody.velocity; }
set { rigidbody.velocity = value; }
}
}
幽灵:
public class Ghost : MonoBehaviour
{
public Warpable observing;
public Warpable warpable;
public Portal inPortal;
public Portal outPortal;
void FixedUpdate()
{
warpable.Position = OutPosition(observing.Position);
warpable.Rotation = OutRotation(observing.Rotation);
warpable.Velocity = OutDirection(observing.Velocity);
}
Vector3 OutPosition(Vector3 position)
{
Vector3 inPosition = -inPortal.transform.InverseTransformPoint(position);
return outPortal.transform.TransformPoint(inPosition);
}
Quaternion OutRotation(Quaternion rotation)
{
return Quaternion.Inverse(inPortal.transform.rotation) * outPortal.transform.rotation * rotation;
}
Vector3 OutDirection(Vector3 velocity)
{
Vector3 inDirection = -inPortal.transform.InverseTransformDirection(velocity);
return outPortal.transform.TransformDirection(inDirection);
}
void OnDrawGizmos()
{
Gizmos.color = Color.cyan;
Gizmos.DrawWireSphere(warpable.Position, 1f);
Gizmos.DrawLine(warpable.Position, warpable.Position + warpable.Velocity);
}
}
最后的结果是这样的:
关于c# - 对象退出门户在 Unity 2D 中移动错误的方向?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60793670/