This commit is contained in:
2021-06-13 10:28:03 +02:00
parent eb70603c85
commit df2d24cbd3
7487 changed files with 943244 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
# Changelog
## [5.0.0] - 2021-03-17
### Changed
- Update version for release
## [5.0.0-pre.2] - 2021-01-16
### Changed
- Update license file
## [5.0.0-pre.1] - 2020-10-30
### Changed
- Version bump for Unity 2021.1
## [4.0.1] - 2020-06-10
### Fixed
- Fixed the broken documentation URL of the Component.
## [4.0.0] - 2020-05-11
### Changed
- Version bump for Unity 2020.2
## [3.0.2] - 2020-03-28
### Fixed
- Fixed an issue where Cinemachine Pixel Perfect Extension didn't work when CinemachineBrain Update Method is anything other than Late Update.
## [3.0.1] - 2019-11-14
### Changed
- Deploy samples as individual files.
- Made the editor class internal.
- Changed License file
## [3.0.0] - 2019-11-06
### Changed
- Update version number for Unity 2020.1
## [2.0.3] - 2019-11-06
### Changed
- Deprecated the CinemachinePixelPerfect extension. Use the one from Cinemachine v2.4 instead.
## [2.0.2] - 2019-07-13
### Changed
- Mark package to support Unity 2019.3.0a10 onwards.
## [2.0.1] - 2019-07-12
### Changed
- Deploy Samples as UnityPackage.
## [2.0.0] - 2019-07-05
### Added
- Added CinemachinePixelPerfect, a Cinemachine Virtual Camera Extension that solves some compatibility issues between Cinemachine and Pixel Perfect Camera.
### Fixed
- Fixed an issue where recompiling scripts while a Pixel Perfect Camera is running would cause null reference exeptions.
## [1.0.1-preview] - 2018-06-19
### Changed
- Disabled "Run In Edit Mode" button for presets and inactive game objects.
- "Run In Edit Mode" is now automatically disabled when you enter play mode.
### Fixed
- Fixed an issue where some UI text was missing from the preset inspector.
- Addressed a performance warning you could get when you target mobile platforms.
## [1.0.0-preview] - 2018-05-03
### This is the first preview release of *Unity Package \<2D Pixel Perfect\>*.
*This initial release contains a Pixel Perfect Camera component which ensures your pixel art remains crisp and clear at different resolutions, and stable in motion.*

View File

