Since I have started this platformer project I have used a pixel perfect script to help while I was developing it but now that the game has grown my pixel perfect script is getting in the way (illustrated with pictures) My problem is with the zoom factor of the Pixel Perfect Script; I want the field of view to be appropriate for the player (not too close or far away) This is zoom 1: too far away This is zoom 2: too close (want the player to have a good view of enemies coming) This is zoom 1.5 (I just added in the script) : This is the field of view im looking for but it disorients the pixels because 1.5 zoom doesn't fit with the pixel ratio calculations So I am just wondering if anybody has advice for PixelPerfect in Unity in regards to my zoom problem. Tuts, scripts, assets, etc... Any help will be AWESOME! Heres the script for a reference (Made by unity user Marrt): Code (CSharp): using UnityEngine; using System.Collections; using UnityEngine.UI; namespace MoreMountains.CorgiEngine { //PixelPerfect Camera in Unity //from Marrt //Spriteguy gathered from http://www.rpgmakervxace.net/topic/7926-8-direction-sprites/ public enum FollowMode { SnapToScreenPixel, SnapToArtPixel }; public class PixelPerfect : MonoBehaviour { public static PixelPerfect instance; //make sure only one instance of this script exists //PIXELPERFECT VIEWPORT public Camera cam; public Transform camAnchor; private Transform followTarget; public FollowMode snapMode = FollowMode.SnapToArtPixel; private float orthoSize; protected Transform _target; //pixel perfect & zoom public bool readAngle = true; private float camTilt = 53.13010235F; //yields: zStretch 1.25F yStretch= 1.6666666666F for pixel perfect rendering private float xStretch = 1F; //factor of X stretching is 1F private float zStretch; //factor of Z stretching applied to floor Tiles to make pixels square private float yStretch; //factor of Y stretching applied to wall Tiles to make pixels square public float pxPerUnit = 100F; //one Unity MeterSpans pxPerUnit pixels private float subPixelFactor = 2F; //changes with zoom Level, is used to position actor pefectly // //rounding Values, width of a single SCREEN-pixel in world coordinates, if you need ART-pixel snapping use RoundToArtPixelGrid() private bool pixelSnapOn = true; private float xSnap = 0F; private float ySnap = 0F; private float zSnap = 0F; void Start() { Zoom(1.5F); followTarget = GameManager.Instance.Player.transform; } void Awake() { instance = this; InitViewPort(readAngle); Zoom(1.5F); } void Update() { if (Input.GetKeyDown("1")) { Zoom(1F); } if (Input.GetKeyDown("2")) { Zoom(1.5F); } if (Input.GetKeyDown("3")) { Zoom(2F); } if (Input.GetKeyDown("4")) { Zoom(3F); } if (Input.GetKeyDown("5")) { Zoom(4F); } if (Input.GetKeyDown("6")) { Zoom(5F); } if (Input.GetKeyDown("7")) { Zoom(6F); } } private void InitViewPort(bool read) { if (!read) { //write tilt of cam camAnchor.transform.rotation = Quaternion.Euler(new Vector3(camTilt, 0F, 0F)); } //calculate stretch factors, they are 1 & infinity if environment is viewing from top yStretch = 1F / Mathf.Cos(camAnchor.rotation.eulerAngles.x * Mathf.Deg2Rad); zStretch = 1F / Mathf.Sin(camAnchor.rotation.eulerAngles.x * Mathf.Deg2Rad); yStretch = float.IsInfinity(yStretch) ? 1F : yStretch; zStretch = float.IsInfinity(zStretch) ? 1F : zStretch; print("zStretch" + zStretch + "\tyStretch" + yStretch + "\nlenght of a pixel:" + 1 / pxPerUnit); } private void Zoom(float subPxFactor) { print("Pixel 1:" + subPxFactor + "x" + subPxFactor); orthoSize = GetPixelPerfectOrthoSize(subPxFactor); //StartCoroutine(ZoomTransition()); cam.orthographicSize = orthoSize; } private IEnumerator ZoomTransition() { //during transition, we cannot be pixelperfect float timer = 1F; while (timer > 0F) { cam.orthographicSize = Mathf.Lerp(cam.orthographicSize, orthoSize, Time.deltaTime * 10F); timer -= Time.deltaTime; yield return null; } cam.orthographicSize = orthoSize; } private float GetPixelPerfectOrthoSize(float screenPixelPerSpritePixelWidth) { subPixelFactor = screenPixelPerSpritePixelWidth; //1 means 1ArtPixel = 1 ScreenPixel, 2 means 1 Art = 2x2 Screen float s = pxPerUnit * subPixelFactor; xSnap = xStretch / pxPerUnit / subPixelFactor; ySnap = yStretch / pxPerUnit / subPixelFactor; zSnap = zStretch / pxPerUnit / subPixelFactor; print("SceenPixelSnapingGrid:(" + xSnap + "," + ySnap + "," + zSnap); return cam.pixelHeight / s / 2F; } public static Vector3 RoundToScreenPixelGrid(Vector3 worldPos) { float xSnapArt = PixelPerfect.instance.xSnap; float ySnapArt = PixelPerfect.instance.ySnap; float zSnapArt = PixelPerfect.instance.zSnap; return new Vector3(Mathf.Round(worldPos.x / xSnapArt) * xSnapArt, Mathf.Round(worldPos.y / ySnapArt) * ySnapArt, Mathf.Round(worldPos.z / zSnapArt) * zSnapArt); } public static Vector3 RoundToArtPixelGrid(Vector3 worldPos) { float xSnapArt = PixelPerfect.instance.xSnap * PixelPerfect.instance.subPixelFactor; float ySnapArt = PixelPerfect.instance.ySnap * PixelPerfect.instance.subPixelFactor; float zSnapArt = PixelPerfect.instance.zSnap * PixelPerfect.instance.subPixelFactor; return new Vector3(Mathf.Round(worldPos.x / xSnapArt) * xSnapArt, Mathf.Round(worldPos.y / ySnapArt) * ySnapArt, Mathf.Round(worldPos.z / zSnapArt) * zSnapArt); } //UI-Button Functions public void PixelSnapOn(bool on) { pixelSnapOn = on; } public void ToggleSnappingMode(bool art) { if (art) { snapMode = FollowMode.SnapToArtPixel; } else { snapMode = FollowMode.SnapToScreenPixel; } } } }
Unity actually made a pretty neat blog for helping people get closer to being pixel perfect. Here is the link to the blog http://blogs.unity3d.com/2015/06/19/pixel-perfect-2d/ Hopefully this will help your project a little bit.
Thanks for the reply and I have seen that before but have decided to take a whack at it again and it didnt help with the problem. Unity just has a problem with 2D Pixel rendering. I cant get the exact FOV i would want. Considering going to Game Maker studio, awesome pixels and good games made from it such as risk of rain, splelunky, hotline: miami.
Depending on if you want to spend money for a good camera system you might want to check this out. https://www.assetstore.unity3d.com/en/#!/content/42095 It supports pixel perfect. It has a demo if you want to try it out. Other than that Pixel Perfect is a matter of trial and error between FOV and PPU. Either way, I wish you luck on your project.
I have created a simple camera script that I think does exactly what you want and it is free. You can get the ideal orthographic camera height (or width) and the script will chose the closest pixel-perfect size. So, this may result is zooming-in or zooming-out. You can even set a maximum allowed width or height, so that you don't zoom-out too much. Get it for free: https://www.assetstore.unity3d.com/en/#!/content/64563 You should keep in mind though that this problem exists in every engine. It's just there are specific pixel-perfect camera sizes, given a screen resolution.
I want to share some of my insights and mildly slap you on the butt for using Zoom(1.5F): There is no "Pixel-Perfect" if you use a Zoom() level of 1.5f as you have noticed. I don't know if Zoom() was the best name for that function because Zoom(1.5F) just means that 1x1-ArtworkPixel will be shown on 1.5x1.5 ScreenPixels. I refer to this factor as subPxFactor. That this non integer subPxFactor yields varying line-thicknesses is obvious, look at your picture for example: I guess that all letters have a line width of 1px in your artwork. But on screen this line becomes sometimes two, and sometimes one. Because at this "zoom", each 2nd pixel on your screen samples its point filtered color-value at the very knife edge between two pixels of the Texture. Therefore floating point inaccuracies or some other voodoo decide which to take, you can try this in MS-Paint: My approach: What you have to do is consider all possible resolutions that you might have to cater. Create a function that will yield you a subPxFactor that creates the lowest deviation in orthographic viewport size across all your supported resolutions Then visualize it. Consider this DebugScreen of our project for example. The outer frame shows exactly what a viewport at 1920x1080 resp. 960x540 would contain, the others are showing other resolutions in above pic (low budget samsung phones and iphones). The resolution varies, but the area viewed is nearly the same and each one is Pixel-Perfect. What varies between them is the subPxFactor. 1920x1080 has a subPxFactor of 6, while 960x540 has 3 - but those have the same border and viewport-size. You might argue "Well, then your game offers advantages for players with specific resolution devices". Of course, but it could also be said for different aspect ratios (16:10 vs 16:9) in most cases. If you want pixel-perfection across multiple resolutions, your game has to be made in a way that it doesn't penalize or reward any of the possible resolutions. If your game is completely dependent on seeing the incoming fire ball from the edge of the screen at resolution-A 200ms prior to resolution-B... i guess your game mechanics aren't in sync with the actual representation of your game. We were very careful in that regard because our game features melee and ranged. We use a nominal subject-size of 24x24px and we target lower resolutions like 960x540(=1080p/2). At that resolution we get subPixelFactors ranging from 2 to 4 when matching the results with our compromise-comfort-zone between overview and detail at melee range. We arrived at 24x24 after asking the very question you asked in OP: How can i solve non integer subPxFactors to be pixel-perfect? You cannot, the answer is: You have to design your artwork from the beginning with resolution in mind to avoid needing non-integer subPixelFactors. 24x24px was the best compromise for us and now we design for that size. We only arrived there after weeks of tinkering and checking what view-area would suit our game. You too should make some mockup characters with different sizes (like 16x16, 24x24, 32x32...) and THEN check which subPxFactor-subjectSize-combination is feasible for your gameplay. Edit: Improved wording