スプライトをアニメーション表示にする(手動制御)

スプライトにアニメーションフレームを設定し、フレームを切り替えてアニメーション表示を行います。

2022-11-15
公開

コード

res_image.res

  1. PALETTE tire_palette "tire.png"
  2. SPRITE tire_sprite "tire.png" 4 4

main.c

  1. #include <genesis.h>
  2. #include "res_image.h"
  3.  
  4. #define BUTTON_REPEAT_TRIGGER_COUNT 0
  5. #define BUTTON_REPEAT_INTERVAL 5
  6. #define NUM_OF_ANIMATION 3
  7.  
  8. /**
  9. * コントローラの状態
  10. */
  11. typedef struct {
  12. u16 current; // 押下しているキー
  13. u16 trigger; // 押し始めたキー
  14. u16 off; // 離したキー
  15. u16 repeat; // 押し続けているキー
  16. u16 repeatCount; // リピートカウント
  17. } JoyState;
  18.  
  19. /**
  20. * コントローラの状態を保持するグローバル変数
  21. */
  22. JoyState joyState;
  23.  
  24. /**
  25. * コントローラ状態の初期設定
  26. */
  27. static void joyInit() {
  28. // 初期化(0埋め)
  29. memset(&joyState, 0, sizeof(JoyState));
  30. }
  31.  
  32. /**
  33. * VBlankごとに1回だけボタンの情報を取得する
  34. */
  35. static void joyExec() {
  36. u16 state = JOY_readJoypad(JOY_1);
  37.  
  38. joyState.trigger = state & ~joyState.current;
  39. joyState.off = joyState.current & ~state;
  40. joyState.current = state;
  41.  
  42. // 「押し続けているキー」の設定
  43. // NOTE: 「押し続けているキー」は、押し始めたあと指定の時間無効化され、
  44. // それ以降押下している間は一定の間隔でオンオフを繰り返す
  45. if (joyState.trigger) {
  46. joyState.repeat = joyState.current;
  47. joyState.repeatCount = BUTTON_REPEAT_TRIGGER_COUNT;
  48. }
  49. else if (!joyState.repeatCount) {
  50. joyState.repeat = joyState.current;
  51. joyState.repeatCount = BUTTON_REPEAT_INTERVAL;
  52. }
  53. else {
  54. joyState.repeat = 0;
  55. }
  56. // カウント
  57. if (joyState.current) {
  58. if (joyState.repeatCount > 0) {
  59. joyState.repeatCount--;
  60. }
  61. }
  62. }
  63.  
  64. int main(u16 hard) {
  65. // タイルのVRAM位置
  66. u16 index = TILE_USERINDEX;
  67.  
  68. // コントローラ状態の初期化
  69. joyInit();
  70.  
  71. // 色の設定
  72. PAL_setPaletteColors(0, &tire_palette, DMA);
  73.  
  74. // スプライトエンジンの初期化
  75. SPR_init();
  76.  
  77. // スプライトの追加と取得
  78. Sprite* tire = SPR_addSprite(&tire_sprite, 144, 88, TILE_ATTR_FULL(PAL0, FALSE, FALSE, FALSE, index));
  79.  
  80. // 操作説明テキスト
  81. VDP_drawText("A Button:Next Frame", 9, 17);
  82. VDP_drawText("B Button:Next Animation", 9, 18);
  83.  
  84. // メインループ
  85. while (TRUE) {
  86. SYS_doVBlankProcess();
  87.  
  88. // ボタン状態取得
  89. joyExec();
  90.  
  91. if ((joyState.trigger & BUTTON_A) || (joyState.repeat & BUTTON_A)) {
  92. // スプライトのフレームを1つ進める
  93. SPR_nextFrame(tire);
  94. }
  95. if (joyState.trigger & BUTTON_B) {
  96. // スプライトのアニメーションを次のセットにする
  97. SPR_setAnim(tire, (tire->animInd + 1) % NUM_OF_ANIMATION);
  98. }
  99.  
  100. // スプライト更新(スプライトに対して行った変更が反映される)
  101. SPR_update();
  102. }
  103.  
  104. return 0;
  105. }

実行結果

上記コードをSGDK1.70でコンパイルし
Gens v2.14 Souvenirで実行したスクリーンショット

解説

画像

スプライトをアニメーション表示にするためには、表示したいフレーム分のイメージを設定した1枚画像を用意します。
今回使用した画像で説明します。

使用したスプライト画像

アニメーションスプライトは「animation」セットの中にそれぞれの「frame」を持った構造になっています。
画像の横に並んだ部分が1つのアニメーションセットとなり、そのセットを等間隔で分割したフレームが1枚ずつ表示されることによりアニメーション表示となります。
今回の画像では以下のようなアニメーションスプライトになります。

2フレームのアニメーションセット0、4フレームのアニメーションセット1、4フレームのアニメーションセット2です。フレーム数は異なっても問題ありません。透明色のみのフレームは無視されます。

ResComp

画像を用意したら、次はResComp1のためのresファイルの設定です。今回使用したresファイルのスプライト部分の各項目の意味は以下になります。

SPRITE
リソース種別
tire_sprite
スプライト名
"tire.png"
画像のパス
4
幅(タイル数)
4
高さ(タイル数)

ここで設定した幅と高さで対象画像が分割され、アニメーションデータとして格納されます。

VRAM

スプライトで使用するタイルは、自動でVRAM2にアップロードするかしないかを設定できます。
自動アップロードが有効の場合はフレームが切り替わるたびにVRAMの該当位置にあるスプライトタイルが置き換わります。
無効の場合は手動でスプライトタイルをVRAM上にアップロードし、フレームを切り替える際にそのタイル位置を指定する必要があります。
VRAMに保持出来るデータ量(タイル数)には制限があるため、表示する画面によっては自動アップロードの有効無効を上手く切り替える必要があるでしょう。
画面上に1つしか存在しないスプライト(操作する主人公など)は有効に、複数登場するスプライト(雑魚敵など)は無効にするのが切り替えの基準になるかもしれません(参考: ガンスターヒーローズのVRAM使用状況(記事中盤の動画))。

ダウンロード

1画像や音楽データをSGDKコード内で使える形に変換してくれるツールです。
様々な形式に対応しており、また各形式によって指定できるオプションが異なるため、rescomp.txt(sgdk\bin\rescomp.txt)を確認することを推奨します。

2画面に表示するデータの汎用ストレージ領域