Dev Blog 5: Window Resolution and Sprites
So, as I mentioned last time I ran into a bit of trouble when working with my new Buffalo sprite. I was having two issues in Unity (5.2.3), the first was that I was having trouble where my sprite animations would randomly be off centered by a pixel and the other was that occassionally a pixel would show up on the edge of the sprite half transparent or real pixels would disappear completely.
I googled tons of articles and help sites wich all offered common fixes you have to do to make your sprites work 'pixel perfect'. I'll probably miss a few, but to keep things complete, some of those steps are:
Sprite Import Settings
1. Pixels Per Unit: Set it to something that makes sense. 1 is a tight 1 to 1 ratio, but not necessary. From what I read, it relates primarily to 1 pixel = 1 meter, where the built in physics system will then make use of that value to do its calculations. It also will have an effect on the size of your character when displayed on the screen. I read that you should set it to 1, but I don't believe that it is necessary. Just something to note.
2. Generate Mip Maps: If you're doing sprites and don't plan on having huge changes in zoom levels, this one should be turned off. It can cause edge pixels to be pulled in when the system thinks it should display the sprite smaller or larger than how it was originally drawn. Basically, it can cause edge artifacts.
3. Filter Mode: Point. Since we're working with sprite/pixel art, we don't want any blending to occur as it will change the color and alphas of our pixels.
4. Max Size: set to the size of your texture, so that your texture won't be resized, which could cause your pixels to be stretched in an unhelpful manner.
5. Format: Truecolor. We want the exact pixel colors we used, not approximations.
Project Settings: Quality
Further suggestion, go to
Edit > Project Settings > Quality
We don't want any pixel approximation or blending between pixels so set Anisotripic Textures: Disabled and Anti Aliasing: Disabled. These may not be required depending on what you're doing exactly, as you may desire them on for certain shader effects or smoother lighting.
Project Settings: Player
It is important to decide early on what screen resolution you plan on using. This is important because you'll need to draw you sprites at a size that looks good with your resolution. I decided to do the various 16:9 resolutions and probably will provide non widescreen option or two in addition.
Edit > Project Settings > Player
1. Temporarily uncheck Default Is Full Screen: The additional Default width and height options will appear. Populate those with your planned base resolution. Whatever you set here will show up in your Game Tab, under the 'Standalone' resolution.
2. Recheck Default Is Full Screen: (unless you don't want this of course)
3. Supported Aspect Ratios: Uncheck the aspect ratios that you don't plan on supporting.
Game Tab Settings
And now for the root of my problem. All the things so far are just helpful things to consider when working with pixel sprites, but they may not be required depending on what you're doing in your game. Think of it as a good starting point. The first of my issues (animations that move around by a pixel almost randomly) was a result of me watching my Game Tab Screen when it was in a irregular resolution, despite that I had it set to 16:9.
The buffalo is drawn to only move its head and tail, however the whole body jerks left and right or sometimes up and down depending on where it was placed on the screen.
The reason is because the tiny game window was not large enough to display in a true 16:9 aspect ratio and so it decided to come 'close', which then causes the sprite to do this effect. I found that if I'd choose the 'Maximize on Play' option it would have enough space to properly display the ratio and the animation would look correct.
One of my problems during this whole mess was that I was trying to adjust my sprites pivot in the wrong resolution and then when I'd play the game it would all of a sudden look wrong. I had adjusted it to the wrong pivots not knowing I wasn't in a true 16:9 display.
The solution that worked for me was to define some proper 16:9 resolutions in the game tab where I could see the various resolutions I plan on supporting and ensuring that they display correctly as well as a very tiny one that I can swap to when setting my animations that will fit within the game tab and not get distorted.
You may also notice that when using one of these custom resolutions if the tab can't fit it properly, it'll say in the top left corner what resolution it is using. I found that when using a built in one, like the 16:9 mentioned before, that it won't say anything when it isn't proper 16:9. Keep your eye on this if you notice sprites randomly jumping by pixels when animated.
Now for the final solution to my issues. Be sure that your camera is set to a Projection: Orthographic. Then take the time to get a correct Size: where size is calculated from your screen height. If you plan on having an adjustable window or your resolution will change, then you may need to change it at runtime. Here is the code I use:
this.mainCamera.orthographicSize = Screen.height / (2.0f * this.scale);
I cached my camera into my mono behavior and have a float scale property that effects the zoom level. I then make sure this code runs when the screen changes sizes.
My last problem was seemed to show itself when my camera had moved in the scene. You'll see near the feet when the head moves around that the 2 pixel border becomes one pixel in a few frames. I knew enough to force the camera's position to always be an integer. We don't want to focus on half a pixel, because then the video card will have to choose if a pixel shows up or not. This can cause issues with tiles, as sometimes a single pixel seem will show up when the card decided that the pixel shouldn't be rendered. My problem however was fixed by adding a tiny fraction to the rounded position. This was suggested by an old thread in a forum that I can't seem to find, otherwise I'd give the guy credit. That said, I can't exactly explain what is going on here, so if anyone has additional input on the reason behind this I'd love to hear. My guess is that it forces the video card to not fall into the conditions when it'd not render a pixel and always renders them, thus being correct in our case.
Take the camera's current position, round or floor the X,Y, Z coordinates to the nearest integer, then adjust by a small fraction.
this.transform.position = roundedPosition + new Vector3(-0.1f, -0.1f, 0);
And while we're on the subject of integer positions, make sure that your game objects have their positions rounded or floored to the nearest integer also. I found that animations can still jitter slightly even if you do all these things we've discussed. If the game object's position happens to be between two pixels, the renderer will make a guess as to which pixel should be used and sometimes it makes a bad choice. You can do this pretty easily in whatever function controls your characters movement. For example, just cast the new position's coordinates to integers prior to setting the transform.position field.
this.transform.position = new Vector3((int)newPosition.X, (int)newPosition.Y, (int)newPosition.Z);
Hopefully that helps out someone. If you know more solutions, find mistakes, or have questions, please feel free to post below.