@@ -0,0 +1,11 @@
namespace UnityEditor.U2D
{
[CustomEditor(typeof(UnityEngine.U2D.CinemachinePixelPerfect)), CanEditMultipleObjects]
internal class CinemachinePixelPerfectEditor : Editor
{
public override void OnInspectorGUI()
{
EditorGUILayout.HelpBox("This Cinemachine extension is now deprecated and doesn't function properly. Instead, use the one from Cinemachine v2.4.0 or newer.", MessageType.Error);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,72 @@
using System;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.U2D;
namespace UnityEditor.U2D
{
static class GameObjectCreation
{
const int k_PixelPerfectCameraGameObjectMenuPriority = 5;
[MenuItem("GameObject/2D Object/Pixel Perfect Camera", priority = k_PixelPerfectCameraGameObjectMenuPriority)]
static void GameObjectCreatePixelPerfectCamera(MenuCommand menuCommand)
{
var go = CreateGameObject("Pixel Perfect Camera", menuCommand, new []{typeof(PixelPerfectCamera)});
go.GetComponent<PixelPerfectCamera>().pixelSnapping = true;
}
static public GameObject CreateGameObject(string name, MenuCommand menuCommand, params Type[] components)
{
var parent = menuCommand.context as GameObject;
var newGO = ObjectFactory.CreateGameObject(name, components);
newGO.name = name;
Selection.activeObject = newGO;
Place(newGO, parent);
if (EditorSettings.defaultBehaviorMode == EditorBehaviorMode.Mode2D)
{
var position = newGO.transform.position;
position.z = 0;
newGO.transform.position = position;
}
Undo.RegisterCreatedObjectUndo(newGO, string.Format("Create {0}", name));
return newGO;
}
internal static void Place(GameObject go, GameObject parentTransform)
{
if (parentTransform != null)
{
var transform = go.transform;
Undo.SetTransformParent(transform, parentTransform.transform, "Reparenting");
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
go.layer = parentTransform.gameObject.layer;
if (parentTransform.GetComponent<RectTransform>())
ObjectFactory.AddComponent<RectTransform>(go);
}
else
{
PlaceGameObjectInFrontOfSceneView(go);
StageUtility.PlaceGameObjectInCurrentStage(go); // may change parent
}
// Only at this point do we know the actual parent of the object and can modify its name accordingly.
GameObjectUtility.EnsureUniqueNameForSibling(go);
Undo.SetCurrentGroupName("Create " + go.name);
Selection.activeGameObject = go;
}
internal static void PlaceGameObjectInFrontOfSceneView(GameObject go)
{
var view = SceneView.lastActiveSceneView;
if (view != null)
{
view.MoveToView(go.transform);
}
}
}
}

View File

@@ -0,0 +1,256 @@
using System;
using UnityEngine;
using UnityEngine.U2D;
namespace UnityEditor.U2D
{
[CustomEditor(typeof(PixelPerfectCamera))]
class PixelPerfectCameraEditor : Editor
{
private class Style
{
public GUIContent x = new GUIContent("X");
public GUIContent y = new GUIContent("Y");
public GUIContent assetsPPU = new GUIContent("Assets Pixels Per Unit", "The amount of pixels that make up one unit of the Scene. Set this value to match the PPU value of Sprites in the Scene.");
public GUIContent refRes = new GUIContent("Reference Resolution", "The original resolution the Assets are designed for.");
public GUIContent upscaleRT = new GUIContent("Upscale Render Texture", "If enabled, the Scene is rendered as close as possible to the Reference Resolution while maintaining the screen aspect ratio, then upscaled to fit the full screen.");
public GUIContent pixelSnapping = new GUIContent("Pixel Snapping", "If enabled, Sprite Renderers are snapped to a grid in world space at render-time. Grid size is based on the Assets Pixels Per Unit value. This does not affect GameObjects' Transform positions.");
public GUIContent cropFrame = new GUIContent("Crop Frame", "Crops the viewport to match the Reference Resolution, along the checked axis. Black bars will be added to fit the screen aspect ratio.");
public GUIContent stretchFill = new GUIContent("Stretch Fill", "If enabled, expands the viewport to fit the screen resolution while maintaining the viewport aspect ratio.");
public GUIContent currentPixelRatio = new GUIContent("Current Pixel Ratio", "Ratio of the rendered Sprites compared to their original size.");
public GUIContent runInEditMode = new GUIContent("Run In Edit Mode", "Enable this to preview Camera setting changes in Edit Mode. This will cause constant changes to the Scene while active.");
public string srpWarning = "Pixel Perfect Camera in the 2D Pixel Perfect package isn't compatible with Scriptable Render Pipeline. If you are using the Lightweight Render Pipeline, you can swap this for the Pixel Perfect Camera component that ships with LWRP.";
public GUIStyle centeredLabel;
public Style()
{
centeredLabel = new GUIStyle(EditorStyles.label);
centeredLabel.alignment = TextAnchor.MiddleCenter;
}
}
private static Style m_Style;
private const float k_SingleLetterLabelWidth = 15.0f;
private const float k_DottedLineSpacing = 2.5f;
private SerializedProperty m_AssetsPPU;
private SerializedProperty m_RefResX;
private SerializedProperty m_RefResY;
private SerializedProperty m_UpscaleRT;
private SerializedProperty m_PixelSnapping;
private SerializedProperty m_CropFrameY;
private SerializedProperty m_CropFrameX;
private SerializedProperty m_StretchFill;
private Vector2 m_GameViewSize = Vector2.zero;
private GUIContent m_CurrentPixelRatioValue;
private void LazyInit()
{
if (m_Style == null)
m_Style = new Style();
if (m_CurrentPixelRatioValue == null)
m_CurrentPixelRatioValue = new GUIContent();
}
public void OnEnable()
{
m_AssetsPPU = serializedObject.FindProperty("m_AssetsPPU");
m_RefResX = serializedObject.FindProperty("m_RefResolutionX");
m_RefResY = serializedObject.FindProperty("m_RefResolutionY");
m_UpscaleRT = serializedObject.FindProperty("m_UpscaleRT");
m_PixelSnapping = serializedObject.FindProperty("m_PixelSnapping");
m_CropFrameY = serializedObject.FindProperty("m_CropFrameY");
m_CropFrameX = serializedObject.FindProperty("m_CropFrameX");
m_StretchFill = serializedObject.FindProperty("m_StretchFill");
}
public override bool RequiresConstantRepaint()
{
PixelPerfectCamera obj = target as PixelPerfectCamera;
if (obj == null || !obj.enabled)
return false;
// If game view size changes, we need to force a repaint of the inspector as the pixel ratio value may change accordingly.
Vector2 gameViewSize = Handles.GetMainGameViewSize();
if (gameViewSize != m_GameViewSize)
{
m_GameViewSize = gameViewSize;
return true;
}
else
return false;
}
public override void OnInspectorGUI()
{
LazyInit();
if (UnityEngine.Rendering.GraphicsSettings.renderPipelineAsset != null)
EditorGUILayout.HelpBox(m_Style.srpWarning, MessageType.Warning);
float originalLabelWidth = EditorGUIUtility.labelWidth;
serializedObject.Update();
EditorGUILayout.PropertyField(m_AssetsPPU, m_Style.assetsPPU);
if (m_AssetsPPU.intValue <= 0)
m_AssetsPPU.intValue = 1;
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PrefixLabel(m_Style.refRes);
EditorGUIUtility.labelWidth = k_SingleLetterLabelWidth * (EditorGUI.indentLevel + 1);
EditorGUILayout.PropertyField(m_RefResX, m_Style.x);
if (m_RefResX.intValue <= 0)
m_RefResX.intValue = 1;
EditorGUILayout.PropertyField(m_RefResY, m_Style.y);
if (m_RefResY.intValue <= 0)
m_RefResY.intValue = 1;
EditorGUIUtility.labelWidth = originalLabelWidth;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(m_UpscaleRT, m_Style.upscaleRT);
if (!m_UpscaleRT.boolValue)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_PixelSnapping, m_Style.pixelSnapping);
EditorGUI.indentLevel--;
}
EditorGUILayout.BeginHorizontal();
{
EditorGUILayout.PrefixLabel(m_Style.cropFrame);
EditorGUIUtility.labelWidth = k_SingleLetterLabelWidth * (EditorGUI.indentLevel + 1);
EditorGUILayout.PropertyField(m_CropFrameX, m_Style.x, GUILayout.MaxWidth(40.0f));
EditorGUILayout.PropertyField(m_CropFrameY, m_Style.y);
EditorGUIUtility.labelWidth = originalLabelWidth;
}
EditorGUILayout.EndHorizontal();
if (m_CropFrameY.boolValue && m_CropFrameX.boolValue)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(m_StretchFill, m_Style.stretchFill);
EditorGUI.indentLevel--;
}
serializedObject.ApplyModifiedProperties();
PixelPerfectCamera obj = target as PixelPerfectCamera;
if (obj != null)
{
EditorGUI.BeginDisabledGroup(EditorApplication.isPlaying || !obj.isActiveAndEnabled);
EditorGUI.BeginChangeCheck();
bool runInEditMode = EditorGUILayout.Toggle(obj.runInEditMode, GUI.skin.button, GUILayout.Width(110.0f));
GUI.Label(GUILayoutUtility.GetLastRect(), m_Style.runInEditMode, m_Style.centeredLabel);
if (EditorGUI.EndChangeCheck())
{
obj.runInEditMode = runInEditMode;
if (runInEditMode)
obj.GetComponent<Camera>().Render();
else
obj.OnDisable();
}
EditorGUI.EndDisabledGroup();
if (obj.isActiveAndEnabled && (EditorApplication.isPlaying || obj.runInEditMode))
{
if (Event.current.type == EventType.Layout)
m_CurrentPixelRatioValue.text = string.Format("{0}:1", obj.pixelRatio);
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.LabelField(m_Style.currentPixelRatio, m_CurrentPixelRatioValue);
EditorGUI.EndDisabledGroup();
}
}
}
void OnSceneGUI()
{
PixelPerfectCamera obj = target as PixelPerfectCamera;
if (obj == null)
return;
Camera camera = obj.GetComponent<Camera>();
// Show a green rect in scene view that represents the visible area when the pixel perfect correction takes effect in play mode.
Vector2 gameViewSize = Handles.GetMainGameViewSize();
int gameViewWidth = (int)gameViewSize.x;
int gameViewHeight = (int)gameViewSize.y;
int zoom = Math.Max(1, Math.Min(gameViewHeight / obj.refResolutionY, gameViewWidth / obj.refResolutionX));
float verticalOrthoSize;
float horizontalOrthoSize;
if (obj.cropFrameY && obj.cropFrameX)
{
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
horizontalOrthoSize = verticalOrthoSize * ((float)obj.refResolutionX / obj.refResolutionY);
}
else if (obj.cropFrameY)
{
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
horizontalOrthoSize = verticalOrthoSize * ((float)gameViewWidth / (zoom * obj.refResolutionY));
}
else if (obj.cropFrameX)
{
horizontalOrthoSize = obj.refResolutionX * 0.5f / obj.assetsPPU;
verticalOrthoSize = horizontalOrthoSize / (zoom * obj.refResolutionX / (float)gameViewHeight);
}
else
{
verticalOrthoSize = gameViewHeight * 0.5f / (zoom * obj.assetsPPU);
horizontalOrthoSize = verticalOrthoSize * camera.aspect;
}
Handles.color = Color.green;
Vector3 cameraPosition = camera.transform.position;
Vector3 p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
Vector3 p2 = cameraPosition + new Vector3(horizontalOrthoSize, verticalOrthoSize, 0.0f);
Handles.DrawLine(p1, p2);
p1 = cameraPosition + new Vector3(horizontalOrthoSize, -verticalOrthoSize, 0.0f);
Handles.DrawLine(p2, p1);
p2 = cameraPosition + new Vector3(-horizontalOrthoSize, -verticalOrthoSize, 0.0f);
Handles.DrawLine(p1, p2);
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
Handles.DrawLine(p2, p1);
// Show a green dotted rect in scene view that represents the area defined by the reference resolution.
horizontalOrthoSize = obj.refResolutionX * 0.5f / obj.assetsPPU;
verticalOrthoSize = obj.refResolutionY * 0.5f / obj.assetsPPU;
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
p2 = cameraPosition + new Vector3(horizontalOrthoSize, verticalOrthoSize, 0.0f);
Handles.DrawDottedLine(p1, p2, k_DottedLineSpacing);
p1 = cameraPosition + new Vector3(horizontalOrthoSize, -verticalOrthoSize, 0.0f);
Handles.DrawDottedLine(p2, p1, k_DottedLineSpacing);
p2 = cameraPosition + new Vector3(-horizontalOrthoSize, -verticalOrthoSize, 0.0f);
Handles.DrawDottedLine(p1, p2, k_DottedLineSpacing);
p1 = cameraPosition + new Vector3(-horizontalOrthoSize, verticalOrthoSize, 0.0f);
Handles.DrawDottedLine(p2, p1, k_DottedLineSpacing);
}
}
}

