Unityでゲーム作成時に使う画像(pngファイル)を暗号化する話です。ゲーム作者でない人間がゲームに含まれるアセットを取り出すことは可能で(そういうソフトウェアがある)、それを防ぐための手段です。
ゲームにはアセットバンドルという形で画像などを含めることができ、これを利用した方法もあるようです。
https://yakuentei.jp/article/gamedev/unity_asset_encrypt/
(役演亭)にアセットバンドルを使った暗号化について大変詳しい解説があり、勉強させてもらいました。ひと通り実験しながら読めば周辺の全体像が分かると思います。……で、試したのですが、PCではうまく行きましたが実機(スマホ)テストでは考えたような動きになりませんでした。Unityで開発作業をしていると、PCと実機で異なる振る舞いをすることは多いですが、少なくとも今回は原因不明。じゃあ他の方法でいくか……と考えて今回の記事のようにやってみました。
画像を暗号化します。ぼくはPythonでやりました。次のように、暗号化したい画像ファイルのr, gを入れ換えます。実験ですから、これで暗号化されたと考えます。実戦では紹介したサイトにもあるように画像全体をFF(16進数のFF。10進数では255)とXOR(排他的論理和)するか、本気で暗号化したいならAESなどのしっかりした暗号を使えばよいでしょう。
# -*- coding: Shift-JIS -*-
from PIL import Image
import sys
path = 'xxxxx\\yyyyy\\moto.png'
im = Image.open(path).convert('RGBA')
width, height = im.size
for y in range(height):
for x in range(width):
r, g, b, a = im.getpixel( (x, y))
color = (g, r, b, a) #もとの画像のr、gを入れ換えた!
im.putpixel((x, y), color)
im.save('xxxxx\\yyyyy\\crypt.png')
Unity側では先ほど作った crypt.png を Resources フォルダへ入れます。moto.png(暗号化前のファイル) を使うと、アセットを取り出すソフトウェアを使えば画像が丸見えになってしまうのです。そして次のコードで暗号化されたテクスチャを復号化します。つまりここではr、gを入れ換えて元に戻すのです。最後のあたりでテクスチャからスプライトを切り出しています。
public Sprite editPixel() {
Color c = new Color(0, 0, 0, 0);//RGBA 32bit
Color c1 = new Color(0, 0, 0, 0);//RGBA 32bit
Texture2D tex = Resources.Load<Texture2D>("keshitene");
Texture2D tex1 = new Texture2D(tex.width, tex.height);//空のテクスチャ
for (int j = 0; j < tex.height; j++) {
for (int i = 0; i < tex.width; i++) {
c = tex.GetPixel(i, j);
c1.r = c.g;
c1.g = c.r;
c1.b = c.b;
c1.a = c.a;
tex1.SetPixel(i, j, c1);
}
}
tex1.Apply();
Sprite sp = Sprite.Create(tex1, new Rect(32 * 0, 32 * 0, 32, 32),
new Vector2(0.5f, 0.5f), 32);
return sp;
}
注意:
今回は Color c = new Color(0, 0, 0, 0)(RGBAで成分は0~255の整数値)としたので、FormatにRGBA 32bitを選択。またRead/Writeにチェックを入れないとエラーになります。
実機で試してみました。予定通りの動作です!! ゲーム開発では画像以外にもサウンドファイルなどを使うはずです。まだ実験の段階ですが、これもアセットバンドルを使わず、今回紹介した方法でできるはずです。暗号化、多分大事なことだと思うんですが、入門書でもサイトでもあまり見かけません。どういうこと……??