Search Unity

Snapping camera to grid?

Discussion in 'Scripting' started by Denisowator, Oct 18, 2017.

  1. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    I've just had a look around, but can't seem to find anything on this (that is relevant).

    Basically my game is in pixel art, with the PPU of 16. And I've currently settled on a normal, smooth camera follow effect, but I just want to test out how a pixel-snapping camera would look like.

    So how could I for example, create a grid with each of the cells being the same size as the pixels (the unit value of which I have figured out already), and then make the camera movement snap to those cells, or by that specific unit value that matches a single pixel?

    And would it be easy to still have the "Vector3.Lerp" functionality in the camera's movement, so that the camera slows down and speeds up, but still moves only in per-pixel values?
     
    Last edited: Oct 20, 2017
  2. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
  3. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    If I'm getting you right, I think this should work. You wouldn't need to create a grid, just call this method right after your cam LERP.

    Basically this figures out what the closest snapping points are relatively to your current camera position, and then rounds to the closest.
    Code (CSharp):
    1.  
    2. void SnapCamera (Vector3 position)
    3. {
    4.     float x = 0f;
    5.     //Put in your own cell size
    6.     float cellSize = 0.02f;
    7.     //Store the remainder after dividing the current x position by the cellsize
    8.     float xRemainder = position.x % cellSize;
    9.     //Figure out to which of the 2 closest points you're going to snap to
    10.     //Do it by checking if the remainder of x is greater than half the cell size, just like how you normally round numbers
    11.     if (xRemainder >= cellSize / 2f)
    12.     {
    13.         //It's greater than half, means we should round up and keep in mind if x was positive or negative
    14.         x = position.x >= 0f ? position.x - xRemainder + cellSize : position.x + xRemainder - cellSize;
    15.     }
    16.     else
    17.     {
    18.         //It's smaller than half, we should round to the lower snapping point
    19.         x = position.x >= 0f ? position.x - xRemainder : position.x + xRemainder;
    20.     }
    21.  
    22.     //Do the same for y
    23.     //Reassign the position with the new x and y
    24. }
    25.  
    It's untested let me know if you run into any issues
     
    Last edited: Oct 19, 2017
    Denisowator likes this.
  4. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    Sorry, but I got a bit of a noob question.
    What arguments should I be declaring when calling the method?
     
  5. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    The camera's position. Extend the method to modify the y value the same way and then assign the new x and y back to the camera.
     
    Denisowator likes this.
  6. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    It doesn't seem to be doing anything, though I'm not getting any errors or warnings either.

    Here's the current code (it's in a script attached to the camera):
    Code (CSharp):
    1. void LateUpdate () {
    2.         //Setting desiredPosition to the player's position along with an offset Vector3.
    3.         Vector3 desiredPosition = target.position + offset;
    4.         //Smoothing between current position and desiredPosition.
    5.         Vector3 smoothedPosition = Vector3.Lerp (transform.position, desiredPosition, smoothSpeed * Time.deltaTime);
    6.         SnapCamera(transform.position);
    7.         transform.position = smoothedPosition;
    8.     }
    9.  
    10.     void SnapCamera (Vector3 position) {
    11.     float x = 0f;
    12.     //Put in your own cell size
    13.     float cellSize = 0.03125f;
    14.     //Store the remainder after dividing the current x position by the cellsize
    15.     float xRemainder = position.x % cellSize;
    16.     //Figure out to which of the 2 closest points you're going to snap to
    17.     //Do it by checking if the remainder of x is greater than half the cell size, just like how you normally round numbers
    18.     if (xRemainder >= cellSize / 2f) {
    19.         //It's greater than half, means we should round up and keep in mind if x was positive or negative
    20.         x = position.x >= 0f ? position.x - xRemainder + cellSize : position.x + xRemainder - cellSize;
    21.     }
    22.  
    23.     else {
    24.         //It's smaller than half, we should round to the lower snapping point
    25.         x = position.x >= 0f ? position.x - xRemainder : position.x + xRemainder;
    26.     }
    27.  
    28.     //Do the same for y
    29.  
    30.     float y = 0f;
    31.     //Store the remainder after dividing the current y position by the cellsize
    32.     float yRemainder = position.y % cellSize;
    33.     //Figure out to which of the 2 closest points you're going to snap to
    34.     //Do it by checking if the remainder of y is greater than half the cell size, just like how you normally round numbers
    35.     if (yRemainder >= cellSize / 2f) {
    36.         //It's greater than half, means we should round up and keep in mind if y was positive or negative
    37.         y = position.y >= 0f ? position.y - yRemainder + cellSize : position.y + yRemainder - cellSize;
    38.     }
    39.  
    40.     else {
    41.         //It's smaller than half, we should round to the lower snapping point
    42.         y = position.y >= 0f ? position.y - yRemainder : position.y + yRemainder;
    43.     }
    44.     //Reassign the position with the new x and y
    45.     position.x = x;
    46.     position.y = y;
    47.     }
    I tried doing things like putting the method call after the "transform.position = smoothedPosition;" line, taking that line out, etc. But the camera still just behaves normally.
     
    Last edited: Oct 20, 2017
  7. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    Send "smoothedPosition" into the SnapCamera method instead of transform.position. Basically what the method does is it adjusts whatever position you pass and snaps it to the closest vertex of the cell its in.

    At the end, of "SnapCamera", what I mean by assign the position is that you should let the camera know that the x and y you just created are its new position. Just add this line.
    Code (CSharp):
    1. transform.position = new Vector3 (x, y, transform.position.z);
    Also, make sure you delete the line "transform.postition = smoothedPosition" after you call snapcamera since it will revert the camera's position to "smoothedPosition" instead of the newly assigned vector.
     
    Denisowator likes this.
  8. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    It seems to be working, in that it snaps to the cell value, but it doesn't seem to at all have the properties of the "smoothedPosition".

    When you say "Send "smoothedPosition" into the SnapCamera method instead of transform.position", do you mean like this:
    Code (CSharp):
    1. SnapCamera(smoothedPosition);
    Or do you mean, put the whole Vector3 into the actual method?

    The camera has some strange behaviors right now, like when I walk a bit to the left, the smooth effect does activate, and it causes the camera to shake non-stop even when the player and camera are standing still. It doesn't happen when I walk to right.

    I made a new scene and removed everything aside from the player, camera, and a quad for the player to walk on, but it still happens.
     
    Last edited: Oct 20, 2017
  9. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    Yes, that way. The former that is. Can you post your new code?
     
  10. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    Well currently all that I changed is that method call, hence why I asked wheher it's what I'm supposed to do. Where about would I put the "smoothedPosition" Vector3 within the method?
     
  11. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    Code (CSharp):
    1. void LateUpdate ()
    2. {
    3.     Vector3 desiredPosition = target.position + offset;
    4.     Vector3 smoothedPosition = Vector3.Lerp (transform.position, desiredPosition, smoothSpeed * Time.deltaTime);
    5.     transform.position = GetSnappedPosition (smoothedPosition);
    6. }
    7.  
    8. Vector3 GetSnappedPosition (Vector3 position)
    9. {
    10.     float x = 0f;
    11.     float cellSize = 0.03125f;
    12.     float xRemainder = position.x % cellSize;
    13.     if (xRemainder >= cellSize / 2f)
    14.     {
    15.         x = position.x >= 0f ? position.x - xRemainder + cellSize : position.x + xRemainder - cellSize;
    16.     }
    17.     else
    18.     {
    19.         x = position.x >= 0f ? position.x - xRemainder : position.x + xRemainder;
    20.     }
    21.  
    22.     float y = 0f;
    23.     float yRemainder = position.y % cellSize;
    24.     if (yRemainder >= cellSize / 2f)
    25.     {
    26.         y = position.y >= 0f ? position.y - yRemainder + cellSize : position.y + yRemainder - cellSize;
    27.     }
    28.     else
    29.     {
    30.         y = position.y >= 0f ? position.y - yRemainder : position.y + yRemainder;
    31.     }
    32.     return new Vector3 (x, y, position.z);
    33. }
     
    Denisowator likes this.
  12. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    Thanks, it works better now. Though I noticed something about the shacking issue that still seems to be happening. What happens right now is when the camera's x or y position reaches the same value as the cellSize variable's value, the Lerp seems to try and override the snapping, and they sort of happen at the same time (resulting in a shaking).
     
  13. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    Can you lower the cellSize to something like 0.005f and see if the camera behaves weirdly? Even if it defeats the purpose, it'd help diagnose the code.
     
    Denisowator likes this.
  14. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    The weird behaviour is still there after lowering the cellSize, and it still happens when the position reaches its (cellSize's) value (currently 0.005).
     
    Last edited: Oct 20, 2017
  15. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    Try this, it should work now.
    Code (CSharp):
    1. Vector3 GetSnappedPosition (Vector3 position)
    2. {
    3.     float x = 0f;
    4.     float cellSize = 0.03125f;
    5.     float xRemainder = position.x % cellSize;
    6.     if (xRemainder >= cellSize / 2f)
    7.     {
    8.         x = position.x >= 0f ? position.x - xRemainder + cellSize : position.x - xRemainder - cellSize;
    9.     }
    10.     else
    11.     {
    12.         x = position.x - xRemainder;
    13.     }
    14.  
    15.     float y = 0f;
    16.     float yRemainder = position.y % cellSize;
    17.     if (yRemainder >= cellSize / 2f)
    18.     {
    19.         y = position.y >= 0f ? position.y - yRemainder + cellSize : position.y - yRemainder - cellSize;
    20.     }
    21.     else
    22.     {
    23.         y = position.y - yRemainder;
    24.     }
    25.     return new Vector3 (x, y, position.z);
    26. }
     
    Denisowator likes this.
  16. Denisowator

    Denisowator

    Joined:
    Apr 22, 2014
    Posts:
    918
    Thank you, it works great now. :D
     
    ADNCG likes this.
  17. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    For code readability, why would you not use Mathf.Round ?

    NewPos x = round (oldpos x * 16) / 16
     
    Denisowator likes this.
  18. ADNCG

    ADNCG

    Joined:
    Jun 9, 2014
    Posts:
    994
    Sure, that's a lot cleaner.
    Code (CSharp):
    1.  
    2. Vector3 GetSnappedPosition (Vector3 position)
    3. {
    4.     float cellSize = 0.03125f;
    5.     float xRemainder = position.x % cellSize;
    6.     float yRemainder = position.y % cellSize;
    7.     float x = position.x + (Mathf.Round (xRemainder * 32f) / 32f);
    8.     float y = position.y + (Mathf.Round (yRemainder * 32f) / 32f);
    9.     return new Vector3 (x, y, position.z);
    10. }
    Edit : Fixed
     
    Last edited: Oct 20, 2017
    Denisowator likes this.
  19. sngdan

    sngdan

    Joined:
    Feb 7, 2014
    Posts:
    1,154
    or...

    Code (CSharp):
    1. Vector3 GetSnappedPosition (Vector3 position, float snapPPU = 1f)
    2. {
    3.     float x = Mathf.Round (position.x * snapPPU) / snapPPU;
    4.     float y = Mathf.Round (position.y * snapPPU) / snapPPU;
    5.     return new Vector3 (x, y, position.z);
    6. }
     
    Denisowator and ADNCG like this.