C#
備忘録
XNA
Terraria

【Terraria】ゲーム上のイメージデータを加工しつつファイル出力

はじめに

この記事はTerrariaでのMod開発でのイメージデータの操作について、pngやgifでの出力や、加工して出力する方法について説明します。
なお、TerrariaはXNAを利用しているようですが、これまでxnaには触れた事すらないので、手探りで行ったものなので、無駄な事や間違いがあるかもしれませんので、ご注意ください。
とにかくやってみたらできたというレベルです。

GIFの利用について

GIFについてはGIFアニメーションを出力するため、NGifというオープンソースを利用しています。
CodeProjectに登録されていますので、利用にはサインインが必要です。

https://www.codeproject.com/Articles/11505/NGif-Animated-GIF-Encoder-for-NET

アイテムのイメージをそのままpngで保存する

アイテムのイメージデータはxnaのTexture2Dで管理されています。
次のように定義されています。

namespace Terraria
{
    public class Main : Game
    {
         :
        public static Texture2D[] itemTexture;
         :
    }
}

Texture2Dにはpngファイルで保存するメソッドがあるため、pngで保存するなら、次のようにするだけです。

using System.Drawing.Imaging;
using System.IO;
 :
using (FileStream fs = new FileStream(@"c:\hoge.png", FileMode.Create))
{
    Texture2D texture = Main.itemTexture[itemType];
    texture.SaveAsPng(fs, texture.Width, texture.Height);
}

itemType はint型の任意の値です。
TerrariaではアイテムにIDが振られていますが、このIDはItemクラスでtypeというプロパティです。
たとえば、type1のアイテムは"Iron Pickaxe"です。
個人的にはややこしい名称です。
次のように定義されています。

namespace Terraria
{
    public class Item : Entity, TagSerializable
    {
         :
        public int type;
         :
    }
}

背景を合成してファイル出力する

普通に System.Drawing.ImageSystem.Drawing.Graphics を使う方法です。
ゲーム内だけで操作するならXNAの作法に合せればいいのでしょうが、この辺が詳しくないです。

using Microsoft.Xna.Framework.Graphics;
using System.Drawing;
using System.IO;
using Terraria;
 :
Texture2D textureBack = Main.inventoryBack9Texture;
Texture2D textureItem = Main.itemTexture[itemType];

MemoryStream msBack = new MemoryStream();
MemoryStream msItem = new MemoryStream();

textureBack.SaveAsPng(msBack, textureBack.Width, textureBack.Height);
textureItem.SaveAsPng(msItem, textureItem.Width, textureItem.Height);

Image imageBack = Image.FromStream(msBack);
Image imageItem = Image.FromStream(msItem);

using (Graphics g = Graphics.FromImage(imageBack))
{
    g.DrawImage(imageItem, 0, 0, imageItem.Width, imageItem.Height);
}

Bitmap bmp = new Bitmap(imageBack);
bmp.MakeTransparent();
bmp.Save(@"c:\hoge.png", ImageFormat.Png);

っといった感じです。
実際には、共通的な部分はメソッドにしていたり、背景に合成する際は、中心に配置されるようにポジションなどの調整を行いますが、その辺はよくあるコードなので、とりあえずここでは省略しました。

NPCの動作をアニメーションGIFでファイル出力する

Terrariaでは、NPCのイメージは Main.npcTexture 配列にあります。
ただし、アイテムの時と違って、 Main.instance.LoadNPC(int i)を呼び出す必要があるみたいです。

using Microsoft.Xna.Framework.Graphics;
using System.Drawing;
using System.IO;
using Terraria;
 :
Main.instance.LoadNPC(npcType);
Texture2D texture = Main.npcTexture[npcType];
int height = texture.Height / Main.npcFrameCount[npcType]

AnimatedGifEncoder gif = new AnimatedGifEncoder();
gif.Start(@"c:\hoge.gif");
gif.SetDelay(500);
gif.SetRepeat(0);
for (int i = 0; i < Main.npcFrameCount[npcType]; i++)
{
    MemoryStream ms = new MemoryStream();
    texture.SaveAsPng(ms, texture.Width, texture.Height);
    Image imageNPC = Image.FromStream(ms);

    Bitmap bmp = new Bitmap(texture.Width, texture.Height);
    Image image = Image.FromHbitmap(bmp.GetHbitmap());
    using (Graphics g = Graphics.FromImage(image))
    {
        g.DrawImage(imageNPC, 0, 0, new Rectangle(0, height * i, width, height), GraphicsUnit.Pixel);
    }
    gif.AddFrame(image).GetImage());
}
gif.Finish();

Main.npcTexture[npcType] で得られるイメージのフレーム分割数は Main.npcFrameCount[npcType] で得られます。
通常は縦に並んでいますが、一部は縦横にならんでいますので、これではうまくいきませんが、現在その場合のフレームの取得方法をわかっていません。
現時点では、対象となるNPCは 4/579 です。おそらく1.3.4で追加された、Old One's Armyの一部の敵のみのようです。

透過についてはこのコードには記載しませんでした。
今現在実際にやっている方法は、一旦特定の色で塗りつぶして、透過色の指定をするような形をとっています。もう少しちゃんとした方法がわかったら修正したいと思います。

とりあえず

まったくもって、綺麗な内容ではないですが、とりあえずまとめるだけまとめておこうと思って記載した次第です。
今後知識が深まった際や、余力ができたら、コードを整理して、実際にTerrariaでMod開発をしてみようという人が参考に出来るような内容にしたいと考えています。

以上