Files
tiny_tackle_heroes/Assets/_Project/Tests/PlayMode/ComparisonSpriteFlipRenderPlayModeTests.cs
T
2026-09-27 21:08:05 +09:00

223 lines
11 KiB
C#

using System.Collections;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.TestTools;
using Object = UnityEngine.Object;
namespace BumpCombat.Tests
{
public sealed class ComparisonSpriteFlipRenderPlayModeTests
{
private const int FlipRenderTestLayer = 31;
[UnityTest]
public IEnumerator SpriteRendererFlipXMirrorsAsymmetricRenderedSprite()
{
const int width = 80;
const int height = 40;
const int textureWidth = 8;
const int textureHeight = 4;
GameObject cameraObject = null;
GameObject spriteObject = null;
Texture2D source = null;
Texture2D capture = null;
RenderTexture target = null;
Material material = null;
Sprite sprite = null;
try
{
Shader shader = Shader.Find("BumpCombat/Comparison Sprite Depth");
Assert.That(shader, Is.Not.Null);
material = new Material(shader) { enableInstancing = true };
source = new Texture2D(textureWidth, textureHeight, TextureFormat.RGBA32, false)
{
filterMode = FilterMode.Point,
wrapMode = TextureWrapMode.Clamp,
};
Color32[] sourcePixels = new Color32[textureWidth * textureHeight];
for (int i = 0; i < sourcePixels.Length; i++) sourcePixels[i] = new Color32(0, 0, 0, 0);
sourcePixels[0] = new Color32(255, 32, 32, 255);
sourcePixels[1] = new Color32(255, 96, 32, 255);
sourcePixels[textureWidth + 2] = new Color32(32, 255, 32, 255);
sourcePixels[textureWidth * 2 + 5] = new Color32(32, 64, 255, 255);
sourcePixels[textureWidth * 3 + 7] = new Color32(255, 255, 32, 255);
source.SetPixels32(sourcePixels);
source.Apply(false, false);
sprite = Sprite.Create(source, new Rect(0, 0, textureWidth, textureHeight),
new Vector2(.5f, .5f), 2f);
target = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32)
{
filterMode = FilterMode.Point,
antiAliasing = 1,
};
target.Create();
cameraObject = new GameObject("Comparison flip render test camera");
Camera camera = cameraObject.AddComponent<Camera>();
cameraObject.AddComponent<UniversalAdditionalCameraData>().SetRenderer(1);
camera.orthographic = true;
camera.orthographicSize = 1f;
camera.aspect = (float)width / height;
camera.clearFlags = CameraClearFlags.SolidColor;
camera.backgroundColor = Color.black;
camera.targetTexture = target;
camera.cullingMask = 1 << FlipRenderTestLayer;
camera.transform.position = new Vector3(0f, 0f, -10f);
spriteObject = new GameObject("Asymmetric flip test sprite");
spriteObject.layer = FlipRenderTestLayer;
SpriteRenderer renderer = spriteObject.AddComponent<SpriteRenderer>();
renderer.sprite = sprite;
renderer.sharedMaterial = material;
renderer.flipX = false;
capture = new Texture2D(width, height, TextureFormat.RGBA32, false);
yield return null;
Color[] forward = Capture(camera, target, capture);
renderer.flipX = true;
yield return null;
Color[] flipped = Capture(camera, target, capture);
int mismatchedPixels = 0;
int forwardBrightPixels = 0;
int flippedBrightPixels = 0;
int flipChangedPixels = 0;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
Color expected = forward[y * width + (width - 1 - x)];
Color actual = flipped[y * width + x];
Color sameCoordinateForward = forward[y * width + x];
if (IsBright(forward[y * width + x])) forwardBrightPixels++;
if (IsBright(actual)) flippedBrightPixels++;
if (ColorDistance(sameCoordinateForward, actual) > .02f) flipChangedPixels++;
if (Mathf.Abs(expected.r - actual.r) + Mathf.Abs(expected.g - actual.g)
+ Mathf.Abs(expected.b - actual.b) + Mathf.Abs(expected.a - actual.a) > .02f)
mismatchedPixels++;
}
Assert.That(forwardBrightPixels, Is.GreaterThan(0), "The forward image must contain rendered sprite pixels.");
Assert.That(flippedBrightPixels, Is.GreaterThan(0), "The flipped image must contain rendered sprite pixels.");
Assert.That(flipChangedPixels, Is.GreaterThan(0), "Flipping this asymmetric sprite must change actual pixels.");
Assert.That(mismatchedPixels, Is.Zero,
"SpriteRenderer.flipX must mirror the rendered pixels, not merely expose a flip flag.");
}
finally
{
if (spriteObject != null) Object.Destroy(spriteObject);
if (cameraObject != null) Object.Destroy(cameraObject);
if (sprite != null) Object.Destroy(sprite);
if (source != null) Object.Destroy(source);
if (capture != null) Object.Destroy(capture);
if (target != null) { target.Release(); Object.Destroy(target); }
if (material != null) Object.Destroy(material);
}
}
[UnityTest]
public IEnumerator SharedSpriteMaterialStillRendersMeshAndLineGeometry()
{
const int width = 80;
const int height = 80;
GameObject cameraObject = null;
GameObject meshObject = null;
GameObject lineObject = null;
Texture2D capture = null;
RenderTexture target = null;
Material material = null;
Mesh mesh = null;
try
{
Shader shader = Shader.Find("BumpCombat/Comparison Sprite Depth");
Assert.That(shader, Is.Not.Null);
material = new Material(shader);
target = new RenderTexture(width, height, 24, RenderTextureFormat.ARGB32);
target.Create();
cameraObject = new GameObject("Shared material render test camera");
Camera camera = cameraObject.AddComponent<Camera>();
cameraObject.AddComponent<UniversalAdditionalCameraData>().SetRenderer(1);
camera.orthographic = true;
camera.orthographicSize = 2f;
camera.aspect = 1f;
camera.clearFlags = CameraClearFlags.SolidColor;
camera.backgroundColor = Color.black;
camera.targetTexture = target;
camera.cullingMask = 1 << FlipRenderTestLayer;
camera.transform.position = new Vector3(0f, 0f, -10f);
meshObject = new GameObject("Ground-like MeshRenderer");
meshObject.layer = FlipRenderTestLayer;
mesh = new Mesh
{
vertices = new[]
{
new Vector3(-.6f, .1f, 0f), new Vector3(.6f, .1f, 0f),
new Vector3(-.6f, 1.3f, 0f), new Vector3(.6f, 1.3f, 0f),
},
uv = new[] { Vector2.zero, Vector2.right, Vector2.up, Vector2.one },
colors = new[] { Color.white, Color.white, Color.white, Color.white },
triangles = new[] { 0, 2, 1, 2, 3, 1 },
};
mesh.RecalculateBounds();
meshObject.AddComponent<MeshFilter>().sharedMesh = mesh;
meshObject.AddComponent<MeshRenderer>().sharedMaterial = material;
lineObject = new GameObject("Ground-like LineRenderer");
lineObject.layer = FlipRenderTestLayer;
LineRenderer line = lineObject.AddComponent<LineRenderer>();
line.useWorldSpace = true;
line.positionCount = 2;
line.startWidth = line.endWidth = .12f;
line.startColor = line.endColor = Color.red;
line.sharedMaterial = material;
line.SetPosition(0, new Vector3(-1.2f, -1f, 0f));
line.SetPosition(1, new Vector3(1.2f, -1f, 0f));
capture = new Texture2D(width, height, TextureFormat.RGBA32, false);
yield return null;
Color[] pixels = Capture(camera, target, capture);
int meshPixels = 0;
int linePixels = 0;
foreach (Color pixel in pixels)
{
if (pixel.r > .8f && pixel.g > .8f && pixel.b > .8f) meshPixels++;
if (pixel.r > .8f && pixel.g < .3f && pixel.b < .3f) linePixels++;
}
Assert.That(meshPixels, Is.GreaterThan(10),
"The shared shader must keep opaque floor/mesh geometry visible with default SpriteProps.");
Assert.That(linePixels, Is.GreaterThan(3),
"LineRenderer geometry using the shared comparison material must remain visible.");
}
finally
{
if (meshObject != null) Object.Destroy(meshObject);
if (lineObject != null) Object.Destroy(lineObject);
if (cameraObject != null) Object.Destroy(cameraObject);
if (capture != null) Object.Destroy(capture);
if (mesh != null) Object.Destroy(mesh);
if (target != null) { target.Release(); Object.Destroy(target); }
if (material != null) Object.Destroy(material);
}
}
private static Color[] Capture(Camera camera, RenderTexture target, Texture2D capture)
{
RenderTexture previous = RenderTexture.active;
camera.Render();
RenderTexture.active = target;
capture.ReadPixels(new Rect(0f, 0f, target.width, target.height), 0, 0);
capture.Apply(false, false);
Color[] pixels = capture.GetPixels();
RenderTexture.active = previous;
return pixels;
}
private static bool IsBright(Color color) => color.r > .25f || color.g > .25f || color.b > .25f;
private static float ColorDistance(Color a, Color b) => Mathf.Abs(a.r - b.r) + Mathf.Abs(a.g - b.g)
+ Mathf.Abs(a.b - b.b) + Mathf.Abs(a.a - b.a);
}
}