View File

@@ -0,0 +1,16 @@
{
"name": "Unity.2D.PixelPerfect.Editor",
"references": [
"GUID:476f7c6c6dfeed041b063446a926e656"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": []
}

View File

@@ -0,0 +1,5 @@
com.unity.2d.pixel-perfect copyright © 2020 Unity Technologies ApS
Licensed under the Unity Companion License for Unity-dependent projects (see https://unity3d.com/legal/licenses/unity_companion_license).
Unless expressly provided otherwise, the Software under this license is made available strictly on an “AS IS” BASIS WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. Please review the license for details on these and other terms and conditions.

View File

@@ -0,0 +1,5 @@
# 2D Pixel Perfect
Quick start guide:
1. Add *Pixel Perfect Camera* component to your main camera.
2. Set *Assets Pixels Per Unit* and *Reference Resolution*.
3. Enter Play Mode and see the result.

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Unity.2D.PixelPerfect.Tests")]
[assembly: InternalsVisibleTo("Unity.2D.PixelPerfect.Editor")]

View File

@@ -0,0 +1,17 @@
namespace UnityEngine.U2D
{
/// <summary>
/// (Deprecated) An add-on module for Cinemachine Virtual Camera that tweaks the orthographic size
/// of the virtual camera. It detects the presence of the Pixel Perfect Camera component and use the
/// settings from that Pixel Perfect Camera to correct the orthographic size so that pixel art
/// sprites would appear pixel perfect when the virtual camera becomes live.
/// </summary>
[AddComponentMenu("")] // Hide in menu
public class CinemachinePixelPerfect : MonoBehaviour
{
void OnEnable()
{
Debug.LogError("CinemachinePixelPerfect is now deprecated and doesn't function properly. Instead, use the one from Cinemachine v2.4.0 or newer.");
}
}
}

View File

@@ -0,0 +1,301 @@
namespace UnityEngine.U2D
{
/// <summary>
/// The Pixel Perfect Camera component ensures your pixel art remains crisp and clear at different resolutions, and stable in motion.
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("Rendering/Pixel Perfect Camera")]
[RequireComponent(typeof(Camera))]
[HelpURL("https://docs.unity3d.com/Packages/com.unity.2d.pixel-perfect@latest/index.html?subfolder=/manual/index.html%23properties")]
public class PixelPerfectCamera : MonoBehaviour, IPixelPerfectCamera
{
/// <summary>
/// Match this value to to the Pixels Per Unit values of all Sprites within the Scene.
/// </summary>
public int assetsPPU { get { return m_AssetsPPU; } set { m_AssetsPPU = value > 0 ? value : 1; } }
/// <summary>
/// The original horizontal resolution your Assets are designed for.
/// </summary>
public int refResolutionX { get { return m_RefResolutionX; } set { m_RefResolutionX = value > 0 ? value : 1; } }
/// <summary>
/// Original vertical resolution your Assets are designed for.
/// </summary>
public int refResolutionY { get { return m_RefResolutionY; } set { m_RefResolutionY = value > 0 ? value : 1; } }
/// <summary>
/// Set to true to have the Scene rendered to a temporary texture set as close as possible to the Reference Resolution,
/// while maintaining the full screen aspect ratio. This temporary texture is then upscaled to fit the full screen.
/// </summary>
public bool upscaleRT { get { return m_UpscaleRT; } set { m_UpscaleRT = value; } }
/// <summary>
/// Set to true to prevent subpixel movement and make Sprites appear to move in pixel-by-pixel increments.
/// Only applicable when upscaleRT is false.
/// </summary>
public bool pixelSnapping { get { return m_PixelSnapping; } set { m_PixelSnapping = value; } }
/// <summary>
/// Set to true to crop the viewport with black bars to match refResolutionX in the horizontal direction.
/// </summary>
public bool cropFrameX { get { return m_CropFrameX; } set { m_CropFrameX = value; } }
/// <summary>
/// Set to true to crop the viewport with black bars to match refResolutionY in the vertical direction.
/// </summary>
public bool cropFrameY { get { return m_CropFrameY; } set { m_CropFrameY = value; } }
/// <summary>
/// Set to true to expand the viewport to fit the screen resolution while maintaining the viewport's aspect ratio.
/// Only applicable when both cropFrameX and cropFrameY are true.
/// </summary>
public bool stretchFill { get { return m_StretchFill; } set { m_StretchFill = value; } }
/// <summary>
/// Ratio of the rendered Sprites compared to their original size (readonly).
/// </summary>
public int pixelRatio
{
get
{
if (m_CinemachineCompatibilityMode)
{
if (m_UpscaleRT)
return m_Internal.zoom * m_Internal.cinemachineVCamZoom;
else
return m_Internal.cinemachineVCamZoom;
}
else
{
return m_Internal.zoom;
}
}
}
/// <summary>
/// Round a arbitrary position to an integer pixel position. Works in world space.
/// </summary>
/// <param name="position"> The position you want to round.</param>
/// <returns>
/// The rounded pixel position.
/// Depending on the values of upscaleRT and pixelSnapping, it could be a screen pixel position or an art pixel position.
/// </returns>
public Vector3 RoundToPixel(Vector3 position)
{
float unitsPerPixel = m_Internal.unitsPerPixel;
if (unitsPerPixel == 0.0f)
return position;
Vector3 result;
result.x = Mathf.Round(position.x / unitsPerPixel) * unitsPerPixel;
result.y = Mathf.Round(position.y / unitsPerPixel) * unitsPerPixel;
result.z = Mathf.Round(position.z / unitsPerPixel) * unitsPerPixel;
return result;
}
/// <summary>
/// Find a pixel-perfect orthographic size as close to targetOrthoSize as possible. Used by Cinemachine to solve compatibility issues with Pixel Perfect Camera.
/// </summary>
/// <param name="targetOrthoSize">Orthographic size from the live Cinemachine Virtual Camera.</param>
/// <returns>The corrected orthographic size.</returns>
public float CorrectCinemachineOrthoSize(float targetOrthoSize)
{
m_CinemachineCompatibilityMode = true;
if (m_Internal == null)
return targetOrthoSize;
else
return m_Internal.CorrectCinemachineOrthoSize(targetOrthoSize);
}
[SerializeField]
int m_AssetsPPU = 100;
[SerializeField]
int m_RefResolutionX = 320;
[SerializeField]
int m_RefResolutionY = 180;
[SerializeField]
bool m_UpscaleRT = false;
[SerializeField]
bool m_PixelSnapping = false;
[SerializeField]
bool m_CropFrameX = false;
[SerializeField]
bool m_CropFrameY = false;
[SerializeField]
bool m_StretchFill = false;
Camera m_Camera;
PixelPerfectCameraInternal m_Internal;
bool m_CinemachineCompatibilityMode;
// Snap camera position to pixels using Camera.worldToCameraMatrix.
void PixelSnap()
{
Vector3 cameraPosition = m_Camera.transform.position;
Vector3 roundedCameraPosition = RoundToPixel(cameraPosition);
Vector3 offset = roundedCameraPosition - cameraPosition;
offset.z = -offset.z;
Matrix4x4 offsetMatrix = Matrix4x4.TRS(-offset, Quaternion.identity, new Vector3(1.0f, 1.0f, -1.0f));
m_Camera.worldToCameraMatrix = offsetMatrix * m_Camera.transform.worldToLocalMatrix;
}
void Awake()
{
m_Camera = GetComponent<Camera>();
m_Internal = new PixelPerfectCameraInternal(this);
m_Internal.originalOrthoSize = m_Camera.orthographicSize;
m_Internal.hasPostProcessLayer = GetComponent("PostProcessLayer") != null; // query the component by name to avoid hard dependency
if (m_Camera.targetTexture != null)
Debug.LogWarning("Render to texture is not supported by Pixel Perfect Camera.", m_Camera);
}
void LateUpdate()
{
m_Internal.CalculateCameraProperties(Screen.width, Screen.height);
// To be effective immediately this frame, forceIntoRenderTexture should be set before any camera rendering callback.
// An exception of this is when the editor is paused, where we call LateUpdate() manually in OnPreCall().
// In this special case, you'll see one frame of glitch when toggling renderUpscaling on and off.
m_Camera.forceIntoRenderTexture = m_Internal.hasPostProcessLayer || m_Internal.useOffscreenRT;
}
void OnPreCull()
{
#if UNITY_EDITOR
// LateUpdate() is not called while the editor is paused, but OnPreCull() is.
// So call LateUpdate() manually here.
if (UnityEditor.EditorApplication.isPaused)
LateUpdate();
#endif
PixelSnap();
if (m_Internal.pixelRect != Rect.zero)
m_Camera.pixelRect = m_Internal.pixelRect;
else
m_Camera.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
// In Cinemachine compatibility mode the control over orthographic size should
// be given to the virtual cameras, whose orthographic sizes will be corrected to
// be pixel-perfect. This way when there's blending between virtual cameras, we
// can have temporary not-pixel-perfect but smooth transitions.
if (!m_CinemachineCompatibilityMode)
{
m_Camera.orthographicSize = m_Internal.orthoSize;
}
}
void OnPreRender()
{
// Clear the screen to black so that we can see black bars.
// Need to do it before anything is drawn if we're rendering directly to the screen.
if (m_Internal.cropFrameXOrY && !m_Camera.forceIntoRenderTexture && !m_Camera.allowMSAA)
GL.Clear(false, true, Color.black);
PixelPerfectRendering.pixelSnapSpacing = m_Internal.unitsPerPixel;
}
void OnPostRender()
{
PixelPerfectRendering.pixelSnapSpacing = 0.0f;
// Clear the screen to black so that we can see black bars.
// If a temporary offscreen RT is used, we do the clear after we're done with that RT to avoid an unnecessary RT switch.
if (m_Camera.activeTexture != null)
{
Graphics.SetRenderTarget(null as RenderTexture);
GL.Viewport(new Rect(0.0f, 0.0f, Screen.width, Screen.height));
GL.Clear(false, true, Color.black);
}
if (!m_Internal.useOffscreenRT)
return;
RenderTexture activeRT = m_Camera.activeTexture;
if (activeRT != null)
activeRT.filterMode = m_Internal.useStretchFill ? FilterMode.Bilinear : FilterMode.Point;
m_Camera.pixelRect = m_Internal.CalculatePostRenderPixelRect(m_Camera.aspect, Screen.width, Screen.height);
}
void OnEnable()
{
m_CinemachineCompatibilityMode = false;
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
UnityEditor.EditorApplication.playModeStateChanged += OnPlayModeChanged;
#endif
}
internal void OnDisable()
{
m_Camera.rect = new Rect(0.0f, 0.0f, 1.0f, 1.0f);
m_Camera.orthographicSize = m_Internal.originalOrthoSize;
m_Camera.forceIntoRenderTexture = m_Internal.hasPostProcessLayer;
m_Camera.ResetAspect();
m_Camera.ResetWorldToCameraMatrix();
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying)
UnityEditor.EditorApplication.playModeStateChanged -= OnPlayModeChanged;
#endif
}
#if DEVELOPMENT_BUILD || UNITY_EDITOR
// Show on-screen warning about invalid render resolutions.
void OnGUI()
{
#if UNITY_EDITOR
if (!UnityEditor.EditorApplication.isPlaying && !runInEditMode)
return;
#endif
Color oldColor = GUI.color;
GUI.color = Color.red;
Vector2Int renderResolution = Vector2Int.zero;
renderResolution.x = m_Internal.useOffscreenRT ? m_Internal.offscreenRTWidth : m_Camera.pixelWidth;
renderResolution.y = m_Internal.useOffscreenRT ? m_Internal.offscreenRTHeight : m_Camera.pixelHeight;
if (renderResolution.x % 2 != 0 || renderResolution.y % 2 != 0)
{
string warning = string.Format("Rendering at an odd-numbered resolution ({0} * {1}). Pixel Perfect Camera may not work properly in this situation.", renderResolution.x, renderResolution.y);
GUILayout.Box(warning);
}
if (Screen.width < refResolutionX || Screen.height < refResolutionY)
{
GUILayout.Box("Screen resolution is smaller than the reference resolution. Image may appear stretched or cropped.");
}
GUI.color = oldColor;
}
#endif
#if UNITY_EDITOR
void OnPlayModeChanged(UnityEditor.PlayModeStateChange state)
{
// Stop running in edit mode when entering play mode.
if (state == UnityEditor.PlayModeStateChange.ExitingEditMode)
{
runInEditMode = false;
OnDisable();
}
}
#endif
}
}

