/// <summary> /// Exports a palette to a file /// </summary> /// <param name="outputPath">The path to export to</param> /// <param name="palette">The palette</param> public static void ExportPalette(string outputPath, IList <ARGBColor> palette, int scale = 16, int offset = 0, int?optionalLength = null, int?optionalWrap = null) { int length = optionalLength ?? palette.Count; int wrap = optionalWrap ?? length; var tex = TextureHelpers.CreateTexture2D(Mathf.Min(length, wrap) * scale, Mathf.CeilToInt(length / (float)wrap) * scale, clear: true); for (int i = 0; i < length; i++) { int mainY = (tex.height / scale) - 1 - (i / wrap); int mainX = i % wrap; Color col = palette[offset + i].GetColor(); // Remove transparency col = new Color(col.r, col.g, col.b); for (int y = 0; y < scale; y++) { for (int x = 0; x < scale; x++) { tex.SetPixel(mainX * scale + x, tex.height - (mainY * scale + y) - 1, col); } } } tex.Apply(); Util.ByteArrayToFile(outputPath, tex.EncodeToPNG()); }
private void CreateTilemapFull() { var lvl = LevelEditorData.Level; int mapWidth = 16; int mapHeight = Mathf.CeilToInt(lvl.Maps[LevelEditorData.CurrentMap].TileSet[0].Tiles.Length / (float)mapWidth); int cellSize = LevelEditorData.Level.CellSize; Texture2D tex = TextureHelpers.CreateTexture2D(mapWidth * cellSize, mapHeight * cellSize); // Refresh the full tilemap template for current map int xx = 0; int yy = 0; foreach (Unity_TileTexture t in lvl.Maps[LevelEditorData.CurrentMap].TileSet[0].Tiles) { FillInTilePixels(tex, t, null, xx, yy, cellSize); xx++; if (xx == mapWidth) { xx = 0; yy++; } } templateMaxY = yy + 1; tex.filterMode = FilterMode.Point; tex.Apply(); tilemapFull.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0, 0), LevelEditorData.Level.PixelsPerUnit, 0, SpriteMeshType.FullRect); }
/// <summary> /// Creates a tile set from a tile map /// </summary> /// <param name="tileMapColors">The tile map colors</param> /// <param name="tileMapWidth">The tile map width, in tiles</param> /// <param name="cellSize">The tile size</param> public Unity_TileSet(IList <BaseColor> tileMapColors, int tileMapWidth, int cellSize) { // Create the tile array Tiles = new Unity_TileTexture[tileMapColors.Count / (cellSize * cellSize)]; // Create each tile for (var index = 0; index < Tiles.Length; index++) { // Create the texture Texture2D tex = TextureHelpers.CreateTexture2D(cellSize, cellSize); // Get the tile x and y var tileY = (int)Math.Floor(index / (double)tileMapWidth); var tileX = (index - (tileMapWidth * tileY)); var tileOffset = (tileY * tileMapWidth * cellSize * cellSize) + (tileX * cellSize); // Set every pixel for (int y = 0; y < cellSize; y++) { for (int x = 0; x < cellSize; x++) { tex.SetPixel(x, y, tileMapColors[(tileOffset + (y * cellSize * tileMapWidth + x))].GetColor()); } } // Apply the pixels tex.Apply(); // Create a tile Tiles[index] = tex.CreateTile(); } }
/// <summary> /// Creates an empty tileset with a single transparent tile /// </summary> public Unity_TileSet(int cellSize) { Tiles = new Unity_TileTexture[] { TextureHelpers.CreateTexture2D(cellSize, cellSize, true, true).CreateTile() }; }
public Texture2D ToTexture(Context context) { // Create the texture var tex = TextureHelpers.CreateTexture2D(Width, Height); // Get the block width var blockWidth = GetBlockWidth(context.Settings.EngineVersion); // Write each block for (int blockIndex = 0; blockIndex < ImageBlocks.Length; blockIndex++) { // Get the block data var blockData = ImageBlocks[blockIndex]; // Write the block for (int y = 0; y < Height; y++) { for (int x = 0; x < blockWidth; x++) { // Get the color var c = blockData[x + (y * blockWidth)]; c.Alpha = Byte.MaxValue; // Set the pixel tex.SetPixel((x + (blockIndex * blockWidth)), tex.height - y - 1, c.GetColor()); } } } tex.Apply(); return(tex); }
public virtual Unity_MapTileMap GetTileSet(Context context, SNES_Proto_ROM rom) { // Read the tiles const int block_size = 0x20; uint length = (uint)rom.TileDescriptors.Length * 8 * 8; // Get the tile-set texture var tex = TextureHelpers.CreateTexture2D(256, Mathf.CeilToInt(length / 256f / Settings.CellSize) * Settings.CellSize); for (int i = 0; i < rom.TileDescriptors.Length; i++) { var descriptor = rom.TileDescriptors[i]; var x = ((i / 4) * 2) % (256 / 8) + ((i % 2) == 0 ? 0 : 1); var y = (((i / 4) * 2) / (256 / 8)) * 2 + ((i % 4) < 2 ? 0 : 1); var curOff = block_size * descriptor.TileIndex; FillTextureBlock(tex, 0, 0, x, y, rom.TileMap, curOff, rom.Palettes, descriptor.Palette, descriptor.FlipX, descriptor.FlipY); } tex.Apply(); return new Unity_MapTileMap(tex, Settings.CellSize); }
/// <summary> /// Creates a tileset with a single colored tile /// </summary> public Unity_TileSet(int cellSize, Color color) { var tex = TextureHelpers.CreateTexture2D(cellSize, cellSize); tex.SetPixels(Enumerable.Repeat(color, cellSize * cellSize).ToArray()); tex.Apply(); Tiles = new Unity_TileTexture[] { tex.CreateTile() }; }
public void ExportTileset() { var tileSetIndex = 0; // Export every tile set foreach (var tileSet in LevelEditorData.Level.Maps[LevelEditorData.CurrentMap].TileSet.Where(x => x?.Tiles?.Any(y => y != null) == true)) { // Get values var tileCount = tileSet.Tiles.Length; const int tileSetWidth = 16; var tileSetHeight = (int)Math.Ceiling(tileCount / (double)tileSetWidth); var tileSize = (int)tileSet.Tiles.First().rect.width; // Create the texture var tileTex = TextureHelpers.CreateTexture2D(tileSetWidth * tileSize, tileSetHeight * tileSize); // Default to fully transparent tileTex.SetPixels(Enumerable.Repeat(new Color(0, 0, 0, 0), tileTex.width * tileTex.height).ToArray()); // Add every tile to it for (int i = 0; i < tileCount; i++) { // Get the tile texture var tile = tileSet.Tiles[i]; // Get the texture offsets var offsetY = (i / tileSetWidth) * tileSize; var offsetX = (i % tileSetWidth) * tileSize; // Set the pixels for (int y = 0; y < tile.rect.height; y++) { for (int x = 0; x < tile.rect.width; x++) { tileTex.SetPixel(x + offsetX, tileTex.height - (y + offsetY) - 1, tile.texture.GetPixel((int)tile.rect.x + x, (int)tile.rect.y + y)); } } } tileTex.Apply(); var destPath = $@"Tilemaps\{LevelEditorData.CurrentSettings.GameModeSelection}\{LevelEditorData.CurrentSettings.GameModeSelection} - {LevelEditorData.CurrentSettings.World} {LevelEditorData.CurrentSettings.Level:00} ({tileSetIndex}).png"; Directory.CreateDirectory(Path.GetDirectoryName(destPath)); // Save the tile map File.WriteAllBytes(destPath, tileTex.EncodeToPNG()); tileSetIndex++; } }
public async UniTask ExportSpritesAsync(GameSettings settings, string outputDir) { using (var context = new Context(settings)) { // Load rom await LoadFilesAsync(context); var rom = FileFactory.Read <SNES_Proto_ROM>(GetROMFilePath, context); var graphicsGroups = GetGraphicsGroups(rom); foreach (var graphicsGroup in graphicsGroups) { // Export every sprite for (int i = 0; i < graphicsGroup.Sprites.Length; i++) { var spriteIndex = i % graphicsGroup.ImageDescriptors.Length; var vramConfig = i / graphicsGroup.ImageDescriptors.Length; var imgDescriptor = graphicsGroup.ImageDescriptors[spriteIndex]; var sprite = graphicsGroup.Sprites[i]; if (sprite == null) { continue; } var xPos = imgDescriptor.TileIndex % 16; var yPos = (imgDescriptor.TileIndex - xPos) / 16; var width = (int)sprite.rect.width; var height = (int)sprite.rect.height; var newTex = TextureHelpers.CreateTexture2D(width, height); var flipX = false; var flipY = true; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { newTex.SetPixel(flipX ? width - x - 1 : x, (!flipY) ? height - y - 1 : y, sprite.texture.GetPixel((int)sprite.rect.x + x, (int)sprite.rect.y + y)); } } newTex.Apply(); Util.ByteArrayToFile(Path.Combine(outputDir, graphicsGroup.Name, $"{spriteIndex} - {vramConfig}.png"), newTex.EncodeToPNG()); } } } }
void ExportVignette(GBC_PalmOS_Vignette vignette, string outputPath) { if (vignette == null) { throw new Exception("Not a vignette"); } if (vignette.Width > 0x1000 || vignette.Height > 0x1000 || vignette.Width == 0 || vignette.Height == 0) { throw new Exception("Not a vignette"); } int w = (int)vignette.Width; int h = (int)vignette.Height; var palette = vignette.BPP == 8 ? Util.CreateDummyPalette(256, firstTransparent: false) : Util.CreateDummyPalette(16, firstTransparent: false).Reverse().ToArray(); Texture2D tex = TextureHelpers.CreateTexture2D(w, h); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int ind = y * w + x; if (vignette.BPP == 16) { BaseColor col = vignette.DataPPC[ind]; tex.SetPixel(x, h - 1 - y, col.GetColor()); } else if (vignette.BPP == 8) { int col = vignette.Data[ind]; tex.SetPixel(x, h - 1 - y, palette[col].GetColor()); } else { int col = vignette.Data[ind / 2]; if (ind % 2 == 0) { col = BitHelpers.ExtractBits(col, 4, 4); } else { col = BitHelpers.ExtractBits(col, 4, 0); } tex.SetPixel(x, h - 1 - y, palette[col].GetColor()); } } } tex.Apply(); Util.ByteArrayToFile(outputPath, tex.EncodeToPNG()); }
/// <summary> /// Exports the v-ram as an image /// </summary> /// <param name="outputPath">The path to export to</param> /// <param name="vram">The v-ram</param> public static void ExportVram(string outputPath, PS1_VRAM vram) { Texture2D vramTex = TextureHelpers.CreateTexture2D(16 * 128, 2 * 256); for (int x = 0; x < 16 * 128; x++) { for (int y = 0; y < 2 * 256; y++) { byte val = vram.GetPixel8(0, y / 256, x, y % 256); vramTex.SetPixel(x, (2 * 256) - 1 - y, new Color(val / 255f, val / 255f, val / 255f)); } } vramTex.Apply(); Util.ByteArrayToFile(outputPath, vramTex.EncodeToPNG()); }
public IEnumerable <Texture2D> GetAnimationFrames(Context context, GBAIsometric_RHR_AnimSet animSet, GBAIsometric_RHR_Animation anim, Dictionary <ushort, byte[]> decompressedDictionary, RGBA5551Color[] palette) { SerializerObject s = context.Deserializer; var startFrame = anim.StartFrameIndex; var frameCount = anim.FrameCount; Color[] pal = Util.ConvertGBAPalette(palette); for (int f = 0; f < frameCount; f++) { var frame = animSet.Frames[startFrame + f]; Texture2D tex = TextureHelpers.CreateTexture2D(animSet.Width * CellSize, animSet.Height * CellSize, clear: true); if (frame.PatternIndex == 0xFFFF || frame.TileIndicesIndex == 0xFFFF) { // Empty frame } else { var patIndex = frame.PatternIndex; int curTile = frame.TileIndicesIndex; for (int p = 0; p < animSet.Patterns[patIndex].Length; p++) { var pattern = animSet.Patterns[patIndex][p]; for (int y = 0; y < pattern.Height; y++) { for (int x = 0; x < pattern.Width; x++) { int actualX = x + pattern.XPosition; int actualY = y + pattern.YPosition; ushort tileIndex = animSet.TileIndices[curTile]; if (!decompressedDictionary.ContainsKey(tileIndex)) { s.Goto(animSet.GraphicsDataPointer.Value.CompressedDataPointer); s.DoEncoded(new RHR_SpriteEncoder(animSet.Is8Bit, animSet.GraphicsDataPointer.Value.CompressionLookupBuffer, animSet.GraphicsDataPointer.Value.CompressedDataPointer, tileIndex), () => { decompressedDictionary[tileIndex] = s.SerializeArray <byte>(default, s.CurrentLength, name: $"{animSet.Name}:Tiles[{curTile}]:{tileIndex}");
public Texture2D ToTexture2D() { if (Width == 0 || Height == 0) { return(null); } Texture2D tex = TextureHelpers.CreateTexture2D(Width * 8, Height * 8); for (int y = 0; y < Height; y++) { var pal = Util.ConvertAndSplitGBCPalette(Palette[y], transparentIndex: null); for (int x = 0; x < Width; x++) { var tileInd = (y * Width + x); tex.FillInTile(TileSet, tileInd * 0x10, pal[PalIndices[tileInd]], Util.TileEncoding.Planar_2bpp, 8, true, x * 8, y * 8); } } tex.Apply(); return(tex); }
public Texture2D ToTexture2D() { const int tileWidth = 8; const int tileLength = tileWidth * tileWidth / 2; var tex = TextureHelpers.CreateTexture2D(Width * AssembleWidth * tileWidth, Height * AssembleHeight * tileWidth); var assembleLength = Width * Height * tileLength; for (int assembleY = 0; assembleY < AssembleHeight; assembleY++) { for (int assembleX = 0; assembleX < AssembleWidth; assembleX++) { var assembleOffset = assembleLength * (assembleY * AssembleWidth + assembleX); for (int y = 0; y < Height; y++) { for (int x = 0; x < Width; x++) { var relTileOffset = tileLength * (y * Width + x); tex.FillInTile( imgData: TileData, imgDataOffset: assembleOffset + relTileOffset, pal: Util.ConvertGBAPalette(Palette), encoding: Util.TileEncoding.Linear_4bpp, tileWidth: tileWidth, flipTextureY: true, tileX: tileWidth * (assembleX * Width + x), tileY: tileWidth * (assembleY * Height + y)); } } } } tex.Apply(); return(tex); }
public Texture2D ToTexture2D() { const int cellSize = 8; const int tileSize = cellSize * cellSize; var tex = TextureHelpers.CreateTexture2D(Map.Width * cellSize, Map.Height * cellSize); var pal = Util.ConvertGBAPalette(Palette); var fullTileSet = TileSets.SelectMany(x => x).ToArray(); for (int y = 0; y < Map.Height; y++) { for (int x = 0; x < Map.Width; x++) { var tile = Map.MapData[y * Map.Width + x]; tex.FillInTile(fullTileSet, tile.TileMapY * tileSize, pal, Util.TileEncoding.Linear_8bpp, cellSize, true, x * cellSize, y * cellSize, tile.HorizontalFlip, tile.VerticalFlip); } } tex.Apply(); return(tex); }
/// <summary> /// Converts the PCX data to a texture /// </summary> /// <returns>The texture</returns> public Texture2D ToTexture(bool flip = false) { // Create the texture var tex = TextureHelpers.CreateTexture2D(ImageWidth, ImageHeight); // Set every pixel for (int y = 0; y < ImageHeight; y++) { for (int x = 0; x < ImageWidth; x++) { // Get the palette index var paletteIndex = ScanLines[y][x]; // Set the pixel tex.SetPixel(x, flip ? (ImageHeight - y - 1) : y, VGAPalette[paletteIndex].GetColor()); } } // Apply the pixels tex.Apply(); // Return the texture return(tex); }
/// <summary> /// Exports all vignette textures to the specified output directory /// </summary> /// <param name="settings">The game settings</param> /// <param name="outputDir">The output directory</param> public override void ExportVignetteTextures(GameSettings settings, string outputDir) { // Create a new context using (var context = new Context(settings)) { void exportBit(string file, int width = 16, bool swizzled = true, int blockWidth = 8, int blockHeight = 8, IList <Vector2> sizes = null) { // Add the file to the context context.AddFile(new LinearSerializedFile(context) { filePath = file, Endianness = BinaryFile.Endian.Big }); // Read the file BIT bit = FileFactory.Read <BIT>(file, context); // Get the texture var tex = bit.ToTexture(width, swizzled: swizzled, blockWidth: blockWidth, blockHeight: blockHeight, invertYAxis: true); if (sizes?.Any() == true) { // Get the pixels var pixels = tex.GetPixels(); var pixelOffset = 0; // TODO: This doesn't work... for (int i = 0; i < sizes.Count; i++) { // Get the output file path var outputPath = Path.Combine(outputDir, FileSystem.ChangeFilePathExtension(file, $" - {i}.png")); // Create a new texture var newTex = TextureHelpers.CreateTexture2D((int)sizes[i].x, (int)sizes[i].y); // Set the pixels newTex.SetPixels(pixels.Reverse().Skip(pixelOffset).Take(newTex.width * newTex.height).ToArray()); newTex.Apply(); pixelOffset += newTex.width * newTex.height; Util.ByteArrayToFile(outputPath, newTex.EncodeToPNG()); } } else { // Get the output file path var outputPath = Path.Combine(outputDir, FileSystem.ChangeFilePathExtension(file, $".png")); Util.ByteArrayToFile(outputPath, tex.EncodeToPNG()); } } void exportVig(string file, int width) { // Add the file to the context context.AddFile(new LinearSerializedFile(context) { filePath = file, Endianness = BinaryFile.Endian.Big }); // Read the raw data var rawData = FileFactory.Read <ObjectArray <ARGB1555Color> >(file, context, onPreSerialize: (s, x) => x.Length = s.CurrentLength / 2); // Create the texture var tex = TextureHelpers.CreateTexture2D(width, (int)(rawData.Length / width)); // Set the pixels for (int y = 0; y < tex.height; y++) { for (int x = 0; x < tex.width; x++) { var c = rawData.Value[y * tex.width + x]; tex.SetPixel(x, tex.height - y - 1, c.GetColor()); } } // Apply the pixels tex.Apply(); // Get the output file path var outputPath = Path.Combine(outputDir, FileSystem.ChangeFilePathExtension(file, $".png")); Util.ByteArrayToFile(outputPath, tex.EncodeToPNG()); } foreach (var bitFile in Directory.GetFiles(settings.GameDirectory, "*.bit", SearchOption.AllDirectories)) { var relativePath = bitFile.Substring(context.BasePath.Length).Replace('\\', '/'); if (!VigWidths.ContainsKey(relativePath)) { Debug.LogWarning($"Vignette file {relativePath} has no width"); continue; } exportBit(relativePath, width: VigWidths[relativePath]); } foreach (var vigFile in Directory.GetFiles(settings.GameDirectory, "*.vig", SearchOption.AllDirectories)) { var relativePath = vigFile.Substring(context.BasePath.Length).Replace('\\', '/'); if (!VigWidths.ContainsKey(relativePath)) { Debug.LogWarning($"Vignette file {relativePath} has no width"); continue; } exportVig(relativePath, width: VigWidths[relativePath]); } } }
/// <summary> /// Gets the sprite texture for an event /// </summary> /// <param name="context">The context</param> /// <param name="imgBuffer">The image buffer, if available</param> /// <param name="s">The image descriptor to use</param> /// <returns>The texture</returns> public override Texture2D GetSpriteTexture(Context context, byte[] imgBuffer, R1_ImageDescriptor s) { if (s.ImageType != 2 && s.ImageType != 3) { return(null); } // Ignore dummy sprites if (s.IsDummySprite()) { return(null); } // Get the image properties var width = s.Width; var height = s.Height; var offset = s.ImageBufferOffset; var pal4 = FileFactory.Read <ObjectArray <RGBA5551Color> >(GetPalettePath(context.Settings, 4), context, (y, x) => x.Length = 256); var pal8 = FileFactory.Read <ObjectArray <RGBA5551Color> >(GetPalettePath(context.Settings, 8), context, (y, x) => x.Length = 256); // Select correct palette var palette = s.ImageType == 3 ? pal8.Value : pal4.Value; var paletteOffset = 16 * s.Unknown1; // Create the texture Texture2D tex = TextureHelpers.CreateTexture2D(width, height); // Set every pixel if (s.ImageType == 3) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { var paletteIndex = imgBuffer[offset + width * y + x]; // Set the pixel tex.SetPixel(x, height - 1 - y, palette[paletteIndex].GetColor()); } } } else if (s.ImageType == 2) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int actualX = (s.ImageOffsetInPageX + x) / 2; var paletteIndex = imgBuffer[offset + (width * y + x) / 2]; if (x % 2 == 0) { paletteIndex = (byte)BitHelpers.ExtractBits(paletteIndex, 4, 0); } else { paletteIndex = (byte)BitHelpers.ExtractBits(paletteIndex, 4, 4); } // Set the pixel tex.SetPixel(x, height - 1 - y, palette[paletteOffset + paletteIndex].GetColor()); } } } // Apply the changes tex.Apply(); // Return the texture return(tex); }
/// <summary> /// Gets the sprite texture for an event /// </summary> /// <param name="context">The context</param> /// <param name="imgBuffer">The image buffer, if available</param> /// <param name="s">The image descriptor to use</param> /// <returns>The texture</returns> public override Texture2D GetSpriteTexture(Context context, byte[] imgBuffer, R1_ImageDescriptor img) { if (img.ImageType != 2 && img.ImageType != 3) { return(null); } if (img.Unknown2 == 0) { return(null); } ImageBuffer buf = context.GetStoredObject <ImageBuffer>("vram"); // Get the image properties var width = img.Width; var height = img.Height; var offset = img.ImageBufferOffset; Texture2D tex = TextureHelpers.CreateTexture2D(width, height); var palette = FileFactory.Read <R1_PS1_Executable>(ExeFilePath, context).Saturn_Palettes; var paletteOffset = img.PaletteInfo; var isBigRay = img.Offset.file.filePath == GetBigRayFilePath(); var isFont = context.GetStoredObject <R1_PS1_FontData[]>("Font")?.SelectMany(x => x.ImageDescriptors).Contains(img) == true; //paletteOffset = (ushort)(256 * (img.Unknown2 >> 4)); if (img.ImageType == 3) { //paletteOffset = 20 * 256; paletteOffset = isBigRay ? (ushort)(21 * 256) : isFont ? (ushort)(19 * 256) : (ushort)((GetPaletteIndex(context) * 256)); } else { paletteOffset = isBigRay ? (ushort)(21 * 256) : isFont ? (ushort)(19 * 256) : (ushort)((GetPaletteIndex(context) * 256) + ((img.PaletteInfo >> 8)) * 16); //paletteOffset = (ushort)((GetPaletteIndex(context) * 256) + ((img.Unknown2 >> 4) - 1) * 16); //paletteOffset = (ushort)(19 * 256 + ((img.Unknown2 >> 4) - 1) * 16); } // Set every pixel if (img.ImageType == 3) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { var paletteIndex = buf.GetPixel8((uint)(offset + width * y + x)); // Set the pixel var color = palette[paletteOffset + paletteIndex].GetColor(); if (paletteIndex == 0) { color = new Color(color.r, color.g, color.b, 0f); } else { color = new Color(color.r, color.g, color.b, 1f); } tex.SetPixel(x, height - 1 - y, color); } } } else if (img.ImageType == 2) { for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { var paletteIndex = buf.GetPixel8((uint)(offset + (width * y + x) / 2)); if (x % 2 == 0) { paletteIndex = (byte)BitHelpers.ExtractBits(paletteIndex, 4, 4); } else { paletteIndex = (byte)BitHelpers.ExtractBits(paletteIndex, 4, 0); } // Set the pixel var color = palette[paletteOffset + paletteIndex].GetColor(); if (paletteIndex == 0) { color = new Color(color.r, color.g, color.b, 0f); } else { color = new Color(color.r, color.g, color.b, 1f); } tex.SetPixel(x, height - 1 - y, color); } } } //tex.SetPixels(Enumerable.Repeat(Color.blue, tex.width * tex.height).ToArray()); tex.Apply(); return(tex); }
public Texture2D CreateLevelScreenshot() { // Hide unused links and show gendoors foreach (var e in Objects) { // Update the event to make sure it has rendered e.ForceUpdate(); if (e.ObjData is Unity_Object_R1 r1Obj) { Enum[] exceptions = new Enum[] { R1_EventType.TYPE_GENERATING_DOOR, R1_EventType.TYPE_DESTROYING_DOOR, R1_EventType.MS_scintillement, R1_EventType.MS_super_gendoor, R1_EventType.MS_super_kildoor, R1_EventType.MS_compteur, R1_EventType.TYPE_RAY_POS, R1_EventType.TYPE_INDICATOR, }; if (exceptions.Contains(r1Obj.EventData.Type)) { e.gameObject.SetActive(true); } } else if (e.ObjData is Unity_Object_GBA gbaObj) { if (gbaObj.Actor.ActorID == 0) { e.gameObject.SetActive(true); } } // Always hide events with no graphics if (e.defautRenderer.enabled) { e.gameObject.SetActive(false); } // TODO: Change this option // Helper method bool showLinksForObj(Unity_ObjBehaviour ee) { if (ee.ObjData is Unity_Object_R1 r1Object) { return(r1Object.EventData.Type == R1_EventType.TYPE_GENERATING_DOOR || r1Object.EventData.Type == R1_EventType.TYPE_DESTROYING_DOOR || r1Object.EventData.Type == R1_EventType.MS_scintillement || r1Object.EventData.Type == R1_EventType.MS_super_gendoor || r1Object.EventData.Type == R1_EventType.MS_super_kildoor || r1Object.EventData.Type == R1_EventType.MS_compteur); } return(ee.ObjData.EditorLinkGroup != 0); } if (e.ObjData.EditorLinkGroup == 0) { e.lineRend.enabled = false; e.linkCube.gameObject.SetActive(false); } else { // Hide link if not linked to gendoor bool gendoorFound = showLinksForObj(e); var allofSame = new List <Unity_ObjBehaviour> { e }; foreach (Unity_ObjBehaviour f in Objects.Where(f => f.ObjData.EditorLinkGroup == e.ObjData.EditorLinkGroup)) { allofSame.Add(f); if (showLinksForObj(f)) { gendoorFound = true; } } if (!gendoorFound) { foreach (var a in allofSame) { a.lineRend.enabled = false; a.linkCube.gameObject.SetActive(false); } } } } RenderTexture renderTex = new RenderTexture(LevelEditorData.MaxWidth * LevelEditorData.Level.CellSize / 1, LevelEditorData.MaxHeight * LevelEditorData.Level.CellSize / 1, 24); renderCamera.targetTexture = renderTex; var cellSizeInUnits = LevelEditorData.Level.CellSize / (float)LevelEditorData.Level.PixelsPerUnit; renderCamera.transform.position = new Vector3((LevelEditorData.MaxWidth) * cellSizeInUnits / 2f, -(LevelEditorData.MaxHeight) * cellSizeInUnits / 2f, renderCamera.transform.position.z); renderCamera.orthographicSize = (LevelEditorData.MaxHeight * cellSizeInUnits / 2f); renderCamera.rect = new Rect(0, 0, 1, 1); renderCamera.Render(); // Save to picture RenderTexture.active = renderTex; tex = TextureHelpers.CreateTexture2D(renderTex.width, renderTex.height); tex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0); tex.Apply(); RenderTexture.active = null; renderCamera.rect = new Rect(0, 0, 0, 0); return(tex); }
// Used to redraw all tiles with different palette (0 = auto, 1-3 = palette) public void RefreshTiles(int palette) { Debug.Log($"Refreshing tiles with palette {palette}"); // Change button visibility _currentPalette = palette; for (int i = 0; i < paletteButtons.Length; i++) { ColorBlock b = paletteButtons[i].colors; b.normalColor = _currentPalette == i ? new Color(1, 1, 1) : new Color(0.5f, 0.5f, 0.5f); paletteButtons[i].colors = b; } // Get the current level and map var lvl = LevelEditorData.Level; // If auto, refresh indexes if (palette == 0) { lvl.AutoApplyPalette(); } // Refresh tiles for every map if (GraphicsTilemaps.Length != LevelEditorData.Level.Maps.Length) { Array.Resize(ref GraphicsTilemaps, LevelEditorData.Level.Maps.Length); for (int i = 1; i < GraphicsTilemaps.Length; i++) { GraphicsTilemaps[i] = Instantiate <SpriteRenderer>(GraphicsTilemaps[0], new Vector3(0, 0, -i), Quaternion.identity, GraphicsTilemaps[0].transform.parent); GraphicsTilemaps[i].gameObject.name = "Tilemap Graphics " + i; if (lvl.Maps[i].IsForeground) { Debug.Log($"{i} is in front"); //TilemapRenderer tr = GraphicsTilemaps[i].GetComponent<TilemapRenderer>(); SpriteRenderer tr = GraphicsTilemaps[i]; tr.sortingLayerName = "Tiles Front"; } } } animatedTiles = new Dictionary <Unity_AnimatedTile, List <Unity_AnimatedTile.Instance> > [GraphicsTilemaps.Length]; for (int mapIndex = 0; mapIndex < LevelEditorData.Level.Maps.Length; mapIndex++) { var map = lvl.Maps[mapIndex]; animatedTiles[mapIndex] = new Dictionary <Unity_AnimatedTile, List <Unity_AnimatedTile.Instance> >(); if (map.IsAdditive) { GraphicsTilemaps[mapIndex].material = additiveMaterial; } if (map.Alpha.HasValue) { GraphicsTilemaps[mapIndex].color = new Color(1f, 1f, 1f, map.Alpha.Value); } int cellSize = LevelEditorData.Level.CellSize; Texture2D tex = TextureHelpers.CreateTexture2D(map.Width * cellSize, map.Height * cellSize); for (int y = 0; y < map.Height; y++) { for (int x = 0; x < map.Width; x++) { var t = map.MapTiles[y * map.Width + x]; if (palette != 0) { t.PaletteIndex = palette; } if (t.PaletteIndex - 1 >= map.TileSet.Length) { t.PaletteIndex = 1; } Unity_TileTexture tile = map.GetTile(t, LevelEditorData.CurrentSettings); var atInstance = map.GetAnimatedTile(t, LevelEditorData.CurrentSettings); if (atInstance != null) { atInstance.x = x; atInstance.y = y; var at = atInstance.animatedTile; if (!animatedTiles[mapIndex].ContainsKey(at)) { animatedTiles[mapIndex][at] = new List <Unity_AnimatedTile.Instance>(); } animatedTiles[mapIndex][at].Add(atInstance); } FillInTilePixels(tex, tile, t, x, y, cellSize); /*GraphicsTilemaps[mapIndex].SetTile(new Vector3Int(x, y, 0), map.GetTile(t, LevelEditorData.CurrentSettings)); * GraphicsTilemaps[mapIndex].SetTransformMatrix(new Vector3Int(x, y, 0), GraphicsTilemaps[mapIndex].GetTransformMatrix(new Vector3Int(x, y, 0)) * Matrix4x4.Scale(new Vector3(t.Data.HorizontalFlip ? -1 : 1, t.Data.VerticalFlip ? -1 : 1, 1)));*/ } } tex.filterMode = FilterMode.Point; tex.Apply(); // Note: FullRect is important, otherwise when editing you need to create a new sprite GraphicsTilemaps[mapIndex].sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0, 0), LevelEditorData.Level.PixelsPerUnit, 0, SpriteMeshType.FullRect); } CreateTilemapFull(); }
public Sprite[] GetSprites(SNES_Proto_ROM rom, SNES_Proto_ImageDescriptor[] imageDescriptors) { var sprites = new Sprite[imageDescriptors.Length * 3]; var pal = Util.ConvertAndSplitGBAPalette(rom.SpritePalette); var buffer = new byte[rom.SpriteTileSet.Length]; for (int addBlock = 0; addBlock < 3; addBlock++) { Array.Copy(rom.SpriteTileSet, buffer, buffer.Length); switch (addBlock) { case 0: Array.Copy(rom.SpriteTileSetAdd0, 0, buffer, 0xC00, 0x400); Array.Copy(rom.SpriteTileSetAdd0, 0x400, buffer, 0x1000, 0x100); Array.Copy(rom.SpriteTileSetAdd0, 0x500, buffer, 0x1200, 0x100); break; case 1: Array.Copy(rom.SpriteTileSetAdd1, 0, buffer, 0xC00, 0x400); Array.Copy(rom.SpriteTileSetAdd1, 0x400, buffer, 0x1000, 0x100); Array.Copy(rom.SpriteTileSetAdd1, 0x500, buffer, 0x1200, 0x100); break; case 2: Array.Copy(rom.SpriteTileSetAdd2, 0, buffer, 0xC00, 0x400); break; } var tileSets = pal.Select(x => Util.ToTileSetTexture(buffer, x, Util.TileEncoding.Planar_4bpp, 8, true, wrap: 16, flipTileX: true)).ToArray(); for (int i = 0; i < imageDescriptors.Length; i++) { if (i == 0) { sprites[i] = null; continue; } var imgDescriptor = imageDescriptors[i]; var xPos = imgDescriptor.TileIndex % 16; var yPos = (imgDescriptor.TileIndex - xPos) / 16; var size = imgDescriptor.IsLarge ? 16 : 8; var spriteTex = TextureHelpers.CreateTexture2D(size, size); var flipX = imgDescriptor.FlipX; var flipY = !imgDescriptor.FlipY; var texX = xPos * 8; var texY = tileSets[imgDescriptor.Palette].height - yPos * 8 - size; var width = size; var height = size; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { spriteTex.SetPixel(flipX ? width - x - 1 : x, (!flipY) ? height - y - 1 : y, tileSets[imgDescriptor.Palette].GetPixel(texX + x, texY + y)); } } spriteTex.Apply(); sprites[addBlock * imageDescriptors.Length + i] = spriteTex.CreateSprite(); } } return(sprites); }
/// <summary> /// Converts the PCX data to a texture /// </summary> /// <returns>The texture</returns> public Texture2D ToTexture(int width, int blockWidth = 8, int blockHeight = 8, bool swizzled = true, bool invertYAxis = false) { int height = (PixelsPaletted?.Length ?? Pixels.Length) / width; // Create the texture var tex = TextureHelpers.CreateTexture2D(width, height, clear: true); var blockSwizzlePattern = new Coordinate[] { new Coordinate(0, 0), new Coordinate(1, 0), new Coordinate(0, 1), new Coordinate(1, 1) }; // Set every pixel int curPixel = 0; int xStep, yStep; if (swizzled) { xStep = blockSwizzlePattern.Max(c => c.x) + 1; yStep = blockSwizzlePattern.Max(c => c.y) + 1; if (width < xStep * blockWidth) { blockSwizzlePattern = blockSwizzlePattern.Where(c => c.x * blockWidth < width).ToArray(); } if (height < yStep * blockWidth) { blockSwizzlePattern = blockSwizzlePattern.Where(c => c.y * blockHeight < height).ToArray(); } xStep = blockSwizzlePattern.Max(c => c.x) + 1; yStep = blockSwizzlePattern.Max(c => c.y) + 1; } else { blockSwizzlePattern = new Coordinate[] { new Coordinate(0, 0) }; xStep = 1; yStep = 1; } for (int y = 0; y < height / blockHeight / yStep; y++) { for (int x = 0; x < width / blockWidth / xStep; x++) { for (int b = 0; b < blockSwizzlePattern.Length; b++) { int Bx = ((x * xStep) + blockSwizzlePattern[b].x) * blockWidth; int By = ((y * yStep) + blockSwizzlePattern[b].y) * blockHeight; if (Bx >= width) { continue; } if (By >= height) { continue; } for (int by = 0; by < blockHeight; by++) { for (int bx = 0; bx < blockWidth; bx++) { if (Bx + bx >= width) { continue; } if (By + by >= height) { continue; } if (PixelsPaletted != null) { byte index = PixelsPaletted[curPixel]; int pltIndex = ((y * yStep)) * blockHeight + x; if (pltIndex < PLT.Value.Length) { byte numPalette = PLT.Value[pltIndex]; uint palIndex = 256 * (uint)numPalette + index; Color col = PAL.Value[palIndex].GetColor(); // 555? huh? tex.SetPixel(Bx + bx, invertYAxis ? height - 1 - (By + by) : (By + by), new Color(col.r, col.g, col.b, 1f)); //tex.SetPixel(Bx + bx, height - 1 - (By + by), new Color(index / 255f, index / 255f, index / 255f, 1f)); } } else { tex.SetPixel(Bx + bx, invertYAxis ? height - 1 - (By + by) : (By + by), Pixels[curPixel].GetColor()); } curPixel++; } } } } } // Apply the pixels tex.Apply(); // Return the texture return(tex); }
public override Unity_ObjGraphics GetCommonDesign(GBA_ActorGraphicData graphicData) { // Create the design var des = new Unity_ObjGraphics { Sprites = new List <Sprite>(), Animations = new List <Unity_ObjAnimation>(), }; if (graphicData == null) { return(des); } var tileMap = graphicData.SpriteGroup_BatmanVengeance.TileMap; var pal = graphicData.SpriteGroup_BatmanVengeance.Palette.Palette; const int tileWidth = 8; const int tileSize = (tileWidth * tileWidth) / 2; var numPalettes = graphicData.SpriteGroup_BatmanVengeance.Palette.Palette.Length / 16; // Add sprites for each palette for (int palIndex = 0; palIndex < numPalettes; palIndex++) { for (int i = 0; i < tileMap.TileMapLength; i++) { var tex = TextureHelpers.CreateTexture2D(CellSize, CellSize); for (int y = 0; y < tileWidth; y++) { for (int x = 0; x < tileWidth; x++) { int index = (i * tileSize) + ((y * tileWidth + x) / 2); var b = tileMap.TileMap[index]; var v = BitHelpers.ExtractBits(b, 4, x % 2 == 0 ? 0 : 4); Color c = pal[palIndex * 16 + v].GetColor(); if (v != 0) { c = new Color(c.r, c.g, c.b, 1f); } tex.SetPixel(x, (tileWidth - 1 - y), c); } } tex.Apply(); des.Sprites.Add(tex.CreateSprite()); } } Unity_ObjAnimationPart[] GetPartsForLayer(GBA_BatmanVengeance_SpriteGroup s, GBA_BatmanVengeance_Animation a, int frame, GBA_BatmanVengeance_AnimationChannel l) { /*if (l.TransformMode == GBA_AnimationLayer.AffineObjectMode.Hide || l.RenderMode == GBA_AnimationLayer.GfxMode.Window || l.RenderMode == GBA_AnimationLayer.GfxMode.Regular || l.Mosaic) return new Unity_ObjAnimationPart[0]; || if (l.Color == GBA_AnimationLayer.ColorMode.Color8bpp) { || Debug.LogWarning("Animation Layer @ " + l.Offset + " has 8bpp color mode, which is currently not supported."); || return new Unity_ObjAnimationPart[0]; || }*/ Unity_ObjAnimationPart[] parts = new Unity_ObjAnimationPart[l.XSize * l.YSize]; if (l.ImageIndex > graphicData.SpriteGroup_BatmanVengeance.TileMap.TileMapLength) { Controller.print("Image index too high: " + graphicData.Offset + " - " + l.Offset); } if (l.PaletteIndex > graphicData.SpriteGroup_BatmanVengeance.Palette.Palette.Length / 16) { Controller.print("Palette index too high: " + graphicData.Offset + " - " + l.Offset + " - " + l.PaletteIndex + " - " + (graphicData.SpriteGroup_BatmanVengeance.Palette.Palette.Length / 16)); } float rot = 0; // l.GetRotation(a, s, frame); Vector2?scl = null; // l.GetScale(a, s, frame); for (int y = 0; y < l.YSize; y++) { for (int x = 0; x < l.XSize; x++) { parts[y * l.XSize + x] = new Unity_ObjAnimationPart { ImageIndex = tileMap.TileMapLength * l.PaletteIndex + (l.ImageIndex + y * l.XSize + x), IsFlippedHorizontally = l.IsFlippedHorizontally, IsFlippedVertically = l.IsFlippedVertically, XPosition = (l.XPosition + (l.IsFlippedHorizontally ? (l.XSize - 1 - x) : x) * CellSize), YPosition = (l.YPosition + (l.IsFlippedVertically ? (l.YSize - 1 - y) : y) * CellSize), Rotation = rot, Scale = scl, TransformOriginX = (l.XPosition + l.XSize * CellSize / 2f), TransformOriginY = (l.YPosition + l.YSize * CellSize / 2f) }; } } return(parts); } // Add first animation for now foreach (var a in graphicData.SpriteGroup_BatmanVengeance.Animations) { var unityAnim = new Unity_ObjAnimation(); var frames = new List <Unity_ObjAnimationFrame>(); for (int i = 0; i < a.FrameCount; i++) { frames.Add(new Unity_ObjAnimationFrame(a.Frames[i].Layers /*.OrderByDescending(l => l.Priority)*/.SelectMany(l => GetPartsForLayer(graphicData.SpriteGroup_BatmanVengeance, a, i, l)).Reverse().ToArray())); } unityAnim.Frames = frames.ToArray(); unityAnim.AnimSpeed = 1; des.Animations.Add(unityAnim); } return(des); }
public async UniTask ExportAnimFramesAsync(GameSettings settings, string outputDir, bool saveAsGif) { using (var context = new Context(settings)) { // Load rom await LoadFilesAsync(context); var rom = FileFactory.Read <SNES_Proto_ROM>(GetROMFilePath, context); foreach (var graphicsGroup in GetGraphicsGroups(rom)) { var groupDir = Path.Combine(outputDir, graphicsGroup.Name); Directory.CreateDirectory(groupDir); var sprites = graphicsGroup.Sprites; var states = graphicsGroup.States; var animIndex = 0; foreach (var stateGroup in states.Select(x => x.SNES_State).GroupBy(x => x.Animation)) { // Get the animation var anim = stateGroup.Key; var layersPerFrame = anim.LayersPerFrame; var frameCount = anim.FrameCount; string animPointer = stateGroup.Key.Offset != null ? $"{(stateGroup.Key.Offset.FileOffset + 4) % 0x8000 + 0x8000:X4}" : null; int vramConfig = stateGroup.First().VRAMConfigIndex; var spriteOffset = graphicsGroup.ImageDescriptors.Length * vramConfig; // Calculate frame size int minX = anim.Layers.Where(x => sprites[x.ImageIndex] != null).Min(x => x.XPosition); int minY = anim.Layers.Where(x => sprites[x.ImageIndex] != null).Min(x => x.YPosition); int frameWidth = (int)anim.Layers.Where(x => sprites[x.ImageIndex] != null).Max(x => sprites[x.ImageIndex].rect.width + x.XPosition); int frameHeight = (int)anim.Layers.Where(x => sprites[x.ImageIndex] != null).Max(x => sprites[x.ImageIndex].rect.height + x.YPosition); // Create frame textures var frames = new Texture2D[frameCount]; // Create each animation frame for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { var tex = TextureHelpers.CreateTexture2D(frameWidth - minX, frameHeight - minY, clear: true); // Write each layer for (var layerIndex = 0; layerIndex < layersPerFrame; layerIndex++) { var animationLayer = anim.Layers[frameIndex * layersPerFrame + layerIndex]; if ((spriteOffset + animationLayer.ImageIndex) >= sprites.Length) { continue; } // Get the sprite var sprite = sprites[spriteOffset + animationLayer.ImageIndex]; if (sprite == null) { continue; } // Set every pixel for (int y = 0; y < sprite.rect.height; y++) { for (int x = 0; x < sprite.rect.width; x++) { var c = sprite.texture.GetPixel((int)sprite.rect.x + x, (int)sprite.rect.y + y); var xPosition = (animationLayer.IsFlippedHorizontally ? (sprite.rect.width - 1 - x) : x) + animationLayer.XPosition; var yPosition = (!animationLayer.IsFlippedVertically ? (sprite.rect.height - 1 - y) : y) + animationLayer.YPosition; xPosition -= minX; yPosition -= minY; if (c.a != 0) { tex.SetPixel((int)xPosition, (int)(tex.height - yPosition - 1), c); } } } } tex.Apply(); frames[frameIndex] = tex; } // Export animation if (saveAsGif) { var speeds = stateGroup.Select(x => x.AnimSpeed).Distinct(); foreach (var speed in speeds) { using (MagickImageCollection collection = new MagickImageCollection()) { int index = 0; foreach (var tex in frames) { var img = tex.ToMagickImage(); collection.Add(img); collection[index].AnimationDelay = speed; collection[index].AnimationTicksPerSecond = 60; collection[index].Trim(); collection[index].GifDisposeMethod = GifDisposeMethod.Background; index++; } // Save gif collection.Write(Path.Combine(groupDir, $"{animIndex} ({speed}){(animPointer != null ? $" - {animPointer}" : String.Empty)}.gif")); } } } else { for (int i = 0; i < frames.Length; i++) { Util.ByteArrayToFile(Path.Combine(groupDir, $"{animIndex}", $"{i}.png"), frames[i].EncodeToPNG()); } } animIndex++; } } } }
public Unity_TileSet LoadTileSet(byte[] tileSet, RGBA5551Color[] palette, bool is2bpp, bool flipX, SNES_Proto_AnimatedTileEntry[] animatedTiles = null, bool shadow = false) { var pal = is2bpp ? Util.ConvertAndSplitGBCPalette(palette) : Util.ConvertAndSplitGBAPalette(palette); int numPalettes = pal.Length; const int wrap = 64; int bpp = is2bpp ? 2 : 4; const int tileWidth = 8; int tileSize = tileWidth * tileWidth * bpp / 8; int tilesetLength = tileSet.Length / tileSize; int animatedTilesLength = animatedTiles?.Sum(at => at.GraphicsBuffer.Length / tileSize) ?? 0; int totalTilesetLength = tilesetLength + animatedTilesLength; int tilesX = Math.Min(totalTilesetLength * numPalettes, wrap); int tilesY = Mathf.CeilToInt(totalTilesetLength * numPalettes / (float)wrap); var tex = TextureHelpers.CreateTexture2D(tilesX * tileWidth, tilesY * tileWidth); for (int p = 0; p < numPalettes; p++) { for (int i = 0; i < tilesetLength; i++) { int tileInd = i + p * totalTilesetLength; int tileY = (tileInd / wrap) * tileWidth; int tileX = (tileInd % wrap) * tileWidth; tex.FillInTile( imgData: tileSet, imgDataOffset: i * tileSize, pal: pal[p], encoding: is2bpp ? Util.TileEncoding.Planar_2bpp : Util.TileEncoding.Planar_4bpp, tileWidth: tileWidth, flipTextureY: false, tileX: tileX, tileY: tileY, flipTileX: flipX); } if (animatedTiles != null) { int curAnimatedTile = 0; for (int i = 0; i < animatedTiles.Length; i++) { int numTiles = animatedTiles[i].GraphicsBuffer.Length / tileSize; for (int t = 0; t < numTiles; t++) { int tileInd = tilesetLength + curAnimatedTile + p * totalTilesetLength; int tileY = (tileInd / wrap) * tileWidth; int tileX = (tileInd % wrap) * tileWidth; tex.FillInTile( imgData: animatedTiles[i].GraphicsBuffer, imgDataOffset: t * tileSize, pal: pal[p], encoding: is2bpp ? Util.TileEncoding.Planar_2bpp : Util.TileEncoding.Planar_4bpp, tileWidth: tileWidth, flipTextureY: false, tileX: tileX, tileY: tileY, flipTileX: flipX); curAnimatedTile++; } } } } if (shadow) { var colors = tex.GetPixels(); colors = colors.Select(c => c.a == 0 ? c : Color.black).ToArray(); tex.SetPixels(colors); } tex.Apply(); Unity_AnimatedTile[] unityAnimatedTiles = null; if (animatedTiles != null) { int curAnimatedTile = 0; var animTilesDict = new Dictionary <int, List <int> >(); foreach (var at in animatedTiles) { var tileInd = (at.VRAMAddress * 2) / 0x20; int numTiles = at.GraphicsBuffer.Length / tileSize; for (int i = 0; i < numTiles; i++) { int key = tileInd + i; if (!animTilesDict.ContainsKey(key)) { animTilesDict[key] = new List <int>(); animTilesDict[key].Add(key); } animTilesDict[key].Add(tilesetLength + curAnimatedTile); curAnimatedTile++; } } var unityAnimTilesList = new List <Unity_AnimatedTile>(); for (int p = 0; p < numPalettes; p++) { foreach (var kv in animTilesDict) { Unity_AnimatedTile newAT = new Unity_AnimatedTile() { AnimationSpeed = 2, TileIndices = kv.Value.Select(t => t + p * totalTilesetLength).ToArray() }; //Debug.Log(string.Join(",",newAT.TileIndices)); unityAnimTilesList.Add(newAT); } } unityAnimatedTiles = unityAnimTilesList.ToArray(); } return(new Unity_TileSet(tex, tileWidth) { SNES_BaseLength = totalTilesetLength, AnimatedTiles = unityAnimatedTiles }); }