Created
June 21, 2023 10:04
-
-
Save codorizzi/bf544229f76266c9df0b723a1e83a152 to your computer and use it in GitHub Desktop.
Unity Sprite Merging - Takes children Sprite Renderers, Merges them into a single parent
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections; | |
using System.Collections.Generic; | |
using Sirenix.OdinInspector; | |
using UnityEngine; | |
[RequireComponent(typeof(SpriteRenderer))] | |
public class SpriteMerger : MonoBehaviour | |
{ | |
private const int PixelsPerUnit = 256; | |
[Button] | |
public void Combine() | |
{ | |
// Calculate the bounds of all child sprites | |
Rect totalBounds = GetTotalBounds(); | |
// Create a combined texture | |
Texture2D combinedTexture = CreateCombinedTexture(totalBounds); | |
// Trim the combined texture | |
combinedTexture = TrimTexture(combinedTexture); | |
// Create the final sprite | |
CreateFinalSprite(combinedTexture); | |
} | |
private Rect GetTotalBounds() | |
{ | |
SpriteRenderer[] childSprites = GetComponentsInChildren<SpriteRenderer>(); | |
float minX = float.MaxValue, minY = float.MaxValue, maxX = float.MinValue, maxY = float.MinValue; | |
foreach (SpriteRenderer childSprite in childSprites) | |
{ | |
if (childSprite.gameObject != this.gameObject) | |
{ | |
Vector2 spriteSize = childSprite.sprite.bounds.size; | |
Vector2 localPosition = childSprite.transform.localPosition; | |
minX = Mathf.Min(minX, localPosition.x); | |
minY = Mathf.Min(minY, localPosition.y); | |
maxX = Mathf.Max(maxX, localPosition.x + spriteSize.x); | |
maxY = Mathf.Max(maxY, localPosition.y + spriteSize.y); | |
} | |
} | |
Vector2 min = new Vector2(minX, minY); | |
Vector2 size = new Vector2(maxX - minX, maxY - minY); | |
return new Rect(min, size); | |
} | |
private Texture2D CreateCombinedTexture(Rect totalBounds) | |
{ | |
// Convert bounds size to pixel size | |
Vector2Int totalSize = new Vector2Int( | |
Mathf.CeilToInt(totalBounds.width * PixelsPerUnit), | |
Mathf.CeilToInt(totalBounds.height * PixelsPerUnit) | |
); | |
// Create a new texture with the calculated size | |
Texture2D combinedTexture = new Texture2D(totalSize.x, totalSize.y, TextureFormat.RGBA32, false); | |
combinedTexture.filterMode = FilterMode.Point; | |
// Initialize all pixels to be fully transparent | |
for (int x = 0; x < combinedTexture.width; x++) | |
{ | |
for (int y = 0; y < combinedTexture.height; y++) | |
{ | |
combinedTexture.SetPixel(x, y, new Color(0, 0, 0, 0)); | |
} | |
} | |
// Go through all child sprites and draw them onto the new texture | |
SpriteRenderer[] childSprites = GetComponentsInChildren<SpriteRenderer>(); | |
foreach (SpriteRenderer childSprite in childSprites) | |
{ | |
if (childSprite.gameObject != this.gameObject) | |
{ | |
// Position is local to the parent transform | |
Vector2 localPosition = childSprite.transform.localPosition; | |
AddSpriteToTexture(localPosition, childSprite.sprite, totalBounds, combinedTexture); | |
childSprite.gameObject.SetActive(false); | |
} | |
} | |
combinedTexture.Apply(); | |
return combinedTexture; | |
} | |
private void AddSpriteToTexture(Vector2 position, Sprite sprite, Rect totalBounds, Texture2D combinedTexture) | |
{ | |
Texture2D spriteTexture = sprite.texture; | |
Vector2Int pixelPos = WorldToPixelCoordinates(position, totalBounds); | |
for (int x = 0; x < spriteTexture.width; x++) | |
{ | |
for (int y = 0; y < spriteTexture.height; y++) | |
{ | |
Color pixelColor = spriteTexture.GetPixel(x, y); | |
if (pixelColor.a > 0) // Don't overwrite pixels if the current pixel is transparent | |
{ | |
combinedTexture.SetPixel(pixelPos.x + x, pixelPos.y + y, pixelColor); | |
} | |
} | |
} | |
} | |
private Vector2Int WorldToPixelCoordinates(Vector2 worldPos, Rect totalBounds) | |
{ | |
return new Vector2Int( | |
Mathf.FloorToInt((worldPos.x - totalBounds.xMin) * PixelsPerUnit), | |
Mathf.FloorToInt((worldPos.y - totalBounds.yMin) * PixelsPerUnit) | |
); | |
} | |
private Texture2D TrimTexture(Texture2D original) | |
{ | |
int xMin = int.MaxValue, xMax = int.MinValue, yMin = int.MaxValue, yMax = int.MinValue; | |
for (int x = 0; x < original.width; ++x) | |
{ | |
for (int y = 0; y < original.height; ++y) | |
{ | |
Color pixelColor = original.GetPixel(x, y); | |
if (pixelColor.a > 0) | |
{ | |
if (x < xMin) xMin = x; | |
if (x > xMax) xMax = x; | |
if (y < yMin) yMin = y; | |
if (y > yMax) yMax = y; | |
} | |
} | |
} | |
int width = xMax - xMin + 1; | |
int height = yMax - yMin + 1; | |
Color[] pixels = original.GetPixels(xMin, yMin, width, height); | |
Texture2D trimmedTexture = new Texture2D(width, height); | |
trimmedTexture.SetPixels(pixels); | |
trimmedTexture.Apply(); | |
return trimmedTexture; | |
} | |
private void CreateFinalSprite(Texture2D combinedTexture) | |
{ | |
Sprite finalSprite = Sprite.Create(combinedTexture, new Rect(0.0f, 0.0f, combinedTexture.width, combinedTexture.height), new Vector2(0.5f, 0.5f), PixelsPerUnit); | |
SpriteRenderer renderer = GetComponent<SpriteRenderer>(); | |
renderer.sprite = finalSprite; | |
} | |
} |
Hey,
The code was a modified version of Unity's layout group. Because Unity's
terms are pretty strict, I thought it best just to remove the gist, despite
everyone having access to the original, and it only being operable in Unity.
If it was just my code, I would share it freely.
…On Tue., Jan. 7, 2025, 5:43 p.m. Wiktor Aaron Wąsowski, < ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
Hello!
Apologies for commenting on an unrelated gist but a while ago I've
commented on your SLayoutGroup gist with a question about its licence
because I wanted to know if it's ok if I use it in a commercial project.
However, you hid or deleted that gist right after. Was there any problem
with the licensing of that code? Could you please let me know? I have my
email address public on my profile. Thanks in advance!
—
Reply to this email directly, view it on GitHub
<https://gist.github.com/codorizzi/bf544229f76266c9df0b723a1e83a152#gistcomment-5377146>
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACUV6NIYQG4R66DEV5KDA432JPYYZBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDUOJ2WLJDOMFWWLO3UNBZGKYLEL5YGC4TUNFRWS4DBNZ2F6YLDORUXM2LUPGBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVEYTEMZRGE2DONZQU52HE2LHM5SXFJTDOJSWC5DF>
.
You are receiving this email because you authored the thread.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>
.
Ok, thank you for clarification!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello!
Apologies for commenting on an unrelated gist but a while ago I've commented on your SLayoutGroup gist with a question about its licence because I wanted to know if it's ok if I use it in a commercial project. However, you hid or deleted that gist right after. Was there any problem with the licensing of that code? Could you please let me know? I have my email address public on my profile. Thanks in advance!