ECナビの子会社unigameでソーシャルゲームの開発をしている谷西(@tnnsst35)といいます。
新卒入社3年目のまだまだひよっこですが、お付き合いいただければと思います。
今回僕がお話しするのは「画像合成」についてです。
* 画像合成って?
ソーシャルゲームでは、キャラクターの表情、洋服、持ち物など、組み合わせ方によっては何パターンも存在するような画像を扱う必要があります。
そんな数え切れないパターンの画像をひとつひとつデザイナーに作ってもらうのは気の遠くなる話です。
そこで登場するのが、「画像合成」です。
画像合成は文字どおり、画像Aと画像Bを合成して、新しく画像Cを生み出すということです。
unigameで開発しているほぼ全てのソーシャルゲームで画像合成は登場しています。
unigameではPHPのGDライブラリを用いて画像合成をしています。
* GDで画像合成
例として
キャラクター(img/chara.gif) に 青い洋服(img/wears/blue.gif) を着せた画像を img/compose/blue_chara.gif として保存する方法をGDでやってみましょう。
<?php $baseImg = imageCreateFromGif('img/chara.gif'); //img/chara.gifを読み込む $wearImg = imageCreateFromGif('img/wears/blue.gif'); //img/wears/blue.gifを読み込む
$size = getImageSize('img/wears/blue.gif'); //img/wears/blue.gifのサイズを取得する $width = $size[0]; $height = $size[1];
imageCopy($baseImg, $wearImg, 16, 32, 0, 0, $width, $height); //画像を合成する
imageDestroy($wearImg); //メモリを開放する
$newFile = 'img/compose/blue_chara.gif'; imageGif($baseImg, $newFile); //img/compose/blue_chara.gifとして書き出す chmod($newFile, 0775); //権限を775に変更する
imageDestroy($baseImg); //メモリを開放する?>
どうでしょうか?
ソースコードをみてみると、実に簡単でわかりやすい処理なのかがわかると思います。
imageCreateFromGifで画像を読み込み、imageCopyに読み込んだ画像と合成する座標を引数で渡し、imageGifで書き出す。
たったこれだけで出来てしまいます。
また、複数の画像を合成したい場合は、imageCopyを繰り返せば実現できます。
実際の運用では一度保存した画像は再度合成しないように file_exists でファイルがなければ合成するようにします。
<?php
$newFile = 'img/compose/blue_chara.gif'; if (!file_exists($newFile)) { //画像合成の処理 }?>
* Imagickで画像合成
unigameではGDを用いていますが、他にもいくつか方法はあるようです。
今回は PECL::Imagick を用いた方法も試してみました。
<?PHP $baseImg = new Imagick('img/chara.gif'); //img/chara.gifを読み込む $wearImg = new Imagick('img/wears/blue.gif'); //img/wears/blue.gifを読み込む
$baseImg->compositeImage($wearImg, $wearImg->getImageCompose(), 16, 32); //画像を合成する
$wearImg->destroy(); //メモリを開放する
$newFile = 'img/compose/blue_chara.gif'; $baseImg->writeImage($newFile); //img/compose/blue_chara.gifとして書き出す chmod($newFile, 0775); //権限を775に変更する
$wearImg->destroy(); //メモリを開放する?>
コードをみるとわかりますが、やってることはGDとほとんど変わりません。
一見するとどちらでやっても同じ結果を生み出すので、どちらも採用しても良い感じがしますが、
実は速度に差があるのです。
* GDとImagickの比較
画像合成する処理を 10000回繰り返し行い、かかった時間を測定するテストを行いました。
<?PHP //GDのテスト $startTime = time();
for ($i = 0;$i < 10000;$i++) { $baseImg = imageCreateFromGif('img/chara.gif'); //img/chara.gifを読み込む $wearImg = imageCreateFromGif('img/wears/blue.gif'); //img/wears/blue.gifを読み込む
$size = getImageSize('img/wears/blue.gif'); //img/wears/blue.gifのサイズを取得する $width = $size[0]; $height = $size[1];
imageCopy($baseImg, $wearImg, 16, 32, 0, 0, $width, $height); //画像を合成する
imageDestroy($wearImg); //メモリを開放する imageDestroy($baseImg); //メモリを開放する }
$endTime = time();
echo 'GD : ' . ($endTime - $startTime);
//imagickのテスト
$startTime = time();
for ($i = 0;$i < 10000;$i++) { $baseImg = new Imagick('img/chara.gif'); //img/chara.gifを読み込む $wearImg = new Imagick('img/wears/blue.gif'); //img/wears/blue.gifを読み込む
$baseImg->compositeImage($wearImg, $wearImg->getImageCompose(), 16, 32); //画像を合成する
$wearImg->destroy(); //メモリを開放する $wearImg->destroy(); //メモリを開放する }
$endTime = time();
echo ', imagick : ' . ($endTime - $startTime);?>
テスト1回目 GD : 4, imagick : 10
テスト2回目 GD : 4, imagick : 11
テスト3回目 GD : 5, imagick : 10
テスト4回目 GD : 4, imagick : 9
テスト5回目 GD : 4, imagick : 11
テスト100回してみましたが、GDの方がimagickに比べて2倍の速度があることがわかりました。
画質に関してはフィーチャーフォンでみる分には、ほとんど違いがありませんでした。
* まとめ
ソーシャルゲームでは、どれだけ早くレスポンスを返せるのかということが、ストレスなくゲームを楽しんでもらうための重要な指標のひとつになります。
PHP×ソーシャルゲーム×画像合成にはGDをオススメしたいと思います。
※ 今回のベンチマークで使用した環境はSakura InternetのVPS512プランです。
環境によっては必ずしも同じ結果にはならない事もありますので、ご了承ください。
※ 今回のベンチマークで使用した環境はSakura InternetのVPS512プランです。
環境によっては必ずしも同じ結果にはならない事もありますので、ご了承ください。