Pixel perfect 2d in 4.3?

Discussion in '2D' started by DarkMarmot, Nov 13, 2013.

  1. DarkMarmot

    DarkMarmot

    New Member

    Joined:
    Nov 13, 2013
    Messages:
    17
    Just wondering if there are any guidelines for making pixel perfect retro 2d games in the new unity 4.3?
    E.g, scaling/cropping and aligning a small 'screen' to fit a designated resolution without any blurring or artifacts?

    Thanks,
    Scott
  2. MagicFred

    MagicFred

    Member

    Joined:
    May 29, 2013
    Messages:
    2
    I have the same question: How to configure the main camera to have 1 pixel = 1 Unite?
    It will be very helpfull to have "clean" X/Y positions like 118 pixels or unite instead of 5.9885631...

    Thank you very much! :)
    Last edited: Nov 13, 2013
  3. hellobard

    hellobard

    Member

    Joined:
    Sep 26, 2012
    Messages:
    51
    Bump on this! I am also wondering how to set up pixel perfection across all mobile devices. Should one import Retina iPad sized sprites and then have Unity somehow scale them down? Or is there / will there be an atlas-switching setup like in NGUI/2d toolkit?
  4. fredstales

    fredstales

    New Member

    Joined:
    Jun 4, 2013
    Messages:
    29
    Yeah I wonder this to, if there are any nice tools now in Unity to handle this.
  5. Loius

    Loius

    Member

    Joined:
    Aug 16, 2012
    Messages:
    536
    It's all math. Which I suck at, but I can try for ya.

    Sprites have a resolution in their import settings - it defaults to 100 pixels per unit.

    You know how high your screen is (Screen.Height), and an ortho camera's "size" indicates how tall of an area it displays.

    So in the game's startup somewhere, you need to do something similar to this:

    float UnitsPerPixel = 1f / 100f;
    float PixelsPerUnit = 100f / 1f; // yeah, yeah, 100
    Camera.main.orthographicSize =
    Screen.height / 2f // ortho-size is half the screen height...
    * UnitsPerPixel;

    So if you have a 640x480 resolution, you'll end up with an ortho size of 2.4 (that's half of 480, divided by 100)
    Last edited: Nov 13, 2013
  6. MagicFred

    MagicFred

    Member

    Joined:
    May 29, 2013
    Messages:
    2
    Yeah, it looks good for me: 640x960, main camera ortho size 4.8
    Thank you :)
  7. Nachtmahr

    Nachtmahr

    New Member

    Joined:
    Apr 4, 2013
    Messages:
    1
    Not working like expected. I get one Pixel offset (tripple checked everything).

    However if I move the Camera Y around 0.0001 the offset fits again. The Texture then offsets again at 0.5f, 0.25f, 0.125f... you see the Pattern =)

    Has someone a solution without Offsetting my Camera on certain numbers?

    Edit: On the Default Sprite Material there is a Option called Pixel Snap that solved my issue. But it still appears in uneven hight resolutions like 481. In case someone want to take a look at the built-in shader you can Download them here: http://unity3d.com/unity/download/archive/

    $pixel_offset.png
    Last edited: Nov 14, 2013
  8. DarkMarmot

    DarkMarmot

    New Member

    Joined:
    Nov 13, 2013
    Messages:
    17
    Thanks for the feedback!

    I'm working on a retro title and wanted to scale and center pixel perfect art for multiple devices.

    The following code assumes you have a max vertical art size you can set and a background that can bleed off the edges,
    art with filter mode 'point' and units to pixels = 1.

    It scales the art at some integer multiple to maintain pixel fidelity

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class ConfigCamera : MonoBehaviour {
    6.    
    7.     public float maxPixelHeight = 214f;
    8.  
    9.     void Awake () {
    10.         float scale = Mathf.Ceil(Screen.height / maxPixelHeight);
    11.         Camera.main.orthographicSize = Screen.height / 2f / scale;
    12.     }
    13.  
    14. }
    15.  
    I don't know if it needs any slight offsets or how to modify the pixel snap in the default shader...
    and if anyone has done any work on writing 'anchor' scripts to place ui elements as in some of the other 2d packages that would be pretty slick.

    Thanks,
    Scott
  9. matbrummitt

    matbrummitt

    Member

    Joined:
    Jan 12, 2010
    Messages:
    61
    I'm really struggling with this also.

    In Photoshop i've designed my level in a document size based on iPad Retina (2048x1536). I've imported some of my sprites into Unity and they take up a much bigger proportion of the camera's viewable area, than they do within photoshop.

    For example, one of my level props takes up approximately 17% of the document window in photoshop. With my game view set to the same dimensions as photoshop, the imported sprite takes up approximately 38% of the screen space. I've no idea how to get around this, and the code listed above does not seem to be working for me.

    EDIT: I have attached a crude example image, which hopefully demonstrates my issue. It shows the space one of my props takes up in photoshop, and then in Unity.

    EDIT2: I tried doubling the orthographic size, so my InitCamera class does the following:
    amera.main.orthographicSize = (Screen.height / 2) / _pixelsToUnits * _mod; // mod is set to 2.

    The above now resembles photoshop. However, i'm now unclear as to what happens when someone is playing on a non-retina iPad. If I change _mod to equal 1, the camera's viewable area is halved, and my prop bottom-right is not on screen anymore. This must be a common problem. Can anyone recommend a solution?

    Thanks for your help

    Mat

    Attached Files:

    Last edited: Nov 23, 2013
  10. DylanWilson

    DylanWilson

    Member

    Joined:
    Dec 5, 2013
    Messages:
    1
    Bump! I am also facing the same issue. Are there any good solutions?
  11. Saxi

    Saxi

    Member

    Joined:
    Jun 28, 2013
    Messages:
    116
    Really wish I could find a good tutorial on this, this is driving me nuts coming from other SDK. Everyone I talk to just gives me a generic answer "unity doesn't have default resolution and it will handle it automatically" but it does.
  12. Saxi

    Saxi

    Member

    Joined:
    Jun 28, 2013
    Messages:
    116
    I read PC you may need to offset by .5px but osx you don't. I assume mobile you don't either but not 100% sure. Still trying to figure this out.
  13. Vanz

    Vanz

    Member

    Joined:
    Nov 19, 2013
    Messages:
    58
    Unity developers could you please clarify this and maybe make a short tutorial as it appears quite a few of your users are struggling with this same topic...

    Thanks,

    Vanz
  14. Invertex

    Invertex

    Member

    Joined:
    Nov 7, 2013
    Messages:
    269
    The only problem with that though, is I think you will loose the built-in draw-call batching of the current system by using multiple Sprite Materials for each texture. This greatly reduces performance, since the game needs to do a Draw Call for each material. The way the new integrated Sprite material works, is that it uses that single material for every texture you've set to Sprite. Meaning only 1 draw call is used for all your textures. The PixelSnap is a PRO feature, sadly =/

    And it may disable some of the other performance enhancements as well, like the automatic atlas creation.

    They go through some of the performance enhancements here:
    Last edited: Dec 15, 2013
  15. Amon

    Amon

    Member

    Joined:
    Oct 18, 2009
    Messages:
    699
    When using the monkey Language there is an import I use called Autofit. It was programmed for the monkey language by DruggedBunny a.k.a James L Boyd. He made it available to everyone and released it to the Public Domain.

    This code has never failed me when using Monkey for a 2d game. It is capable of zooming, autoscaling to fit device res with or without borders and has virtual controls that also work flawlessly on any res.

    The code is pasted below but I have also attached the monkey file.

    What brave soul is going to convert it to work with unity? :)

    Code (csharp):
    1.  
    2.  
    3.  
    4. Strict
    5.  
    6.  
    7. ' -----------------------------------------------------------------------------
    8. ' AutoFit - Virtual Display System... [Public domain code]
    9. ' -----------------------------------------------------------------------------
    10.  
    11.  
    12. ' Couldn't think of a better name!
    13.  
    14.  
    15. Import mojo.app
    16. Import mojo.graphics
    17. Import mojo.input
    18.  
    19.  
    20. ' Changes - made Strict and added VTouchX/Y.
    21.  
    22.  
    23. ' Changes - moved LOADS of locals in UpdateVirtualDisplay into fields
    24. ' and now perform very few checks/calculations on each call (most are
    25. ' only done when the device size or zoom level is changed).
    26.  
    27.  
    28.  
    29.  
    30.  
    31.  
    32.  
    33.  
    34. ' -----------------------------------------------------------------------------
    35. ' Usage. For details see function definitions...
    36. ' -----------------------------------------------------------------------------
    37.  
    38.  
    39.  
    40.  
    41.  
    42.  
    43.  
    44.  
    45.  
    46.  
    47. ' -----------------------------------------------------------------------------
    48. ' SetVirtualDisplay
    49. ' -----------------------------------------------------------------------------
    50.  
    51.  
    52. ' Call during OnCreate, passing intended width and height of game area. Design
    53. ' your game for this fixed display size and it will be scaled correctly on any
    54. ' device. You can pass no parameters for default 640 x 480 virtual device size.
    55.  
    56.  
    57. ' Optional zoom parameter default to 1.0.
    58.  
    59.  
    60. ' -----------------------------------------------------------------------------
    61. ' UpdateVirtualDisplay
    62. ' -----------------------------------------------------------------------------
    63.  
    64.  
    65. ' Call at start of OnRender, BEFORE ANYTHING ELSE, including Cls!
    66.  
    67.  
    68. ' -----------------------------------------------------------------------------
    69. ' VMouseX ()/VMouseY ()
    70. ' -----------------------------------------------------------------------------
    71.  
    72.  
    73. ' Call during OnUpdate (or OnRender) to get correctly translated MouseX ()/MouseY ()
    74. ' positions. By default, the results are bound to the display area within the
    75. ' borders. You can override this by passing False as an optional parameter,
    76. ' and the functions will then return values outside of the borders.
    77.  
    78.  
    79. ' -----------------------------------------------------------------------------
    80. ' VTouchX ()/VTouchY ()
    81. ' -----------------------------------------------------------------------------
    82.  
    83.  
    84. ' Call during OnUpdate (or OnRender) to get correctly translated TouchX ()/TouchY ()
    85. ' positions. By default, the results are bound to the display area within the
    86. ' borders. You can override this by passing False as an optional parameter,
    87. ' and the functions will then return values outside of the borders.
    88.  
    89.  
    90. ' -----------------------------------------------------------------------------
    91. ' VDeviceWidth ()/VDeviceHeight ()
    92. ' -----------------------------------------------------------------------------
    93.  
    94.  
    95. ' Call during OnUpdate (or OnRender) for the virtual device width/height. These
    96. ' are just the values you passed to SetVirtualDisplay.
    97.  
    98.  
    99. ' -----------------------------------------------------------------------------
    100. ' SetVirtualZoom
    101. ' -----------------------------------------------------------------------------
    102.  
    103.  
    104. ' Call in OnUpdate to set zoom level.
    105.  
    106.  
    107. ' -----------------------------------------------------------------------------
    108. ' AdjustVirtualZoom
    109. ' -----------------------------------------------------------------------------
    110.  
    111.  
    112. ' Call in OnUpdate to zoom in/out by given amount.
    113.  
    114.  
    115. ' -----------------------------------------------------------------------------
    116. ' GetVirtualZoom
    117. ' -----------------------------------------------------------------------------
    118.  
    119.  
    120. ' Call in OnUpdate or OnRender to retrieve current zoom level.
    121.  
    122.  
    123.  
    124.  
    125.  
    126.  
    127.  
    128.  
    129.  
    130.  
    131.  
    132.  
    133. ' -----------------------------------------------------------------------------
    134. ' Function definitions and parameters...
    135. ' -----------------------------------------------------------------------------
    136.  
    137.  
    138.  
    139.  
    140.  
    141.  
    142.  
    143.  
    144.  
    145.  
    146. ' -----------------------------------------------------------------------------
    147. ' SetVirtualDisplay: Call in OnCreate...
    148. ' -----------------------------------------------------------------------------
    149.  
    150.  
    151. ' Parameters: width and height of virtual game area, optional zoom...
    152.  
    153.  
    154. Function SetVirtualDisplay:Int (width:Int = 640, height:Int = 480, zoom:Float = 1.0)
    155.     New VirtualDisplay (width, height, zoom)
    156.     Return 0
    157. End
    158.  
    159.  
    160. ' -----------------------------------------------------------------------------
    161. ' SetVirtualZoom: Call in OnUpdate...
    162. ' -----------------------------------------------------------------------------
    163.  
    164.  
    165. ' Parameters: zoom level (1.0 being normal)...
    166.  
    167.  
    168. Function SetVirtualZoom:Int (zoom:Float)
    169.     VirtualDisplay.Display.SetZoom zoom
    170.     Return 0
    171. End
    172.  
    173.  
    174. ' -----------------------------------------------------------------------------
    175. ' AdjustVirtualZoom: Call in OnUpdate...
    176. ' -----------------------------------------------------------------------------
    177.  
    178.  
    179. ' Parameters: amount by which to change current zoom level. Positive values
    180. ' zoom in, negative values zoom out...
    181.  
    182.  
    183. Function AdjustVirtualZoom:Int (amount:Float)
    184.     VirtualDisplay.Display.AdjustZoom amount
    185.     Return 0
    186.  
    187.  
    188. ' -----------------------------------------------------------------------------
    189. ' GetVirtualZoom: Call in OnUpdate or OnRender...
    190. ' -----------------------------------------------------------------------------
    191.  
    192.  
    193. ' Parameters: none...
    194.  
    195.  
    196. Function GetVirtualZoom:Float ()
    197.     Return VirtualDisplay.Display.GetZoom ()
    198.  
    199.  
    200. ' -----------------------------------------------------------------------------
    201. ' UpdateVirtualDisplay: Call at start of OnRender...
    202. ' -----------------------------------------------------------------------------
    203.  
    204.  
    205. ' Parameters:
    206.  
    207.  
    208. ' Gah! Struggling to explain this! Just experiment!
    209.  
    210.  
    211. ' The 'zoomborders' parameter can be set to False to allow you to retain FIXED
    212. ' width/height borders for the current device size/ratio. Effectively, this
    213. ' means that as you zoom out, you can see more of the 'playfield' outside the
    214. ' virtual display, instead of having borders drawn to fill the outside area.
    215. ' See VMouseX ()/Y information for more details on how this can be used...
    216.  
    217.  
    218. ' The 'keepborders' parameter, if set to True, means the outer borders are
    219. ' kept no matter how ZOOMED IN the game is. Setting this to False means you
    220. ' can zoom into the game, the borders appearing to go 'outside' the screen
    221. ' as you zoom further in. You'll have to try it to get it, but it only
    222. ' affects zooming inwards.
    223.  
    224.  
    225. ' NB. *** TURNING 'keepborders' OFF ONLY TAKES EFFECT IF zoomborders IS
    226. ' SET TO TRUE! Borders will remain otherwise... ***
    227.  
    228.  
    229. Function UpdateVirtualDisplay:Int (zoomborders:Bool = True, keepborders:Bool = True)
    230.     VirtualDisplay.Display.UpdateVirtualDisplay zoomborders, keepborders
    231.     Return 0
    232.  
    233.  
    234. ' -----------------------------------------------------------------------------
    235. ' Misc functions: Call in OnUpdate (optionally)...
    236. ' -----------------------------------------------------------------------------
    237.  
    238.  
    239. ' Mouse position within virtual display; the limit parameter allows you to only
    240. ' return values within the virtual display.
    241.  
    242.  
    243. ' Set the 'limit' parameter to False to allow returning of values outside
    244. ' the virtual display area. Combine this with ScaleVirtualDisplay's zoomborders
    245. ' parameter set to False if you want to be able to zoom way out and allow
    246. ' gameplay in the full zoomed-out area...
    247.  
    248.  
    249. Function VMouseX:Float (limit:Bool = True)
    250.     Return VirtualDisplay.Display.VMouseX (limit)
    251.  
    252.  
    253. Function VMouseY:Float (limit:Bool = True)
    254.     Return VirtualDisplay.Display.VMouseY (limit)
    255.  
    256.  
    257. Function VTouchX:Float (index:Int = 0, limit:Bool = True)
    258.     Return VirtualDisplay.Display.VTouchX (index, limit)
    259.  
    260.  
    261. Function VTouchY:Float (index:Int = 0, limit:Bool = True)
    262.     Return VirtualDisplay.Display.VTouchY (index, limit)
    263.  
    264.  
    265. ' Virtual display size...
    266.  
    267.  
    268. Function VDeviceWidth:Float ()
    269.     Return VirtualDisplay.Display.vwidth
    270. End
    271.  
    272.  
    273. Function VDeviceHeight:Float ()
    274.     Return VirtualDisplay.Display.vheight
    275. End
    276.  
    277.  
    278.  
    279.  
    280.  
    281.  
    282.  
    283.  
    284. Class VirtualDisplay
    285.  
    286.  
    287.     Global Display:VirtualDisplay
    288.    
    289.     Private
    290.    
    291.     Field vwidth:Float                  ' Virtual width
    292.     Field vheight:Float                 ' Virtual height
    293.  
    294.  
    295.     Field device_changed:Int                ' Device size changed
    296.     Field lastdevicewidth:Int               ' For device change detection
    297.     Field lastdeviceheight:Int              ' For device change detection
    298.    
    299.     Field vratio:Float                  ' Virtual ratio
    300.     Field dratio:Float                  ' Device ratio
    301.  
    302.  
    303.     Field scaledw:Float                 ' Width of *scaled* virtual display in real pixels
    304.     Field scaledh:Float                 ' Width of *scaled* virtual display in real pixels
    305.  
    306.  
    307.     Field widthborder:Float             ' Size of border at sides
    308.     Field heightborder:Float                ' Size of border at top/bottom
    309.  
    310.  
    311.     Field sx:Float                      ' Scissor area
    312.     Field sy:Float                      ' Scissor area
    313.     Field sw:Float                      ' Scissor area
    314.     Field sh:Float                      ' Scissor area
    315.  
    316.  
    317.     Field realx:Float                       ' Width of SCALED virtual display (real pixels)
    318.     Field realy:Float                       ' Height of SCALED virtual display (real pixels)
    319.  
    320.  
    321.     Field offx:Float                        ' Pixels between real borders and virtual borders
    322.     Field offy:Float                        ' Pixels between real borders and virtual borders
    323.  
    324.  
    325.     Field vxoff:Float                       ' Offsets by which view needs to be shifted
    326.     Field vyoff:Float                       ' Offsets by which view needs to be shifted
    327.  
    328.  
    329.     Field multi:Float                       ' Ratio scale factor
    330.     Field vzoom:Float                       ' Zoom scale factor
    331.     Field zoom_changed:Int                  ' Zoom changed
    332.     Field lastvzoom:Float                   ' Zoom change detection
    333.    
    334.     Field fdw:Float                     ' DeviceWidth () gets pre-cast to Float in UpdateVirtualDisplay
    335.     Field fdh:Float                     ' DeviceHeight () gets pre-cast to Float in UpdateVirtualDisplay
    336.    
    337.     Public
    338.    
    339.     Method New (width:Int, height:Int, zoom:Float)
    340.  
    341.  
    342.         ' Set virtual width and height...
    343.            
    344.         vwidth = width
    345.         vheight = height
    346.  
    347.  
    348.         vzoom = zoom
    349.         lastvzoom = vzoom + 1 ' Force zoom change detection! Best hack ever. (vzoom can be zero.)
    350.  
    351.  
    352.         ' Store ratio...
    353.        
    354.         vratio = vheight / vwidth
    355.  
    356.  
    357.         ' Create global VirtualDisplay object...
    358.        
    359.         Display = Self
    360.    
    361.     End
    362.  
    363.  
    364.     Method GetZoom:Float ()
    365.         Return vzoom
    366.     End
    367.    
    368.     Method SetZoom:Int (zoomlevel:Float)
    369.         If zoomlevel < 0.0 Then zoomlevel = 0.0
    370.         vzoom = zoomlevel
    371.         Return 0
    372.     End
    373.    
    374.     Method AdjustZoom:Int (amount:Float)
    375.         vzoom = vzoom + amount
    376.         If vzoom < 0.0 Then vzoom = 0.0
    377.         Return 0
    378.     End
    379.    
    380.     Method VMouseX:Float (limit:Bool)
    381.        
    382.         ' Position of mouse, in real pixels, from centre of screen (centre being 0)...
    383.        
    384.         Local mouseoffset:Float = MouseX () - Float (DeviceWidth ()) * 0.5
    385.        
    386.         ' This calculates the scaled position on the virtual display. Somehow...
    387.        
    388.         Local x:Float = (mouseoffset / multi) / vzoom + (VDeviceWidth () * 0.5)
    389.  
    390.  
    391.         ' Check if mouse is to be limited to virtual display area...
    392.        
    393.         If limit
    394.    
    395.             Local widthlimit:Float = vwidth - 1
    396.    
    397.             If x > 0
    398.                 If x < widthlimit
    399.                     Return x
    400.                 Else
    401.                     Return widthlimit
    402.                 Endif
    403.             Else
    404.                 Return 0
    405.             Endif
    406.    
    407.         Else
    408.             Return x
    409.         Endif
    410.    
    411.         Return 0
    412.        
    413.     End
    414.  
    415.  
    416.     Method VMouseY:Float (limit:Bool)
    417.    
    418.         ' Position of mouse, in real pixels, from centre of screen (centre being 0)...
    419.  
    420.  
    421.         Local mouseoffset:Float = MouseY () - Float (DeviceHeight ()) * 0.5
    422.        
    423.         ' This calculates the scaled position on the virtual display. Somehow...
    424.  
    425.  
    426.         Local y:Float = (mouseoffset / multi) / vzoom + (VDeviceHeight () * 0.5)
    427.        
    428.         ' Check if mouse is to be limited to virtual display area...
    429.  
    430.  
    431.         If limit
    432.        
    433.             Local heightlimit:Float = vheight - 1
    434.        
    435.             If y > 0
    436.                 If y < heightlimit
    437.                     Return y
    438.                 Else
    439.                     Return heightlimit
    440.                 Endif
    441.             Else
    442.                 Return 0
    443.             Endif
    444.  
    445.  
    446.         Else
    447.             Return y
    448.         Endif
    449.        
    450.         Return 0
    451.  
    452.  
    453.     End
    454.  
    455.  
    456.     Method VTouchX:Float (index:Int, limit:Bool)
    457.        
    458.         ' Position of touch, in real pixels, from centre of screen (centre being 0)...
    459.        
    460.         Local touchoffset:Float = TouchX (index) - Float (DeviceWidth ()) * 0.5
    461.        
    462.         ' This calculates the scaled position on the virtual display. Somehow...
    463.        
    464.         Local x:Float = (touchoffset / multi) / vzoom + (VDeviceWidth () * 0.5)
    465.  
    466.  
    467.         ' Check if touches are to be limited to virtual display area...
    468.        
    469.         If limit
    470.    
    471.             Local widthlimit:Float = vwidth - 1
    472.    
    473.             If x > 0
    474.                 If x < widthlimit
    475.                     Return x
    476.                 Else
    477.                     Return widthlimit
    478.                 Endif
    479.             Else
    480.                 Return 0
    481.             Endif
    482.    
    483.         Else
    484.             Return x
    485.         Endif
    486.    
    487.         Return 0
    488.        
    489.     End
    490.  
    491.  
    492.     Method VTouchY:Float (index:Int, limit:Bool)
    493.    
    494.         ' Position of touch, in real pixels, from centre of screen (centre being 0)...
    495.  
    496.  
    497.         Local touchoffset:Float = TouchY (index) - Float (DeviceHeight ()) * 0.5
    498.        
    499.         ' This calculates the scaled position on the virtual display. Somehow...
    500.  
    501.  
    502.         Local y:Float = (touchoffset / multi) / vzoom + (VDeviceHeight () * 0.5)
    503.        
    504.         ' Check if touches are to be limited to virtual display area...
    505.  
    506.  
    507.         If limit
    508.        
    509.             Local heightlimit:Float = vheight - 1
    510.        
    511.             If y > 0
    512.                 If y < heightlimit
    513.                     Return y
    514.                 Else
    515.                     Return heightlimit
    516.                 Endif
    517.             Else
    518.                 Return 0
    519.             Endif
    520.  
    521.  
    522.         Else
    523.             Return y
    524.         Endif
    525.        
    526.         Return 0
    527.  
    528.  
    529.     End
    530.  
    531.  
    532.     Method UpdateVirtualDisplay:Int (zoomborders:Bool, keepborders:Bool)
    533.  
    534.  
    535.         ' ---------------------------------------------------------------------
    536.         ' Calculate/draw borders, if any, scale, etc...
    537.         ' ---------------------------------------------------------------------
    538.  
    539.  
    540.         ' ---------------------------------------------------------------------
    541.         ' Check for 'real' device resolution change...
    542.         ' ---------------------------------------------------------------------
    543.  
    544.  
    545.         If (DeviceWidth () <> lastdevicewidth) Or (DeviceHeight () <> lastdeviceheight)
    546.             lastdevicewidth = DeviceWidth ()
    547.             lastdeviceheight = DeviceHeight ()
    548.             device_changed = True
    549.         Endif
    550.        
    551.         ' ---------------------------------------------------------------------
    552.         ' Force re-calc if so (same for first call)...
    553.         ' ---------------------------------------------------------------------
    554.  
    555.  
    556.         If device_changed
    557.  
    558.  
    559.             ' Store device resolution as float values to avoid loads of casts. Doing it here as
    560.             ' device resolution may potentially be changed on the fly on some platforms...
    561.            
    562.             fdw = Float (DeviceWidth ())
    563.             fdh = Float (DeviceHeight ())
    564.            
    565.             ' Device ratio is calculated on the fly since it can change (eg. resizeable
    566.             ' browser window)...
    567.            
    568.             dratio = fdh / fdw
    569.  
    570.  
    571.             ' Compare to pre-calculated virtual device ratio...
    572.    
    573.             If dratio > vratio
    574.    
    575.                 ' -----------------------------------------------------------------
    576.                 ' Device aspect narrower than (or same as) game aspect ratio:
    577.                 ' will use full width, borders above and below...
    578.                 ' -----------------------------------------------------------------
    579.    
    580.                 ' Multiplier required to scale game width to device width (to be applied to height)...
    581.                
    582.                 multi = fdw / vwidth
    583.                
    584.                 ' "vheight * multi" below applies width multiplier to height...
    585.                
    586.                 heightborder = (fdh - vheight * multi) * 0.5
    587.                 widthborder = 0
    588.                
    589.             Else
    590.    
    591.                 ' -----------------------------------------------------------------
    592.                 ' Device aspect wider than game aspect ratio:
    593.                 ' will use full height, borders at sides...
    594.                 ' -----------------------------------------------------------------
    595.                
    596.                 ' Multiplier required to scale game height to device height (to be applied to width)...
    597.                
    598.                 multi = fdh / vheight
    599.                
    600.                 ' "vwidth * multi" below applies height multiplier to width...
    601.    
    602.                 widthborder = (fdw - vwidth * multi) * 0.5
    603.                 heightborder = 0
    604.    
    605.             Endif
    606.  
    607.  
    608.         Endif
    609.  
    610.  
    611.         ' ---------------------------------------------------------------------
    612.         ' Check for zoom level change...
    613.         ' ---------------------------------------------------------------------
    614.  
    615.  
    616.         If vzoom <> lastvzoom
    617.             lastvzoom = vzoom
    618.             zoom_changed = True
    619.         Endif
    620.        
    621.         ' ---------------------------------------------------------------------
    622.         ' Re-calc if so (and on first call), or if device size changed...
    623.         ' ---------------------------------------------------------------------
    624.  
    625.  
    626.         If zoom_changed Or device_changed
    627.  
    628.  
    629.             If zoomborders
    630.    
    631.                 ' Width/height of SCALED virtual display in real pixels...
    632.                
    633.                 realx = vwidth * vzoom * multi
    634.                 realy = vheight * vzoom * multi
    635.        
    636.                 ' Space in pixels between real device borders and virtual device borders...
    637.                
    638.                 offx = (fdw - realx) * 0.5
    639.                 offy = (fdh - realy) * 0.5
    640.    
    641.                 If keepborders
    642.    
    643.                     ' -----------------------------------------------------
    644.                     ' Calculate inner area...
    645.                     ' -----------------------------------------------------
    646.  
    647.  
    648.                     If offx < widthborder
    649.                         sx = widthborder
    650.                         sw = fdw - widthborder * 2.0
    651.                     Else
    652.                         sx = offx
    653.                         sw = fdw - (offx * 2.0)
    654.                     Endif
    655.    
    656.                     If offy < heightborder
    657.                         sy = heightborder
    658.                         sh = fdh - heightborder * 2.0
    659.                     Else
    660.                         sy = offy
    661.                         sh = fdh - (offy * 2.0)
    662.                     Endif
    663.    
    664.                 Else
    665.    
    666.                     sx = offx
    667.                     sw = fdw - (offx * 2.0)
    668.    
    669.                     sy = offy
    670.                     sh = fdh - (offy * 2.0)
    671.    
    672.                 Endif
    673.  
    674.  
    675.                 ' Apply limits...
    676.                
    677.                 sx = Max (0.0, sx)
    678.                 sy = Max (0.0, sy)
    679.                 sw = Min (sw, fdw)
    680.                 sh = Min (sh, fdh)
    681.  
    682.  
    683.             Else
    684.  
    685.  
    686.                 ' Apply limits...
    687.  
    688.  
    689.                 sx = Max (0.0, widthborder)
    690.                 sy = Max (0.0, heightborder)
    691.                 sw = Min (fdw - widthborder * 2.0, fdw)
    692.                 sh = Min (fdh - heightborder * 2.0, fdh)
    693.            
    694.             Endif
    695.  
    696.  
    697.             ' Width and height of *scaled* virtual display in pixels...
    698.  
    699.  
    700.             scaledw = (vwidth * multi * vzoom)
    701.             scaledh = (vheight * multi * vzoom)
    702.  
    703.  
    704.             ' Find offsets by which view needs to be shifted...
    705.            
    706.             vxoff = (fdw - scaledw) * 0.5
    707.             vyoff = (fdh - scaledh) * 0.5
    708.  
    709.  
    710.             ' Ahh, good old trial and error -- I have no idea how this works!
    711.            
    712.             vxoff = (vxoff / multi) / vzoom
    713.             vyoff = (vyoff / multi) / vzoom
    714.        
    715.             ' Reset these...
    716.            
    717.             device_changed = False
    718.             zoom_changed = False
    719.            
    720.         Endif
    721.        
    722.         ' ---------------------------------------------------------------------
    723.         ' Draw borders at full device size...
    724.         ' ---------------------------------------------------------------------
    725.  
    726.  
    727.         SetScissor 0, 0, DeviceWidth (), DeviceHeight ()
    728.         Cls 0, 0, 0
    729.  
    730.  
    731.         ' ---------------------------------------------------------------------
    732.         ' Draw inner area...
    733.         ' ---------------------------------------------------------------------
    734.  
    735.  
    736.         SetScissor sx, sy, sw, sh
    737.            
    738.         ' ---------------------------------------------------------------------
    739.         ' Scale everything...
    740.         ' ---------------------------------------------------------------------
    741.        
    742.         Scale multi * vzoom, multi * vzoom
    743.  
    744.  
    745.         ' ---------------------------------------------------------------------
    746.         ' Shift display to account for borders/zoom level...
    747.         ' ---------------------------------------------------------------------
    748.  
    749.  
    750.         If vzoom Then Translate vxoff, vyoff
    751.        
    752.         Return 0
    753.        
    754.     End
    755.  
    756.  
    757. End
    758.  
    759.  
    760.  

    Attached Files:

  16. HubertGendron

    HubertGendron

    New Member

    Joined:
    Feb 22, 2013
    Messages:
    5

    Nachtmahr solution works for me and from all the test that I've done, I was able to keep my batching and also able to use PixelSnap with Unity Free version. . :)

    So here's what have done.

    1 - Add a script to your camera to automatically set the othographic size to get 1:1 pixel.
    Code (csharp):
    1. public void Awake()
    2. {
    3.     //
    4.     camera.orthographicSize = (Screen.height / 100f / 2.0f); // 100f is the PixelPerUnit that you have set on your sprite. Default is 100.
    5. }
    2 - Create a new material, choose from the shader list the Sprites/Default shader and check the Pixel Snap option on your material.

    $MaterialSetup.png

    3 - From now you need to add this material on the SpriteRenderer component of each sprites in your game. (Adding this material on all your sprites will keep batching.)

    $SpriteRenderer.png

    And that's it.

    P.S. As Nachtmahr said it will not work with uneven screen height like 485. But I've found that sometimes, if you uncheck PixelSnap when the screen height is uneven it will work. I've tried to uncheck PixelSnap at runtime when the screen height is uneven, but unfortunately, it looks like PixelSnap does not update the shader at runtime. :(

    Hope it will helps ! :D
    Cheers.
  17. Trebor Zaid

    Trebor Zaid

    New Member

    Joined:
    Nov 21, 2013
    Messages:
    5
    Awesome, that fixed my problem with my random tile map generator. I noticed that depending on the camera's position the UV coordinates would sometimes be one pixel off. I think perhaps there should be a special camera option for 2D games that would automatically enable pixel snap for all sprites. If done at the camera level it could also be designed in such a way that it would work for both even and odd screen resolutions.
  18. ham

    ham

    Member

    Joined:
    Aug 28, 2012
    Messages:
    2
  19. AllegroDigital

    AllegroDigital

    New Member

    Joined:
    Jan 8, 2014
    Messages:
    4
    Does it make sense to use

    Code (csharp):
    1.     public void Awake()
    2.     {
    3.         //
    4.         camera.orthographicSize = (Screen.height / 100f / 2.0f); // 100f is the PixelPerUnit that you have set on your sprite. Default is 100.
    5.     }
    ?

    The reason I ask is that the Screen.height seems to pick up whatever your final resolution is when you build the game. So in the event that you're using something like 640x480 for all your graphics, and then the user sets their resolution to 1280x960, it ends up setting your camera to display more than you would have wanted once they run the game.


    I'm only a couple weeks into learning Unity, so I'm not sure what the ideal way would be to do this...
  20. CarterG81

    CarterG81

    Member

    Joined:
    Jul 25, 2013
    Messages:
    421
    This doesn't work. None of it works.

    It gets the correct size, such as turning a 40x40 square into a 40x40 pixels displayed. However, the pixels displayed are incorrect.

    This is what I call "Pixel Imperfect"

    Here is an image as to what I'm talking about:



    edit: this seems to be an issue with the image being 40x40 and not a Power of Two.

    edit: fixed this by using TexturePacker to pack all animations of a character into a POT image, while still keeping their 40x40 size. Alternatively, one could batch resize "Canvas Size" in Photoshop from NPOT (40x40) to POT (64x64) which would also resolve Unity's butchering of the image when it is NPOT.

    Attached Files:

    Last edited: Jan 17, 2014
  21. imaginaryhuman

    imaginaryhuman

    Member

    Joined:
    Mar 21, 2010
    Messages:
    3,280
    When you use a non-power-of-2 texture, as far as it looks on the surface Unity presents it to you as if you are using a non-power-of-2 texture and all is good... however, in secret, internally they actually scale up the texture to a power-of-2 size, and I don' t mean that they just put the pixels onto a po2 texture with some extra space around it, they actually stretch it to fit (I think). This results in most likely an up-scaling, which means interpolation. Then when you render your texture you think you're rendering non power of 2 but actually internally it's taking a power of 2 upscaled version of the texture (that you can't access from scripts either) and using that... hence it doesn't come out right. I found this out when I was trying to get exact pixel values transferred from a texture into a shader that needed those values to be perfect. So either you have to go with power-of-2 assets up-front and manually deal with what size sprites you need/texture atlas etc, or accept the consequences.
  22. M1k4

    M1k4

    New Member

    Joined:
    Jun 8, 2013
    Messages:
    6
    I used the same formula (screen height/100/2) to test it on a 32x32 sprite and i got bad results, here they are (game window resolution - sprite resolution):

    640x480 - 38x39
    800x600 - 32x32
    1280x720 - 38x39

    Am i doing something wrong or is the formula only working on some cases?
  23. zombiegorilla

    zombiegorilla

    Member

    Joined:
    May 8, 2012
    Messages:
    1,660
    That makes sense, though there is probably no secret stretching going on behind the scenes.

    Typically for shaders and materials of the normal variety, po2 is expected and what is commonly used.

    For sprites though, it is a different story/application. Size(ratio) isn't important, because (ideally), the separate elements are going to be batched into atlases, which will be po2. That is really what sprites are about, discrete chunks of image data.

    Really the best approach is not to use one image for both a sprite and a texture. Though you can, the use case for both is different.
  24. zombiegorilla

    zombiegorilla

    Member

    Joined:
    May 8, 2012
    Messages:
    1,660
    What are you trying to do, and where are you applying it?

    The idea behind pixel perfect is that one pixel on the sprite is one pixel on the display. So a 32x32 should be 32x32 everywhere, assuming your camera is setup correctly. If you change the resolution without changing the camera size, they won't be pixel perfect, but they will stay the same visual size.

    The formula you stated is to set the size of the camera, not elements, for pixel perfect resolution. So, for 800x600 your ortho camera size should be 3, 640x480 it would be 2.4. Assuming you are using a pixel to units of 100, a 32x32 sprite at a scale of 1, will be exactly 32x32 in both of those cases.
  25. M1k4

    M1k4

    New Member

    Joined:
    Jun 8, 2013
    Messages:
    6
    I added the script to the update function on the main camera, so it should've been updating every frame, which means it would work on every resolution, right? (also, the sprite is just a red square on the screen, nothing special about it)
  26. zombiegorilla

    zombiegorilla

    Member

    Joined:
    May 8, 2012
    Messages:
    1,660
    It's probably not something you want put in update, (it is unneeded overhead), but sure, that should work, I just gave it quick test and it worked as expected. You are specifying floats, right?
    Code (csharp):
    1. cam.orthographicSize = (Screen.height/100f/2f);
  27. M1k4

    M1k4

    New Member

    Joined:
    Jun 8, 2013
    Messages:
    6
    Damn... I completely forgot that i was using an intenger, which would always give me a number without decimals, and that explains why it only worked on 800x600. Thank you for pointing that out! :D
  28. zombiegorilla

    zombiegorilla

    Member

    Joined:
    May 8, 2012
    Messages:
    1,660
    Heh... the only reason I noticed it was because when I first typed it in I used ints as well, and the sprite was all over the place in size. It's always those little things that are hardest to spot. ;)
  29. siddharth3322

    siddharth3322

    Member

    Joined:
    Nov 29, 2013
    Messages:
    11
    I can able to cover my game within bound of screen using this approach but still exist a black bars so how to come out of this?
  30. CarterG81

    CarterG81

    Member

    Joined:
    Jul 25, 2013
    Messages:
    421
    Here is a tiny script I use to make sure my Camera is always pixel perfect.

    Simply drop it on your camera.

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class PixelPerfect : MonoBehaviour
    6. {
    7.     public float floatable = 100.0f; //This can be PixelsPerUnit, or you can change it during runtime to alter the camera.
    8.  
    9.     void Update ()
    10.     {
    11.         this.camera.orthographicSize = Screen.height * gameObject.camera.rect.height / floatable / 2.0f;//- 0.1f;
    12.     }
    13. }
    14.  

    I don't remember if this works only for my game, or if it would work with anyone's. I'm pretty sure it will work with anyones.
  31. TheFuntastic

    TheFuntastic

    Member

    Joined:
    Feb 11, 2013
    Messages:
    3
    [ANOTHER SOLUTION FOUND]

    I was struggling immensely with this, but I just found something no one else mentioned:

    I had tried:
    - Texture filter mode set to point
    - Max size set to size of texture
    - Compression off, set to true color (this actually seems unnecessary)
    - Power of two texture size (using texture atlas)
    - Sprite Renderer Material with pixel snap set to on
    - Camera orthographic size set to half height / pixels to unity unit size

    And still I was getting rather savage point filtering artefacts. It wasn't until I found some hidden texture import options that I solved it. If you set the texture to sprite first, and then to advanced, you get a bunch of extra texture import options. In particular Mesh Type. By default unity tries to construct a cutout mesh that saves as much transparency overdraw as possible. But this seems to play havoc with point filtering. So I set it Full Rect and voila - pixel perfect textures!

    $MeshFilter.png

    Note: I'm not sure, but I have a feeling this only works if your viewport is set to whole numbers, as I still sometimes see artefacts in the editor window while in play mode.
    Last edited: Mar 13, 2014
  32. RonnyD1978

    RonnyD1978

    New Member

    Joined:
    Mar 13, 2014
    Messages:
    2
    Hey all, thanks for the great information. I thought Ray Wenderlich's tutorial was very handy.

    One quick question though: is there anything wrong with setting "Pixels to units" option at import settings to value 1? I mean that makes Unity units and pixels really 1-on-1. Which means if i position an object at 50, 50 in editor window then it's really at position 50, 50 in pixels, with expected width and height (shortly saying).

    I wonder why leaving it at default 100, are there any disadvantages or pitfalls along the way?

    Thanks in advance!
    Last edited: Mar 13, 2014
  33. Aedous

    Aedous

    Member

    Joined:
    Jun 20, 2009
    Messages:
    76
    This is a very informative read for people trying to understand the sprites and having pixel perfect calculations.

    I'm not sure if that at all applies to the distance of the sprite from the camera ( when using perspective ). From what I've tried even 'TheFuntastic' option does not actually make a difference with trying to get a pixel perfect representation of your sprite. ( If you could provide a screenshot that would be really good to see, maybe I'm doing something wrong )

    I don't know if there are any drawbacks for the number of pixel to units, I doubt there is that much of a set back as you really are only just determining how many pixels to units you want to use.

    The thing I've come to conclude is that in order to have pixel perfect sprites each sprite must be designed to fit a particular resolution that you have decided that the game view is going to have.

    Having an orthographic camera just makes it easier for you to calculate what the camera size should be in order to get a pixel perfect representation, however if you scale a sprite regardless of if you are in perspective or orthographic you lose that fine pixel quality.

    I may be completely wrong because I've only being messing around with 2d for the last couple of months, but it seems if you are going to do anything that is 2D the higher the resolution you bring into unity the better scaling it applies to the sprite so you reduce the odd pixel stretching.

    EDIT: I'm only referring to actually having to scale pixel art, everything seems to be fine if you are not using pixel art.
    Last edited: Mar 16, 2014
  34. Invertex

    Invertex

    Member

    Joined:
    Nov 7, 2013
    Messages:
    269
    The biggest issue with that, is you will quickly start to run into floating point number inaccuracy as you move further and further away from 0,0,0. By keeping it near 100 pixels to units, you're in that sweet zone that will give you the most range out of the float value range. Here's a nice article that covers this topic:
    http://www.davenewson.com/dev/unity-notes-on-rendering-the-big-and-the-small

    You really shouldn't use 1 Pixel Per Unit. It's not that hard to just divide in your head by 100 to get the values you want to use, all you have to do is shift your number over by 2 decimal places, super simple! Or even, use a power of 2 value, such as 64 or 128, so that your texture dimensions are always either 0.25, 0.5 units, 1 units, 2 units, 4 units, etc.... Making it even easier to keep things matched up and placed around. (setting your Snap Settings to something like 0.25, and holding CTRL to move things around)
  35. DSSiege11

    DSSiege11

    Member

    Joined:
    Oct 19, 2013
    Messages:
    304
    Oh, I didn't know about that... I'm using 1 for pixels to units and it's actually working fine ^^'
    See for yourself: http://siegfriedcroes.com/landsoftreasure/ (Game is actually 320x180, web player is 960x540 so that's scaled up 3x)
  36. Invertex

    Invertex

    Member

    Joined:
    Nov 7, 2013
    Messages:
    269
    It's working fine because you're still near the center of the scene. But as your level gets bigger, the further you get away from 0,0,0 world position, the more inaccurate it's going to become, introducing jitter/position errors. By having your scene scale so big, the distance you can go before that happens is going to be muuuuch smaller.
  37. DSSiege11

    DSSiege11

    Member

    Joined:
    Oct 19, 2013
    Messages:
    304
    Hmmm, I'll have to test that. If I would move my scene (object, player, camera,...) very far away from 0,0 should that show the jitter you are talking about? If so I'll try doing that and see what happens.

    EDIT: Moved everything to (10000, 5000), no difference at all ;)
    Last edited: Mar 16, 2014
  38. RonnyD1978

    RonnyD1978

    New Member

    Joined:
    Mar 13, 2014
    Messages:
    2
    Invertex: I see your point here, smaller numbers so it's possible to plot further away positions within a level.

    Project i'm working on right now will only be 1, 2 maybe 3 screens max. So it's not going to be a big problem i think, pixel perfect and easy positioning are the most important aspects of my engine on top of Unity.

    I was thinking though: maybe i'm going to maintain 1 pixel => 2 units for SD textures and 1 pixel => 1 unit for HD textures (retina?), plotting everything onto a 2048x1536 surface.

    Of course at this moment i'm guessing a little and try out things that may easily fail (or not). Thanks for the info and great advice.
  39. Venks

    Venks

    New Member

    Joined:
    Nov 20, 2013
    Messages:
    1
    @HubertGendron I followed your instructions and now everything is working A-OK for me. Thanks a bunch!
  40. CarterG81

    CarterG81

    Member

    Joined:
    Jul 25, 2013
    Messages:
    421
    Is this PRO only? I do not have those last two options: "Mesh Type" and "Extrue Edges".



    This is still a problem, even with all other settings correct.


    Unity pixel distortion occurs unless I offset the camera to .1

    I have tried everything but Pixel Snap on.


    The developers of Unity need to fix this. It's a horrendous bug which plagues all 2D developers. A hacky workaround or adjusting options is no excuse. This is a real issue which should simply not exist in the first place.

    $Unity-pixel-distortion.gif
  41. DSSiege11

    DSSiege11

    Member

    Joined:
    Oct 19, 2013
    Messages:
    304
    I totally agree, I'm also have to use some slight offset to make my pixel art look right, I absolutely hate the distortion. Luckily the offset fix always works so for now it's fine but they should definitely fix it!
  42. CarterG81

    CarterG81

    Member

    Joined:
    Jul 25, 2013
    Messages:
    421
    It's not fine for me, because as my character moves in a 2D map, it distorts the pixels resulting in a kindof flickering of different pixels.



    Pixel Snap - ON actually makes it WORSE.
    Last edited: May 2, 2014
  43. CarterG81

    CarterG81

    Member

    Joined:
    Jul 25, 2013
    Messages:
    421