Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Object exits portal moving the wrong direction in Unity 2D?

Tags:

c#

unity3d

Before I start I'd like to say sorry if this isn't highly professionally written, I've been working on this for hours banging my head against the wall trying to figure this out with no luck, I'm tired and stressed.

I'm trying to get a moving object to enter through 1 portal at a specific point, and come out the other portal at the same point it entered. So if the ball enters the top of the portal, the object will come out at the top of the exit portal, and the if the object enters from the bottom of the portal it will exit the bottom of the other portal. I'm not the best a illustration, but here's what I want to it do: enter image description here Here you can see in both images the object enters the blue portal and exits the orange at the point that it entered, so top to top, bottom to bottom.

I've actually gotten this to work fine, but now I need to do it again but this time, one of the portals needs to be horizontal instead of vertical: enter image description here So what I've done, is make it so when both a vertical, I leave a bool called "exitIsHorizontal" unchecked (false), and when one of them is on the ceiling on the level, it translates the vertical axis to a horizontal one.

I even sort of got that to work, however it's got a reproducible quark that needs to be fixed. When the object enters the bottom of the portal, it works fine like you'd expect and just like the image above. But when you hit the top of the portal, the object comes out the other side of the portal like you'd expect, but the object begins moving in the opposite direction as see in this image: enter image description here Correct exit location, wrong exit direction for some reason. I also need this function to be dynamic so that if say, the object hit the blue portal from the other direction that the exit direction would switch sides as well like this: enter image description here

Here is my script:

    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();
        }

    }

This script is attached to both portals so that both Portals can handle both entering and exiting. And any variable you don't see declared with anything in the script making it essentially null (such as "otherPotal") I declare the in editor.

I'll bet it's something extremely simple that I just keep missing, I just don't know that something is.

like image 256
Jhon Piper Avatar asked Mar 21 '20 21:03

Jhon Piper


People also ask

What is transform forward in 2D?

transform. forward is the Vector pointing in the z Direction of the object. In 3D space, this is usually in front of you, but in 2D this points "into" the screen. You need to replace it with either transform.

How to move 2D object in Unity using game components?

This time we will learn to move 2D Object in Unity by scripting in C# using Game Components. To move our Player, we will need to add some logic to its dumb Game Object. We will do it by creating a Custom Component containing C# code and adding it to the Player Game Object.

How do I move a game object to the right?

This code is creating a Vector3 pointing to the right (1, 0, 0) and multiplying it by time elapsed since the last frame, and then calls Transform.Translate method with the resulting Vector3 which will move the Game Object that distance. TL;DR; This will make the player move 1 Scene unit per second to the right.

How to add multiple enemies to a game object in Unity?

When Enemy.cs file is created, Unity reloads for a sort time and will register a new component to be added to Game Object named Enemy. Go ahead and add it to both Enemy Game Objects. Note: Remember to add the Enemy component to both enemies. To make the Player Game Object move, edit Player.cs file.

How can I tell which way an object is moving?

If you are using transform.position to move, just debug.log the object's transform.position.x or .y (or Z if necessary) to find which way it moves. Do you have the object moving currently? If so, is it using physics or translation? If physics, debug.log the x and y velocity to see where "forward" is in your game.


1 Answers

So a portal is basically a wormhole. An object that enters will retain its local position and direction.

wormhole

Functions:

Unity has functions for transforming from world space to local space and vice versa.

Position:

Transform.InverseTransformPoint

Transforms position from world space to local space.

Transform.TransformPoint

Transforms position from local space to world space.

jumping

Direction:

For transforming directions you will have to use:

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.

Simple Example:

One script that you attach to both portals. It moves objects with the tag "Ball" over to the 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;
        }
    }
}

You get this:

fun

Complex Example:

You need 3 scripts for this to work:

  • Portal: The thing that touches warpable objects
  • Warpable: A thing that travels through the portals
  • Ghost: A mirrored warpable that shows while going through a portal

This is what the ghost looks like:

ghost

You will need two additional layers – Portal and Ghost, with the collision matrix set as in the image.

matrix

Scripts:

I've added enough comments within the code for you to make sense as to what it's doing.

Portal:

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);
    }
}

Warpable:

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; }
    }
}

Ghost:

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);
    }
}

And the final result is this:

warping

like image 105
Iggy Avatar answered Nov 05 '22 10:11

Iggy