View File

@@ -0,0 +1,236 @@
using System;
namespace UnityEngine.U2D
{
internal interface IPixelPerfectCamera
{
int assetsPPU { get; set; }
int refResolutionX { get; set; }
int refResolutionY { get; set; }
bool upscaleRT { get; set; }
bool pixelSnapping { get; set; }
bool cropFrameX { get; set; }
bool cropFrameY { get; set; }
bool stretchFill { get; set; }
}
[Serializable]
internal class PixelPerfectCameraInternal : ISerializationCallbackReceiver
{
// Case 1061634:
// In order for this class to survive hot reloading, we need to make the fields serializable.
// Unity can't serialize an interface object, but does properly serialize UnityEngine.Object.
// So we cast the reference to PixelPerfectCamera (which inherits UnityEngine.Object)
// before serialization happens, and restore the interface reference after deserialization.
[NonSerialized]
IPixelPerfectCamera m_Component;
PixelPerfectCamera m_SerializableComponent;
internal float originalOrthoSize;
internal bool hasPostProcessLayer;
internal bool cropFrameXAndY = false;
internal bool cropFrameXOrY = false;
internal bool useStretchFill = false;
internal int zoom = 1;
internal bool useOffscreenRT = false;
internal int offscreenRTWidth = 0;
internal int offscreenRTHeight = 0;
internal Rect pixelRect = Rect.zero;
internal float orthoSize = 1.0f;
internal float unitsPerPixel = 0.0f;
internal int cinemachineVCamZoom = 1;
internal PixelPerfectCameraInternal(IPixelPerfectCamera component)
{
m_Component = component;
}
public void OnBeforeSerialize()
{
m_SerializableComponent = m_Component as PixelPerfectCamera;
}
public void OnAfterDeserialize()
{
if (m_SerializableComponent != null)
m_Component = m_SerializableComponent;
}
internal void CalculateCameraProperties(int screenWidth, int screenHeight)
{
int assetsPPU = m_Component.assetsPPU;
int refResolutionX = m_Component.refResolutionX;
int refResolutionY = m_Component.refResolutionY;
bool upscaleRT = m_Component.upscaleRT;
bool pixelSnapping = m_Component.pixelSnapping;
bool cropFrameX = m_Component.cropFrameX;
bool cropFrameY = m_Component.cropFrameY;
bool stretchFill = m_Component.stretchFill;
cropFrameXAndY = cropFrameY && cropFrameX;
cropFrameXOrY = cropFrameY || cropFrameX;
useStretchFill = cropFrameXAndY && stretchFill;
// zoom level (PPU scale)
int verticalZoom = screenHeight / refResolutionY;
int horizontalZoom = screenWidth / refResolutionX;
zoom = Math.Max(1, Math.Min(verticalZoom, horizontalZoom));
// off-screen RT
useOffscreenRT = false;
offscreenRTWidth = 0;
offscreenRTHeight = 0;
if (cropFrameXOrY)
{
if (!upscaleRT)
{
if (useStretchFill)
{
useOffscreenRT = true;
offscreenRTWidth = zoom * refResolutionX;
offscreenRTHeight = zoom * refResolutionY;
}
}
else
{
useOffscreenRT = true;
if (cropFrameXAndY)
{
offscreenRTWidth = refResolutionX;
offscreenRTHeight = refResolutionY;
}
else if (cropFrameY)
{
offscreenRTWidth = screenWidth / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
offscreenRTHeight = refResolutionY;
}
else // crop frame X
{
offscreenRTWidth = refResolutionX;
offscreenRTHeight = screenHeight / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
}
}
}
else if (upscaleRT && zoom > 1)
{
useOffscreenRT = true;
offscreenRTWidth = screenWidth / zoom / 2 * 2; // Make sure it's an even number by / 2 * 2.
offscreenRTHeight = screenHeight / zoom / 2 * 2;
}
// viewport
pixelRect = Rect.zero;
if (cropFrameXOrY && !upscaleRT && !useStretchFill)
{
if (cropFrameXAndY)
{
pixelRect.width = zoom * refResolutionX;
pixelRect.height = zoom * refResolutionY;
}
else if (cropFrameY)
{
pixelRect.width = screenWidth;
pixelRect.height = zoom * refResolutionY;
}
else // crop frame X
{
pixelRect.width = zoom * refResolutionX;
pixelRect.height = screenHeight;
}
pixelRect.x = (screenWidth - (int)pixelRect.width) / 2;
pixelRect.y = (screenHeight - (int)pixelRect.height) / 2;
}
else if (useOffscreenRT)
{
// When Camera.forceIntoRenderTexture is true, the size of the internal RT is determined by VP size.
// That's why we set the VP size to be (m_OffscreenRTWidth, m_OffscreenRTHeight) here.
pixelRect = new Rect(0.0f, 0.0f, offscreenRTWidth, offscreenRTHeight);
}
// orthographic size
if (cropFrameY)
orthoSize = (refResolutionY * 0.5f) / assetsPPU;
else if (cropFrameX)
{
float aspect = (pixelRect == Rect.zero) ? (float)screenWidth / screenHeight : pixelRect.width / pixelRect.height;
orthoSize = ((refResolutionX / aspect) * 0.5f) / assetsPPU;
}
else if (upscaleRT && zoom > 1)
orthoSize = (offscreenRTHeight * 0.5f) / assetsPPU;
else
{
float pixelHeight = (pixelRect == Rect.zero) ? screenHeight : pixelRect.height;
orthoSize = (pixelHeight * 0.5f) / (zoom * assetsPPU);
}
// Camera pixel grid spacing
if (upscaleRT || (!upscaleRT && pixelSnapping))
unitsPerPixel = 1.0f / assetsPPU;
else
unitsPerPixel = 1.0f / (zoom * assetsPPU);
}
internal Rect CalculatePostRenderPixelRect(float cameraAspect, int screenWidth, int screenHeight)
{
// This VP is used when the internal temp RT is blitted back to screen.
Rect pixelRect = new Rect();
if (useStretchFill)
{
// stretch (fit either width or height)
float screenAspect = (float)screenWidth / screenHeight;
if (screenAspect > cameraAspect)
{
pixelRect.height = screenHeight;
pixelRect.width = screenHeight * cameraAspect;
pixelRect.x = (screenWidth - (int)pixelRect.width) / 2;
pixelRect.y = 0;
}
else
{
pixelRect.width = screenWidth;
pixelRect.height = screenWidth / cameraAspect;
pixelRect.y = (screenHeight - (int)pixelRect.height) / 2;
pixelRect.x = 0;
}
}
else
{
// center
pixelRect.height = zoom * offscreenRTHeight;
pixelRect.width = zoom * offscreenRTWidth;
pixelRect.x = (screenWidth - (int)pixelRect.width) / 2;
pixelRect.y = (screenHeight - (int)pixelRect.height) / 2;
}
return pixelRect;
}
// Find a pixel-perfect orthographic size as close to targetOrthoSize as possible.
internal float CorrectCinemachineOrthoSize(float targetOrthoSize)
{
float correctedOrthoSize;
if (m_Component.upscaleRT)
{
cinemachineVCamZoom = Math.Max(1, Mathf.RoundToInt(orthoSize / targetOrthoSize));
correctedOrthoSize = orthoSize / cinemachineVCamZoom;
}
else
{
cinemachineVCamZoom = Math.Max(1, Mathf.RoundToInt(zoom * orthoSize / targetOrthoSize));
correctedOrthoSize = zoom * orthoSize / cinemachineVCamZoom;
}
// In this case the actual zoom level is cinemachineVCamZoom instead of zoom.
if (!m_Component.upscaleRT && !m_Component.pixelSnapping)
unitsPerPixel = 1.0f / (cinemachineVCamZoom * m_Component.assetsPPU);
return correctedOrthoSize;
}
}
}

