Dripです。
デュエルキングさん、こんにちは。
パレットモードは単に色が少ない不便なモード…ではなく、非常に便利なシステム
だったりします。また科学太郎さんが挙げられている情報には一部誤りがありますので
少し説明させていただきます。
>昔、Windows 95 が使えるパソコンには、256色モードと呼ばれる特殊モードがあり、
>同時発光色256色で Windows 95 が使えました。
まずOSから見た256色モードですが、これはWindows95に限ったものではありません。
WindowsXPの時代では現代よりも手軽にOSから256色モードが利用できましたが、
現代のOSでは互換モードやアプリケーションからの呼び出しのみでOSを256色で
利用可能です。
またHSPのscreenで利用するパレットモードはOSの色数に関係なく現代でも普通に
利用することが可能です。
また科学太郎さんがパレットモードの「デメリット」として挙げられている点で
>(1)データ保存するときに1ピクセル3バイトで保存するためにデータ量が増加。
は意味不明で、パレットモードは単純計算でフルカラーモードのデータ量の一律
約3分の1程度に減少します。この恩恵は画像の保存だけでなく、読み込みの際にも
享受することができ、例えば非常に枚数の多い滑らかなドットアニメーションをする際や
リアルタイムにデータを読み込むゲームなどではデータの読み込み速度は約3倍速くなり、
メモリの消費量は約3分の1になります。データが軽くなるというのはリアルタイム読み込みを
必要とするゲーム製作において大変役立ちます。
>(2)フィードイン、フィードアウトを実現するには全データを変更する必要があり面倒。
この全データという表現がかなり曖昧なのですが、フェードアウトのために操作するのは
1画面(バッファやスクリーン)のパレットのみです。
更に、自分がフェードさせたい色を選んでフェードできるというメリットもあり、
フルカラーモードでは到底真似できない便利機能でもあります。
>(4)フルカラー専用の命令が一部使えない。→gcopy の半透明コピーなど
とありますが、フルカラーモードとパレットモードは共存が可能なので、
パレットモードで作成したデータをフルカラーモードのバッファにコピーして
フルカラーのデータとして扱うことはできます。この場合は半透明コピーが可能です。
(パレット←→フルカラーの直接半透明コピーはできませんので注意してください!)
ちなみに(3)で挙げられた色数の縛りにおいては、
システム予約色などのお作法を加味すると自由に利用できる色数はもっと減ったり
するのですが、この話は今回はおいておきましょう^^;
●パレットモードとフルカラーモードを連携させるサンプル
パレットモードはフルカラーモードと連携することで、
フルカラーモードだけでは簡単には実現できない表現も容易に実現できます。
以下のような処理において、たった1枚の画像から様々な色違いキャラを作成して
わらわらと画面の中を歩かせることができます。
フルカラーだけで処理するとなると、別途パーツごとに素材を用意したり、
複数回コピーを重ねたりして、準備やデータが非常に複雑になってしまいます。
ちなみに、hsptvの素材ファイルだけでパレットモードを使わずに以下のような処理を
することはまず不可能です。
(pget/pset等を使って地道にやれば絶対に不可能ではないかもしれませんが…)
randomize
//パレットを定義(通常は256色の画像を使うのでこの定義は不要ですが…)
pal(0)=$000000,$31231e,$47312a,$59453c,$7d6960,$a18d84,$c5b2a9,$8d4949
pal(8)=$b07a73,$d4ab9d,$f8ddc8,$1d1d1d,$fefefe,$002d69,$003f93,$1e56a8
pal(16)=$3e6ec0,$7496c0,$5d0036,$85003d,$a83378,$c759a1,$555555,$c5c5c5
pal(24)=$543b36,$684f4a,$7c635e,$313131,$8d8d8d
myPal(0)=7,4 //肌色のパレット(開始IDと数)
myPal(2)=1,6 //髪の毛のパレット(開始IDと数)
myPal(4)=13,5 //制服のパレット(開始IDと数)
myPal(6)=18,4 //リボンのパレット(開始IDと数)
#define max 32 //キャラクターの最大表示数
buffer 4,256,64*max //フルカラーバッファを最大数分確保
buffer 3,256,256,1 //パレットカラーバッファを素材生成用に確保
repeat 28 //パレットを適用する
palette cnt,pal(cnt)>>16,(pal(cnt)>>8)\256,pal(cnt)\256,(cnt=27)
loop
pos -256,0:picload dir_exe+"\\hsptv\\tamadot.bmp",1 //画像を256色に変換して読み込み
screen 0,640,480 //メインスクリーン初期化(フルカラー)
dim cx,max //キャラクター座標X
dim cy,max //キャラクター座標Y
dim spd,max //歩く早さ
mvx=0,-1,0,1 //各方向(下左上右)に対して歩く量X
mvy=1,0,-1,0 //各方向(下左上右)に対して歩く量Y
wx=ginfo_winx //画面サイズ記憶
wy=ginfo_winy
*main //メインループ
redraw 0:color 255,255,255:boxf //背景クリア
repeat max //全キャラクターの移動と描画
if abs(cx(cnt)-wx/2)>wx/2-2 | abs(cy(cnt)-wy/2)>wy/2-2:{ //キャラが画面外に出た
if mvx(cnt\4)=0:cx(cnt)=rnd(wx-8)+4:else:cx(cnt)=(mvx(cnt\4)=-1)*(wx-8)+4 //X位置再配置
if mvy(cnt\4)=0: cy(cnt)=rnd(wy-8)+4:else:cy(cnt)=(mvy(cnt\4)=-1)*(wy-8)+4 //Y位置再配置
spd(cnt)=rnd(5)+2 //速度再設定
gosub *makeMat //画像再作成
}
cx(cnt)+=mvx(cnt\4)*spd(cnt):cy(cnt)+=mvy(cnt\4)*spd(cnt) //キャラを移動させる
pos cx(cnt),cy(cnt):gmode 2,64,64:grotate 4,time/10\4*64,cnt*64,0,128,128 //キャラを描画
loop
time++ //アニメカウンター経過
redraw 1:await 30 //画面更新とウェイト
goto *main
*makeMat //キャラクターの色を再割り当てしてフルカラーバッファに素材を登録する
gsel 3 //パレットバッファ選択
repeat 4 //肌・髪・制服・リボンの順で色を再割り当て
pp=cnt //パーツID(肌なら0 髪なら1 制服なら2 リボンなら3)
H=rnd(192) //パーツの色合いをランダムで決定する
repeat myPal(cnt*2+1),myPal(cnt*2) //登録されているパレット数分処理
if pp=0:{ //肌の色の割り当て
lv=0.8+0.01*rnd(41) //肌の濃さ(0.8〜1.2)
color pal(cnt)>>16,limit((lv+1)/2*((pal(cnt)>>8)\256),0,255),limit(lv*(pal(cnt)\256),0,255)
}else{ //肌以外の色の割り当て
S=rnd(128)+64 //色の彩度を適当に
V=limit(((pal(cnt)>>16)+((pal(cnt)>>8)\256)+pal(cnt)\256)*3/7,0,255) //色の明度を決定
hsvcolor H,S,V //色を作る
}
palette cnt,ginfo_r,ginfo_g,ginfo_b //作った色をパレットに割り当てる
loop
loop
palette 0,0,0,0,1 //パレット更新
gsel 4 //フルカラーパレットに今作ったキャラの画像を登録する
gmode 0,256,64:pos 0,cnt*64:gcopy 3,0,cnt\4*64 //IDに対して向きは固定なので必要範囲分切り出して登録
gsel 0 //メインスクリーンに戻る
return
●よりシンプルなサンプル
上記のサンプルはややテクニカルなものだったので混乱させてしまったかもしれません。
画像からパレットを抽出して画面に表示する基本的なサンプルも貼っておきます。
次のサンプルは256色の画像を用いた基本的な画像の利用方法です。
こうしてみると結構わかりやすいかもしれません。
(HSPには256色の画像サンプルが非常に少ないです。フルカラーモードの画像からは
パレットが抽出できませんので異常動作となりますのでご注意ください。
自分で256色画像を作る際はEDGE(ドット絵エディタ)などを使いましょう^^;)
#include "hsp3util.as" //bmppalette命令を使うために読み込み
//画像読み込み失敗サンプル
screen 0,512,512,1:cls 4:objsize 100,24,24 //パレットモードで画面初期化
pos 128,300:picload dir_exe+"\\sample\\hgimg3\\testchr.bmp",1 //256色画像を読み込む
pos 10,450:palcolor 255:mes "パレットの状態":gosub *palDraw //パレットの状態表示
pos 10,10 //メッセージ表示
mes "パレットを読み込まずにパレットモードを使うと"
mes "画像から正しく色が読み込めません。"
button "次へ",*page1
stop
*page1 //パレットの割り当てに失敗したサンプル
clrobj //ボタン削除
bmppalette dir_exe+"\\sample\\hgimg3\\testchr.bmp" //後からパレットを読み込むと?
mes "後からパレットを読み込んでも色がおかしくなる。"
gosub *palDraw //パレットの状態表示
redraw 1
button "次へ",*page2
stop
*page2 //画像読み込み成功サンプル
clrobj //ボタン削除
mes "改めて画像ロード。今度は成功。\n画像はパレットを読み込んでから読み出そう。"
pos 128,300:picload dir_exe+"\\sample\\hgimg3\\testchr.bmp",1 //改めて画像ロード
gosub *palDraw //パレットの状態表示
stop
*palDraw //パレットの状態を表示
repeat 256 //全色読み出し
palcolor cnt //パレットモードではパレットIDから色を指定できます。
boxf cnt\64*5+20,cnt/64*5+480,cnt\64*5+23,cnt/64*5+480+3 //塗り
loop
return
●パレットモードの高速描画をテストするサンプル
またパレットモードでは描画速度が非常に高速です。
以下のサンプルではパレットモードとフルカラーモードの動作速度の違いを検証できます。
最初の2行の値を操作して、モードを切り替えたり、最大キャラクター数を変更して
速度の違いを体感してください。
#define palMode 0 //0だとフルカラーモード、1だとパレットモードで試せます。
#define max 160 //最大表示数を変更できます。大きいほど重い。
//★ここの2つの↑数値をいじって動作を変えてみましょう。フルカラーでモッサリでもパレットなら?
wx=ginfo_dispx //画面解像度X
wy=ginfo_dispy //画面解像度Y
//パレットを定義(通常は256色の画像を使うのでこの定義は不要です…)
pal(0)=$000000,$31231e,$47312a,$59453c,$7d6960,$a18d84,$c5b2a9,$8d4949
pal(8)=$b07a73,$d4ab9d,$f8ddc8,$1d1d1d,$fefefe,$002d69,$003f93,$1e56a8
pal(16)=$3e6ec0,$7496c0,$5d0036,$85003d,$a83378,$c759a1,$555555,$c5c5c5
pal(24)=$543b36,$684f4a,$7c635e,$313131,$8d8d8d
buffer 3,256,256,palMode //素材バッファ
#if palMode //パレットモードならパレットを適用
#define mymode "パレット"
repeat 28
palette cnt,pal(cnt)>>16,(pal(cnt)>>8)\256,pal(cnt)\256,cnt=27
loop
#else
#define mymode "フルカラー"
#endif
pos -256,0:picload dir_exe+"\\hsptv\\tamadot.bmp",1 //キャラクター画像読み込み
bgscr 0,wx,wy,palMode,0,0 //メインスクリーン初期化
dim cx,max //キャラクター座標X
dim cy,max //キャラクター座標Y
dim spd,max //歩く早さ
mvx=0,-1,0,1 //各方向(下左上右)に対して歩く量X
mvy=1,0,-1,0 //各方向(下左上右)に対して歩く量Y
font msgothic,32
*main //メインループ
redraw 0
#if palMode //パレットモードの背景クリアと文字色の指定
palcolor 255:boxf:palcolor 0
#else //フルカラーモードの背景クリアと文字色の指定
color 255,255,255:boxf:color
#endif
stick ky:if ky&128:end
repeat max //全キャラクターの移動と描画
if abs(cx(cnt)-wx/2)>wx/2-2 | abs(cy(cnt)-wy/2)>wy/2-2:{ //キャラクターが画面外に出た
if mvx(cnt\4)=0:cx(cnt)=rnd(wx-8)+4:else:cx(cnt)=(mvx(cnt\4)=-1)*(wx-8)+4 //X位置再配置
if mvy(cnt\4)=0:cy(cnt)=rnd(wy-8)+4:else:cy(cnt)=(mvy(cnt\4)=-1)*(wy-8)+4 //Y位置再配置
spd(cnt)=rnd(5)+2 //速度再設定
}
cx(cnt)+=mvx(cnt\4)*spd(cnt):cy(cnt)+=mvy(cnt\4)*spd(cnt) //キャラを移動させる
pos cx(cnt),cy(cnt):gmode 2,64,64:grotate 3,time/10\4*64,cnt\4*64,0,192,192 //キャラを描画する
loop
pos wx/2-128,wy/2-32:mes "終了はESCキーです。\n現在は"+mymode+"モード"
time++ //アニメカウンター経過
redraw 1:await 15 //画面更新とウェイト
goto *main
●まだまだ続く道
さらにさらに、番外編としてパレットモードでは画素を1バイト単位の文字列として
扱うことができたりします。これは上級者向けな使い方ですが、文字列操作命令で
画素の置換やコピーを爆速で行えます。画面内の指定した色の数を一瞬で数えたり
(つまり2Dでスプラトゥーンのようなゲームを作った場合、リアルタイムで画面内の
色の占有率を正確に計算したりできます。)、また特定の画素だけを抽出したり、
さらには地形の標高データをそのまま地図画像に変換したりと
パレットモードの可能性はまさに無限大です。
パレットモードを単に色数が少ないモードと考えるのは非常に勿体無いので是非
色々研究してみてください。理解を深めていくと思わぬところで役に立ったりしますよ。
長々とお付き合いいただきありがとうございました。