Search Unity

Screen Wrapping

Discussion in 'Editor & General Support' started by Adam-Buckner, May 11, 2009.

  1. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    So I was lying in bed last night unable to sleep with prolly too much LemSip and too much Springheel Jack going on in my head...

    I was wondering how you'd ask unity to do a screen wraparound...

    I can imagine how you could reposition the game object itself with a script that would reposition it to an opposite part of the window if it strayed outside of the bounds of the game... but how would you tell Unity to draw the part of the game object that extends beyond the edge of the screen if the center of the game object has not yet done so...

    I'd imagine if you wrapped the g.o., the object would *pop* from one side to the other, and not make a smooth transition.

    I imagined some funky way of instantiating a g.o. (or several up to 4) that mirrored the first, and was on the other side of the game screen, but this seems a bit ridiculous...

    If this sounds like a pointless ramble, this is what I'm trying to get at:




     
  2. Tempest

    Tempest

    Joined:
    Dec 10, 2008
    Posts:
    1,286
    I saw the picture and came up with an answer before I read the rest of your post. Then I saw your answer was the same as mine (instantiate other GameObjects).

    It would be interesting. The tough part would be what happens when the wrap is complete: Your original gets deleted, and (one of the ) copy is now the original, and you have to ensure any properties which were part of the original were transferred over.

    It would be amusing.
     
  3. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    I'd suppose you'd have to have some sort of master g.o. that didn't actually move nor was visible, and then others that were instantiated and destroyed based on where they were in the game...

    Anyone know the guts of how the draw functions or draw calls work that out allow a more elegant solution?

    And I'll admit that this is *mostly* idle curiosity...

    [P.S.: @Temepest - I want your .sig to click thru! ~.^]
     
  4. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    This has wraparound, for the large asteroids anyway. It's four game objects, but you don't need to instantiate and destroy stuff; it's just one asteroid with 3 children (for the purposes of making a prefab; the position and rotations are maintained by script). I almost got it working with some camera tricks instead, but it didn't quite work and was more straightforward just to do it that way.

    --Eric
     
  5. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Eric,

    That makes sense. I realized that you didn't need to instantiate and destroy the child objects; you just need to relocate them depending on the position of the 'master' object.

    Seems a bit of a faff, but it's still the only way I can think of doing it!
     
  6. obsidian

    obsidian

    Joined:
    Apr 22, 2010
    Posts:
    13
    Sorry to dredge this up, but I'm making an Asteroids clone to get the hang of things, and this is the biggest hurdle I've come across. At the moment it works by teleporting things across the screen when they move a certain distance off screen, but obviously that's far from satisfactory.

    I'm not sure I follow this:
    There doesn't seem to be a source file for the game given, so I don't think I can go and look myself...
     
  7. Adam-Buckner

    Adam-Buckner

    Joined:
    Jun 27, 2007
    Posts:
    5,664
    Well, I've simply done the following with one of my games:

    Code (csharp):
    1. function Update () {
    2.  
    3.     //  Make sure we stay in the 2D plane.
    4.     thisTransform.position.y = 0;
    5.  
    6.     //  Make sure we stay in the 2D rotation
    7.     thisTransform.rotation.z = 0;
    8.     thisTransform.rotation.x = 0;
    9.  
    10.     thisTransform.Translate ((currentVelocity * Time.deltaTime), Space.World);
    11.     var currentLocation : Vector3 = thisTransform.position;
    12.    
    13.     var wrapAround : boolean = false;
    14.     if (currentLocation.x < minX) {
    15.         currentLocation.x = currentLocation.x + screenX;
    16.         wrapAround = true;
    17.     }
    18.     if (currentLocation.z < minZ) {
    19.         currentLocation.z = currentLocation.z + screenZ;
    20.         wrapAround = true;
    21.     }
    22.     if (currentLocation.x > maxX) {
    23.         currentLocation.x = currentLocation.x - screenX;
    24.         wrapAround = true;
    25.     }
    26.     if (currentLocation.z > maxZ) {
    27.         currentLocation.z = currentLocation.z - screenZ;
    28.         wrapAround = true;
    29.     }
    30.     if (wrapAround)
    31.         transform.position = currentLocation;
    32.    
    33.     wrapAround = false;
    34. }
    Then I make sure that the ship has travelled all the way off of the screen (just) by a pixel on the widest part of the object.

    This is sub optimal as there is a "dead zone" around the border of the game where the ship could be invisible, but it plays seamlessly and is fun to play and the dead zone is very hard to exploit.

    The other method requires 4 objects, all coordinated by the size of the screen, each offset by 1 screen width or 1 screen height. This means when the master object begins to leave the screen, some part of one or all the other 3 slave objects begin to enter the screen from the other side.

    Does that make sense?

    The scripting (as Eric put it) is a Parent and 3 children. The script keeps them 1 width or height apart.
     
  8. obsidian

    obsidian

    Joined:
    Apr 22, 2010
    Posts:
    13
    That makes a lot more sense thanks. The multiple object solution could be a fit fiddly to implement I guess, but it's a nice idea.

    The way you have done it is pretty much my current implementation. My concern with it is that different objects are different sizes, and therefore have different sizes "dead zones" which seems a bit odd.

    I guess I'll leave it as it is for now, and maybe try the multiple object solution when I've got everything else done :D
     
  9. Cirrocumulus

    Cirrocumulus

    Joined:
    Apr 9, 2017
    Posts:
    28
    I was having a go at this myself but couldn't find a solution that could handle wrapping of objects of different sizes. So i came up with this. Still suboptimal because there is no "real" old-school wrapping, just teleportation. But the dead zone is minimal and it works nicely.
    using UnityEngine;

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class WrapAround : MonoBehaviour {
    4.  
    5.     //
    6.     // Private fields
    7.     //
    8.     private Vector2 screenPos;
    9.     private Vector2 screenExtents;
    10.     private float newX;
    11.     private float newY;
    12.     private readonly Vector3 halfUnit = Vector3.one / 2;
    13.     private SpriteRenderer rend;
    14.     private Camera cam;
    15.  
    16.     //
    17.     // Initialisation
    18.     //
    19.     private void Awake()
    20.     {
    21.         rend = GetComponent<SpriteRenderer>();
    22.         cam = Camera.main;
    23.     }
    24.  
    25.     //
    26.     // We check on every physics update
    27.     //
    28.     void FixedUpdate () {
    29.         // Viewport position of the object, based on the center
    30.         // of its render bounds.
    31.         screenPos = cam.WorldToViewportPoint(rend.bounds.center);
    32.  
    33.         // Viewport extents of the object. The extents are just a vector,
    34.         // so we subtract a half-unit vector in order to align it with
    35.         // the viewport coordinate system.
    36.         screenExtents = (cam.WorldToViewportPoint(rend.bounds.extents) - halfUnit);
    37.  
    38.         // We check if the object's position is outside the boundary.
    39.         // If the boundary is 1 (right or top), we add the extents
    40.         // to the position check. If the boundary is 0 (left or bottom),
    41.         // we subtract.
    42.         if (screenPos.x > 1.0f + screenExtents.x)
    43.         {
    44.             newX = -screenExtents.x;
    45.             newY = screenPos.y;
    46.             SetNewPosition();
    47.         }
    48.         if (screenPos.x < 0 - screenExtents.x)
    49.         {
    50.             newX = 1.0f + screenExtents.x;
    51.             newY = screenPos.y;
    52.             SetNewPosition();
    53.         }
    54.         if (screenPos.y > 1.0f + screenExtents.y)
    55.         {
    56.             newY = -screenExtents.y;
    57.             newX = screenPos.x;
    58.             SetNewPosition();
    59.         }
    60.         if (screenPos.y < 0 - screenExtents.y)
    61.         {
    62.             newY = 1.0f + screenExtents.y;
    63.             newX = screenPos.x;
    64.             SetNewPosition();
    65.         }
    66.     }
    67.  
    68.     void SetNewPosition()
    69.     {  
    70.         // Convert from viewport coordinates back to world.
    71.         // Camera is at z = -10 so we compensate for that here,
    72.         // otherwise the transform z will be at -10 and invisible.
    73.         transform.position = cam.ViewportToWorldPoint(new Vector3(newX, newY, 10));
    74.     }
    75. }
     
  10. apalix

    apalix

    Joined:
    Feb 8, 2019
    Posts:
    1
    I've been working on this for a while and experience severe flickering. If it works left to right, as mine did then it flickered going right to left at the edge of the screen. I finally solved it with the following code.
    Code (CSharp):
    1.        
    2.   public float left = .8f; //Temporary until left to right value is worked out
    3.   public float right = .5f; //Temporary until right to left value is worked out
    4.   void CalcualteMovement()
    5.   {
    6.         float horizontalInput = Input.GetAxis("Horizontal");
    7.         float verticalInput = Input.GetAxis("Vertical");
    8.         var newPosition = transform.position;
    9.         var viewportPosition = Camera.main.WorldToViewportPoint(transform.position);
    10.         if (viewportPosition.x > 1 || viewportPosition.x < 0)
    11.         {
    12.             if (viewportPosition.x <0) // going left
    13.                 newPosition.x = -newPosition.x - left) ;
    14.             else
    15.                 newPosition.x = -newPosition.x;
    16.         }
    17.         if (viewportPosition.y > 1 || viewportPosition.y < 0)
    18.         {
    19.             newPosition.y = -newPosition.y + right;
    20.         }
    21.  
    22.         transform.position = newPosition;
    23.         Vector3 direction = new Vector3(horizontalInput, verticalInput, 0);
    24.         transform.Translate(direction * _speed * Time.deltaTime);
    25.   }
    26.  

    Right and left are a public float values that you can change in the Inspector until it works correctly. I found that left = 0.8f and right = .5 worked for me. When the object is almost off the screen, those values cause it to appear halfway on the screen edge after transporting it to the opposite side. It works even in small increments at the edge of the screen without any flicker at all.