View File

@@ -0,0 +1,13 @@
{
"name": "Unity.2D.PixelPerfect",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,21 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NUnit.Framework;
using UnityEngine.TestTools;
using UnityEditor;
namespace UnityEngine.U2D
{
internal class ObjectMenuCreationTests
{
[Test]
public void ExecuteMenuCommandCreatesGameObjectWithPixelPerfectCamera()
{
var transformCount = Object.FindObjectsOfType<Transform>();
EditorApplication.ExecuteMenuItem("GameObject/2D Object/Pixel Perfect Camera");
LogAssert.NoUnexpectedReceived();
Assert.True(Object.FindObjectsOfType<Transform>().Length > transformCount.Length);
}
}
}

View File

@@ -0,0 +1,13 @@
using NUnit.Framework;
namespace UnityEngine.U2D
{
internal class PixelPerfectCameraEditorTests
{
[Test]
public void PlaceHolderTest()
{
Assert.Pass("This is a placeholder to ensure we have at least one editor test.");
}
}
}

View File

@@ -0,0 +1,22 @@
{
"name": "Unity.2D.PixelPerfect.Editor.Tests",
"references": [
"GUID:2543b18169f9e4044a266a0bf35fe4cf",
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": []
}

View File

@@ -0,0 +1,294 @@
using NUnit.Framework;
namespace UnityEngine.U2D
{
internal class PixelPerfectCameraTests
{
internal class PixelPerfectCameraTestComponent : IPixelPerfectCamera
{
public int assetsPPU { get; set; }
public int refResolutionX { get; set; }
public int refResolutionY { get; set; }
public bool upscaleRT { get; set; }
public bool pixelSnapping { get; set; }
public bool cropFrameX { get; set; }
public bool cropFrameY { get; set; }
public bool stretchFill { get; set; }
}
internal class CalculateCameraPropertiesResult
{
public int zoom;
public bool useOffscreenRT;
public int offscreenRTWidth;
public int offscreenRTHeight;
public Rect pixelRect;
public float orthoSize;
public float unitsPerPixel;
}
private static object[] GetCalculateCameraPropertiesTestCases()
{
object[] testCaseArray = new object[9];
for (int i = 0; i < testCaseArray.Length; ++i)
{
PixelPerfectCameraTestComponent testComponent = new PixelPerfectCameraTestComponent();
int screenWidth = 0;
int screenHeight = 0;
CalculateCameraPropertiesResult expected = new CalculateCameraPropertiesResult();
switch (i)
{
case 0:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = false;
testComponent.pixelSnapping = true;
testComponent.cropFrameX = true;
testComponent.cropFrameY = true;
testComponent.stretchFill = true;
screenWidth = 800;
screenHeight = 500;
expected.zoom = 1;
expected.useOffscreenRT = true;
expected.offscreenRTWidth = 400;
expected.offscreenRTHeight = 300;
expected.pixelRect = new Rect(0.0f, 0.0f, 400, 300);
expected.orthoSize = 1.5f;
expected.unitsPerPixel = 0.01f;
break;
case 1:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = true;
testComponent.pixelSnapping = true;
testComponent.cropFrameX = true;
testComponent.cropFrameY = true;
testComponent.stretchFill = true;
screenWidth = 1100;
screenHeight = 900;
expected.zoom = 2;
expected.useOffscreenRT = true;
expected.offscreenRTWidth = 400;
expected.offscreenRTHeight = 300;
expected.pixelRect = new Rect(0.0f, 0.0f, 400, 300);
expected.orthoSize = 1.5f;
expected.unitsPerPixel = 0.01f;
break;
case 2:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = true;
testComponent.pixelSnapping = true;
testComponent.cropFrameX = false;
testComponent.cropFrameY = true;
testComponent.stretchFill = false;
screenWidth = 400;
screenHeight = 250;
expected.zoom = 1;
expected.useOffscreenRT = true;
expected.offscreenRTWidth = 400;
expected.offscreenRTHeight = 300;
expected.pixelRect = new Rect(0.0f, 0.0f, 400, 300);
expected.orthoSize = 1.5f;
expected.unitsPerPixel = 0.01f;
break;
case 3:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = true;
testComponent.pixelSnapping = true;
testComponent.cropFrameX = true;
testComponent.cropFrameY = false;
testComponent.stretchFill = false;
screenWidth = 1600;
screenHeight = 1200;
expected.zoom = 4;
expected.useOffscreenRT = true;
expected.offscreenRTWidth = 400;
expected.offscreenRTHeight = 300;
expected.pixelRect = new Rect(0.0f, 0.0f, 400, 300);
expected.orthoSize = 1.5f;
expected.unitsPerPixel = 0.01f;
break;
case 4:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = true;
testComponent.pixelSnapping = true;
testComponent.cropFrameX = false;
testComponent.cropFrameY = false;
testComponent.stretchFill = false;
screenWidth = 1600;
screenHeight = 1100;
expected.zoom = 3;
expected.useOffscreenRT = true;
expected.offscreenRTWidth = 532;
expected.offscreenRTHeight = 366;
expected.pixelRect = new Rect(0.0f, 0.0f, 532, 366);
expected.orthoSize = 1.83f;
expected.unitsPerPixel = 0.01f;
break;
case 5:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = false;
testComponent.pixelSnapping = false;
testComponent.cropFrameX = false;
testComponent.cropFrameY = false;
testComponent.stretchFill = true;
screenWidth = 800;
screenHeight = 600;
expected.zoom = 2;
expected.useOffscreenRT = false;
expected.offscreenRTWidth = 0;
expected.offscreenRTHeight = 0;
expected.pixelRect = Rect.zero;
expected.orthoSize = 1.5f;
expected.unitsPerPixel = 0.005f;
break;
case 6:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = false;
testComponent.pixelSnapping = false;
testComponent.cropFrameX = true;
testComponent.cropFrameY = true;
testComponent.stretchFill = false;
screenWidth = 800;
screenHeight = 700;
expected.zoom = 2;
expected.useOffscreenRT = false;
expected.offscreenRTWidth = 0;
expected.offscreenRTHeight = 0;
expected.pixelRect = new Rect(0.0f, 50.0f, 800, 600);
expected.orthoSize = 1.5f;
expected.unitsPerPixel = 0.005f;
break;
case 7:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = false;
testComponent.pixelSnapping = true;
testComponent.cropFrameX = false;
testComponent.cropFrameY = true;
testComponent.stretchFill = false;
screenWidth = 900;
screenHeight = 600;
expected.zoom = 2;
expected.useOffscreenRT = false;
expected.offscreenRTWidth = 0;
expected.offscreenRTHeight = 0;
expected.pixelRect = new Rect(0.0f, 0.0f, 900, 600);
expected.orthoSize = 1.5f;
expected.unitsPerPixel = 0.01f;
break;
case 8:
testComponent.assetsPPU = 100;
testComponent.refResolutionX = 400;
testComponent.refResolutionY = 300;
testComponent.upscaleRT = false;
testComponent.pixelSnapping = true;
testComponent.cropFrameX = true;
testComponent.cropFrameY = false;
testComponent.stretchFill = false;
screenWidth = 900;
screenHeight = 600;
expected.zoom = 2;
expected.useOffscreenRT = false;
expected.offscreenRTWidth = 0;
expected.offscreenRTHeight = 0;
expected.pixelRect = new Rect(50.0f, 0.0f, 800, 600);
expected.orthoSize = 1.5f;
expected.unitsPerPixel = 0.01f;
break;
}
testCaseArray[i] = new object[] { testComponent, screenWidth, screenHeight, expected };
}
return testCaseArray;
}
[Test, TestCaseSource("GetCalculateCameraPropertiesTestCases")]
public void CalculateCameraPropertiesProvidesCorrectResultsWithVariousInputs(PixelPerfectCameraTestComponent testComponent, int screenWidth, int screenHeight, CalculateCameraPropertiesResult expected)
{
PixelPerfectCameraInternal internals = new PixelPerfectCameraInternal(testComponent);
internals.CalculateCameraProperties(screenWidth, screenHeight);
Assert.AreEqual(expected.zoom, internals.zoom);
Assert.AreEqual(expected.useOffscreenRT, internals.useOffscreenRT);
Assert.AreEqual(expected.offscreenRTWidth, internals.offscreenRTWidth);
Assert.AreEqual(expected.offscreenRTHeight, internals.offscreenRTHeight);
Assert.AreEqual(expected.pixelRect, internals.pixelRect);
Assert.AreEqual(expected.orthoSize, internals.orthoSize);
Assert.AreEqual(expected.unitsPerPixel, internals.unitsPerPixel);
}
[Test]
public void CalculatePostRenderPixelRectStretchToFitHeightWorks()
{
PixelPerfectCameraInternal internals = new PixelPerfectCameraInternal(new PixelPerfectCameraTestComponent());
internals.useStretchFill = true;
Rect pixelRect = internals.CalculatePostRenderPixelRect(2.0f, 400, 100);
Rect expected = new Rect(100.0f, 0.0f, 200.0f, 100.0f);
Assert.AreEqual(expected, pixelRect);
}
[Test]
public void CalculatePostRenderPixelRectStretchToFitWidthWorks()
{
PixelPerfectCameraInternal internals = new PixelPerfectCameraInternal(new PixelPerfectCameraTestComponent());
internals.useStretchFill = true;
Rect pixelRect = internals.CalculatePostRenderPixelRect(2.0f, 200, 200);
Rect expected = new Rect(0.0f, 50.0f, 200.0f, 100.0f);
Assert.AreEqual(expected, pixelRect);
}
[Test]
public void CalculatePostRenderPixelRectCenteredWorks()
{
PixelPerfectCameraInternal internals = new PixelPerfectCameraInternal(new PixelPerfectCameraTestComponent());
internals.useStretchFill = false;
internals.zoom = 2;
internals.offscreenRTWidth = 400;
internals.offscreenRTHeight = 300;
Rect pixelRect = internals.CalculatePostRenderPixelRect(4.0f / 3.0f, 1600, 1200);
Rect expected = new Rect(400.0f, 300.0f, 800.0f, 600.0f);
Assert.AreEqual(expected, pixelRect);
}
}
}

View File

@@ -0,0 +1,20 @@
{
"name": "Unity.2D.PixelPerfect.Tests",
"references": [
"GUID:476f7c6c6dfeed041b063446a926e656",
"GUID:27619889b8ba8c24980f49ee34dbb44a",
"GUID:0acc523941302664db1f4e527237feb3"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": []
}

View File

@@ -0,0 +1,29 @@
{
"name": "com.unity.2d.pixel-perfect",
"displayName": "2D Pixel Perfect",
"version": "5.0.0",
"unity": "2021.1",
"description": "The 2D Pixel Perfect package contains the Pixel Perfect Camera component which ensures your pixel art remains crisp and clear at different resolutions, and stable in motion.\n\nIt is a single component that makes all the calculations needed to scale the viewport with resolution changes, removing the hassle from the user. The user can adjust the definition of the pixel art rendered within the camera viewport through the component settings, as well preview any changes immediately in Game view by using the Run in Edit Mode feature.",
"keywords": [
"pixel",
"perfect",
"2D",
"sprite"
],
"type": "tool",
"samples": [
{
"displayName": "Sample Scenes and Extras",
"description": "Sample scenes that demonstrate the usage of the Pixel Perfect Camera. Also contains extra helper scripts.",
"path": "Samples~/Scenes and Extras"
}
],
"upmCi": {
"footprint": "c72fd1699b83832160afb14ff8e9016a7d5ec4f1"
},
"repository": {
"url": "https://github.cds.internal.unity3d.com/unity/2d.git",
"type": "git",
"revision": "82ee1598c2f1270bc1081c1f908364e528335bd5"
}
}