キャラクター描画の秘密

GARUDAの内部でキャラクターをどうやって生成しているかについて。
実際のところめちゃくちゃキタナイし、ショート制限でもなければ不要なんで
参考にしにくいと思いますが…とりあえず、サンプルコード。

;---------- おまじない
fn="MS ゴシック"
dim ev,8,jmm
dup eez,ev	; オブジェクト状態値
dup eex,eez.1	; オブジェクト位置x
dup eey,eez.2	; オブジェクト位置y
dup eed,eez.3	; オブジェクト進行方向
dup ees,eez.4	; オブジェクト移動速度
dup eek,eez.5	; オブジェクト属性値
dup eeh,eez.6	; オブジェクト耐久値
screen 2:boxf
#const  cwid 63
#const  cwid01	cwid-4
#const  cwid02	cwid/2
#const	cwid03	cwid-8
;---------- おまじない終わり ここから下がキャラ生成処理

;  84218421842184218421842184218421
;      2222222211111111SSSSssssVVMM		YYYYy:重ね合わせのY差分、MM:Y増分
;  YYYYy                   HHHHhhhh		2:2文字目、1:1文字目、HSV:色情報初期値HSV形式
;                  Y2211SHV
#const Fm0	$B9ac84A5	; 自機パーツ1
#const Fm1	$04062809	; 敵レーザー
#const Fm2	$05df5839	; 敵ミサイル
#const Fm3	$49df630d	; 地上砲台
#const Fm4	$0e6967ad	; 雑魚(反転)
#const Fm5	$Ba5a3779	; 雑魚(回転)
#const Fm6	$Ba69f46e	; 雑魚(誘導)
#const Fm7	$3becd75d	; 雑魚(直進)、中ボス
#const Fm8	$Acdaa599	; ボス胴体
#const Fm9	$Bc9a676a	; ボス頭部
#const FmA	$2887b464	; 自機パーツ2
du=Fm0,Fm1,Fm2,Fm3,Fm4,Fm5,Fm6,Fm7,Fm8,Fm9,FmA
ak="●"

gmode 5,640,90,256	; ダメージ表現用キャラの作成に加算Copyを使う
repeat 11 ; キャラクターの種類だけまわす
  ;各種パラメータ初期化。
  eex=cwid03,5,du(cnt),eed&$FF,(eed&$c)<<4,(eed>>4)&$FF
  ;eex=FontSize、eey=Y座標、eed=データ、ees=H成分、eek=V成分、eeh=S成分
  repeat 15,cnt\10*cwid+4  ; このループのcntはX座標として使う
    font fn,eex            ; 
    poke ak,1,eed>>20      ; 2文字目コード定義
    pos cnt,eey:hsvcolor ees,eek,eeh:mes ak ; 表示
    poke ak,1,eed>>12      ; 1文字目コード定義
    pos ,eey+(eed>>27):hsvcolor ees+33,eek,eeh:mes ak ; 表示
    ; 2文字目表示位置は、X省略、Yは重ねあわせ差分(±)を足す。色相は+33。
    pos 0,$c0:gcopy 2      ; ダメージ表現用のキャラ作成場所に加算Copy。
    eey+=(eed&3)   ;Y座標をずらす(eed&3=1だと同心位置)
    eeh+8          ;輝度加算
    eex-2          ;FontSizeを減算
  loop
loop

おまじないを除いて、AXにして477Bytesの処理となっています。
キャラ生成処理はほぼ実際のコードと同じで、いくつかの省サイズ対策があります。
(おまじないは、サンプルを正常に動作させるためのもので、実際とは異なります)


●データの持ち方
キャラ画像は2個(自機は4個)の記号文字を、サイズや色を変えながら重ねて作っています。
キャラデータの1セットを32bitの中に埋め込んでいます。
情報の持ち方はコメントにあるとおり。
文字コードがバイト単位に区切れていませんが、このビットの配置には理由があります。
Yの増分(MM)やHSV情報は、命令などで使うまでに値を整形しておく必要があります。
下位に集めたこの配置ならMMとHHHHhhhhについてはビットシフトが不要になります。
重なる部分がありますが、HやSの下位4bitは値への影響が小さいので考えません。
2文字を重ねるときの差分(オフセット)は、上位5bitを使い-16〜+15の範囲をとります。
HSPの>>は算術シフトなので、最上位ビットが符号として扱える点を利用しています。
正の値しかないと負方向のオフセットを使うには面倒ですが、この位置ならはじめから
正負の値として持たせることができるので、オフセットの指定が楽になります。
文字情報はPOKEで使用するときに値を$FFでマスクしなくても大丈夫なので、中間へ配置。
これらの情報を#constで宣言して、配列duで保持しています。


●1つ目のRepeat−Loop
最初に描画のためのパラメータ初期化をしています。
配列にDupしてあるeex以降の変数を、一気に代入しています。
この代入では、前の要素で計算した値を後の要素の代入ですぐに参照していたりします。


●2つ目のRepeat−Loop
このループのcntは描画位置のX座標として使います。
そのために、Repeat命令のP2を与えていますが、参照しているcntは上のループの値です。
10で割った余りを使っているのは、自機のために2セットの画像を重ねるためです。
文字コードを作るPokeコマンドでは、書き込む値が下位1バイトのみ有効なことを利用して、
値のビットシフトのみで整形を済ませています。
2個目のpos命令ではX座標の指定を省略し、1文字目と同じX座標にしています。
バイト数削減にも寄与しています。
あと、colorではなくhsvcolorを使うことで、色替えと輝度変化の制御を楽にしています。


実行してみると一目瞭然ですが、プレイ中に見るよりずっとシャープなラインです。
grotateで、これらのキャラ画像を拡大縮小回転させて表示しています。