space wrap 3D

Howdy,
I have written this script to wrap objects around in space à la Asteroids, but in 3D.
It works fine (don’t have to worry about them wrapping to the exactly correct position, or having to have a duplicate of the object (one wrapping out, one wrapping in) etc, since it’s in 3D, and the boundary is invisible in world space).

However, my code definitely doesn’t follow the DRY principle.
I was wondering if anyone might help me with a function which would replace the repetitions.

it would really just need to take in two parameters. One for in which axis the boundary is being exceeded. And one for whether the boundary is being exceeded in the positive or the negative.

(The Portal() function just marks where the asteroid goes through the ‘boundary’ with a portal animation (on the gameObject ‘wrapWarp’))

public class PositionWrap : MonoBehaviour
{
    Vector3 asteroidSize;
    float asteroidDiameter;
    public float boundaryWidth = 100;
    public GameObject wrapWarp;
    public GameObject wrapWarpClone;

    void Start()
    {
        asteroidDiameter = transform.localScale.x;
        asteroidSize = transform.localScale;
    }

    void Update()
    {
        Vector3 asteroidPos = transform.position;

        if (transform.position.x < -boundaryWidth)
        {
            Portal(Vector3.right);
            asteroidPos.x += boundaryWidth * 2 - asteroidDiameter;
        }
        if (transform.position.x > boundaryWidth)
        {
            Portal(Vector3.left);
            asteroidPos.x -= boundaryWidth * 2 - asteroidDiameter;
        }
        if (transform.position.y < -boundaryWidth)
        {
            Portal(Vector3.up);
            asteroidPos.y += boundaryWidth * 2 - asteroidDiameter;
        }

        if (transform.position.y > boundaryWidth)
        {
            Portal(Vector3.down);
            asteroidPos.y -= boundaryWidth * 2 - asteroidDiameter;
        }

        if (transform.position.z < -boundaryWidth)
        {
            Portal(Vector3.forward);
            asteroidPos.z += boundaryWidth * 2 - asteroidDiameter;
        }
        if (transform.position.z > boundaryWidth)
        {
            Portal(Vector3.back);
            asteroidPos.z -= boundaryWidth * 2 - asteroidDiameter;
        }

        transform.position = new Vector3(asteroidPos.x, asteroidPos.y, asteroidPos.z);
    }

    void Portal (Vector3 facing)
    {
        wrapWarpClone = (GameObject) Instantiate(wrapWarp, transform.position, Quaternion.LookRotation(facing));
        wrapWarpClone.transform.localScale = asteroidSize;
    }
}

I don’t see any repetition here except that you should replace “transform.position.XXXX” with “asteroidPos.XXXX”. You also might want to put an “else if” for the opposite side since you can’t wrap left and right at the same time.

You also could add another member variable to combine “boundaryWidth * 2 - asteroidDiameter” into one value in Start.

You could also use your “direction” vector for the translation

public class PositionWrap : MonoBehaviour
{
    Vector3 asteroidSize;
    float asteroidDiameter;
    public float boundaryWidth = 100;
    public GameObject wrapWarp;
    public GameObject wrapWarpClone;
    private float wrapDistance;
    void Start()
    {
        asteroidDiameter = transform.localScale.x;
        asteroidSize = transform.localScale;
        wrapDistance = boundaryWidth * 2f - asteroidDiameter;
    }

    void Update()
    {
        Vector3 asteroidPos = transform.position;

        if (asteroidPos.x < -boundaryWidth)
        {
            Portal(Vector3.right);
        }
        else if (asteroidPos.x > boundaryWidth)
        {
            Portal(Vector3.left);
        }
        
        if (asteroidPos.y < -boundaryWidth)
        {
            Portal(Vector3.up);
        }
        else if (asteroidPos.y > boundaryWidth)
        {
            Portal(Vector3.down);
        }

        if (asteroidPos.z < -boundaryWidth)
        {
            Portal(Vector3.forward);
        }
        else if (asteroidPos.z > boundaryWidth)
        {
            Portal(Vector3.back);
        }
    }

    void Portal(Vector3 facing)
    {
        wrapWarpClone = (GameObject) Instantiate(wrapWarp, transform.position, Quaternion.LookRotation(facing));
        wrapWarpClone.transform.localScale = asteroidSize;
        transform.position += facing * wrapDistance;
        // maybe create another "portal" on the "exit"?
    }

I would probably rename the “Portal” method to “Wrap” or something like that.

ps: I think i will never understand why people do things like:

transform.position = new Vector3(asteroidPos.x, asteroidPos.y, asteroidPos.z);

asteroidPos is already a Vector3. A Vector3 is a value type so each time you pass it around you already work with a copy.