SGDKからC言語を知る

SGDK公式GitHub、「Tuto Introduction」冒頭の「Disclaimer(免責事項)」にこう書かれています。

First, you need to know that SGDK uses C language (assembly is also possible but not necessary)
so it’s highly recommended to be familiar with C programming before trying to develop with SGDK !
Trying to learn C language at same time than learning Sega Mega Drive programming is
definitely too difficult and you will end nowhere.

訳すと、

まず、SGDK は C 言語を使う事を知っておく必要があります(アセンブリも可能ですが、必須ではない)。
そのため、SGDK で開発する前に C プログラミングに精通しておくことを強くお勧めします。
メガドライブのプログラミングを学ぶのと同時に C 言語を学ぼうとすると、
間違いなく難しすぎて行き詰まってしまいます。

バカ言うなと。

あのな、メガドライブでゲーム作れるっていうからC言語勉強できてんだぞこっちは。
なんてね、ワタシもこの文見て…まあそうだよなと思い、
テキスト買ったり、有料講座みたり、C言語のサンプルやったりしてさ。printf、つってさ。
でも、限界あるから。
我ながらひどいこと書いてるわ。でもそうなんだもん。しょうがないよ。
ゲーム作れるC言語講座もあるけど、何かライブラリ的なものと組み合わせるか、テキストベースじゃん。
そしたらメガドライブにダイレクトに行きたくなっちゃうって。
ということでワタシの復習をかねて、ここまで勉強してきたC言語の話をつらつら書いていきます。


重要。この文は実務経験ないながらもメガドライブへの情熱だけでCの基礎を学習し、
SGDKを作るためのシロウトリファレンス、あるいは備忘録として書いているものなので、
C言語そのものの学習には使えません。
やめろ!見るな!そんな事も知らないの?って言うな!知らないんだよ。


頭に刻んだ超前提のはなし

・書いた文はきっちり上から実行される。順番を意識すること。
・16進数は0xFFというカンジで最初に「0x」と書く。
・サンプルコードなどは小規模なのでそうなっていないが、プログラムは「機能ごと」で分割する。らしい。
・いろいろ分割したとして、最初に稼働するのは「main」関数と内部で決まっている。不変の要素。
・mainの中の「while」ループは、フレームごとに更新する要素を記載する模様。
・mainの中が1/60秒の速度で延々繰り返される=60fps。負荷が高くなると遅くなる(ゲームあるある)。

書き方の基本形(BGに絵を映してちょっとパレットいじる程度のごく簡単なプログラム)

//main.c 
#include <genesis.h> 
        //SGDKのベースとなるgenesis.hを読み込む。
#include <res01.h>
        //リソースファイルのリストを読み込む。
        //「ファイル名.res」ではなく「ファイル名.h」。生成されたヘッダを読ませる。
     //その他、分割して作ったプログラムはすべてヘッダとして読み込ませる。

/*
* main関数までに必要な設定などを書いておく。
* 使いたい関数などは事前に宣言されていないとエラーになってしまうので
* ここに書いておかなければならない。
* サンプルプログラムとかは短いので処理丸ごとここに書いてもいいが
* 先に関数だけ書いておいて、あとで処理を記載するパターンもある(プロトタイプ関数)。
* ヘッダーもプロトタイプ関数と思っておく。
*/

int main() //この前に書いてあるヘッダやプログラムを読んで、ここから処理が実行される
{
    //下記のようにゲーム更新に必要なプログラムはmain内に書いていく
    //SGDKが提供するVDPやPALといったコマンドは、ほとんどここに来る。
    //スプライト初期化などもこのあたり。
    VDP_drawImage(BG_A,&rakugaki01,0,0);
    PAL_setColor(9,0x0EEE);


    while(1) //ここが毎フレーム延々ループになる箇所
    {
        //このあたりにスプライトやカメラなどの状態を更新するような命令を描く
        SPR_update();

        //SYS_doVBlankProcessは画面更新の際に記述されたプログラムを
        //一気に実行してくれるコマンド。
        //必須。そもそもプロジェクトを新規作成すると勝手に入ってくる。
        SYS_doVBlankProcess(); 
    }
    return (0);
}

Rescompの話でやってきたことを頭から書くとこういうカンジ。


これをふまえて。
OHSAT GAMESさんのサンプル「MEGA RUNNER」
#1~#6(BONUSなし)まで記述したものを、振り返りということでひとつひとつ解説をつけてみました。
MEGARUNNERはSGDKのバージョンが古いため、コマンドの変更が必要なのですが、
逆にそれによって学習をより深めることができ…非常に良かったです。
1年前はただ書き写すだけだったので、少しはできるようになりましたかね。それではどうぞ。

//スラッシュ2回でコメント化。Cisco iosの「!」と同じか。
/*スラッシュとアスタリスクで最初と終了を指定する書き方もあるけど
なんかあんまりスキくない(笑)。慣れたら使うのかも。*/

//なお今回のSGDKのバージョンは2.00です。

//基本。SGDKの呼び出し。
#include <genesis.h> 
//リソースファイルの呼び出し。
#include <resources.h> 
//↓サンプルで書くよう記載されているが、不要。genesis.hの中に入っている。
//#include <string.h> 


//アニメーションのdefine定義
//defineとは、ある値、数式などに名前をつけて、
//プログラム内で使用できるようにするマクロのようなもの。
//下記の場合、ANIM_RUN・・・とこのあとのプログラム内で書くと、0になる。
#define ANIM_RUN	0
#define ANIM_JUMP	1


//  w
//('=') とり


//垂直変数(ジャンプに使う)
//
//fix16はSGDKのtypes.hに用意されている、typedef宣言のひとつ。
//typedefはC言語において新しい「型」を定義するもの。[typedef 新しい型の形 新しい型名]と書く。
//types.hには[typedef s16 fix16]とある。s16もtypes.hにあり、[typedef short s16]とある。
//shortはC言語で2byte=16bit整数のこと
//(16bitは65535だが、(signed)shortは32767 ~ -32768という割り当て)。
//てことはfix16じゃなくてs16って書けばいいのでは…?
//ただfix16の説明には[16 bits fixed point (10.6) type]とあり、
//fixed point=不動点(固定小数点も検索出るけどここでは不動点が正解だと思う)ということで
//これを調べると、要するに点で区切ることで桁数がマスクされるらしい。
//上記の場合は10bit使用、6bit未使用と。
//shortやintは先頭bitで正負を表現する(先頭bitが1でマイナス判定)ようなので、
//数値で使うのは9bit。つまり「511~-512」が範囲。
//なんだろう、生のs16だとデカいから、メモリ節約とかそんなカンジか?
//
//で結局これは、player_vel_yはプレイヤーの初期垂直速度(velocity)、
//player_yはプレイヤーの初期位置を記載している。普段は垂直に動かないので、0が入っている。
//player_yの値がサンプルと違うのは、絵を大きいのに変えたから。
fix16 player_vel_y = FIX16(0);
fix16 player_y = FIX16(104);


//ジャンプ用変数
//
//[int]はC言語の型で整数を表す。[const]を宣言すると、変更できない固定の変数となる。
//このゲームにおいて床の高さは不変なので、constをつけている。
//
//また結構参考書に書かれていることだが、C言語標準にはTRUE/FALSE2値のbool定数がない。
//作業を容易にするために、SGDKがbool型をサポートしている( TRUE == 1,FALSE == 0)。
//
//player_heightなどの値がサンプルと違うのは、絵を大きいのに変えたから。
int player_height = 24;
const int floor_height = 128;
fix16 gravity = FIX16(0.2);
bool jumping = FALSE;


//ゲーム内メッセージ
//
//[char]はC言語の型で、文字変数を表す。1文字に1byteのデータが割り当てられている。
//なので内部処理的には文字そのものではなく、データである。モジ・ソ・ノモノ。言いたかっただけ。
//この割り当てが合わないと(ANSIとUTF-8とか)ネットの文字化け食らったりするんだね。
//参考書によるとcharで指定する数字(配列)は、最低でも文字数に+1するのが正しく、
//これは文字の終わりを告げる「\0(null文字)」の格納が必要であるため。だから[22]。
//配列数を省略した場合、自動で確保される。多く確保した場合は、ムダに確保される(笑)。
//
//「""」で記述された文字列は文字列リテラル(定数。まんま書いてるので直値とも)と呼ぶ。
const char msg_start[22] = "Press START to Begin!";
const char msg_reset[22] = "Press START to Reset!";
bool game_on = FALSE;


//スクロールスピード
//
//速度が変わるゲームならconstは使わないんだろうなと想像…。
const int scrollspeed = 2;


//プレイヤー設定
//ここめちゃくちゃ学習ポイント多い…
//
//[*]はポインタ型。サンプルは[int* player]と書いてあったが
//いろいろ参考書を読むに[int *player]のがミスしにくい模様。確かにね。ゼロ災でいこうヨシ!
//
//ポインタとは、メモリのアドレスを参照する型。データが入っている箱の"場所"を読みだしている。
//要するに脳を直接見るんでしょ?そういう特殊性癖あるよね(ポインタをそう捉えていいの?)。
//それじゃダメか。ヘブンズドアーーーッ!!!!!にしとこうか。
//まあただでさえ難しいポイント概念なので、冗談抜きでそういう自分内置き換えは効果あると思う。
//
//[Sprite]は、「SPR_addSprite」とセットで使う構造体(常にそう使えと説明が出てくる)。
//スプライトを扱うときにはまずこれで箱を用意して、
//SPR_addSpriteで細かな設定を入れるのがセオリーなのかな?
//このSprite構造体には[status/visibility/spriteDef/..../data/prev/next]といった
//16個の要素が格納されている模様。とりあえずここまで。
//
//このゲームはプレイヤーにx軸の動きがないので、const intで32に固定している。
Sprite *player;
const int player_x = 32;

//int player_y = 104;
//↑サンプルのint player_yは、fix16 player_yを追加したタイミングで削除要。
//矛盾(conflicting)が発生する…矛盾?shortとintの不一致かな?


//障害物設定 Obstacle(ジャマ)。
//初期位置を画面外、速度(velocity)を0にしている。
Sprite *obstacle;
int obstacle_x = 320;
int obstacle_vel_x = 0;


//最初のテキスト表示。動いているけど、ここに書くのでよかったんだろうか…。
//まあでも上から順に読まれているんだろうから、いいのかな。
//
//[void]は数字のゼロと同じで「無」を表す(もしくは大森靖子の曲)。
//関数には引数(入力)と戻り値(出力)を設定するが、
//void型は戻り値なし。()が空白もしくはvoidの場合、引数なし。
//このパターンは引数があり(char s[])、戻り値なしということ。
//
//[VDP_drawText]はまんまテキスト表示。(str,x,y)を指定。strはstring=長さのこと。
//strは引数s[]=長さ無指定のため文字数に依存、
//今回表示されるテキストはいずれも21文字なので、+1の原則で22となる…で合ってる?
//xはstrlen(s)=文字の長さ(22)を2で割って20を引いた位置=9タイル目(0から始まる事に注意)
//20から引いてるのは中央寄せのゲタ履かせかな。yは純粋に10タイル目。
void showText(char s[]){
	VDP_drawText(s, 20 - strlen(s)/2 ,10);
}


//ボタン押した後のテキスト消去。これも動いてるけど…まあいいか…。
//(x,y,w)を指定。wは長さ。タイル指定なので、実質文字数。32文字。
//元の解説にもある通り、y=10タイル目の高さにあるテキストをほぼ消し去るような設定。
void clearText(){
	VDP_clearText(0,10,32);
}

//ゲームスタート処理
void startGame(){

	//障害物を右端に動かす(位置のリセットを行う)
	obstacle_x = 320;

	//先に書いたテキスト消去の処理が走る
	//
	//if文は()の中の条件を見て、該当すれば{}の処理を行う。
	if(game_on == FALSE){
		game_on = TRUE;
		clearText();
	}
}

//ゲームオーバー処理
void endGame(){

	//先に書いたゲームオーバー的文字列を表示させるため
	//showTextの引数を[msg_reset]に書き換えている
	//ところでmsg_reset、msg_startは"const(定数)"設定がどっかおかしいみたい…なんなん
	if(game_on == TRUE){
		showText(msg_reset);
		game_on = FALSE;
	}
}


//ジョイパッド処理
//
//このmyJoyHandlerは別にSGDKの機能で提供されているわけではなく
//引数joy,changed,stateを格納するための、普通のvoid型。
//JOY_1やBUTTON_STARTなどがSGDKで提供されているボタンを処理させる機能。
//
//[u16]はunsigned short型。unsignedは正の数のみということ。16bitの正の整数すなわち「65535~0」。
//いまさらだけどsignは符号か。unsignedは頭の正負判別する符号がないってことか!ははあ。
//
//[joy]ジョイパッドのこと。通常はJOY_1かJOY_2。SGDKは8本までサポート。セガタップ2個刺しか…。
//[changed]ボタンが前フレームから変化したかどうか。違うなら1、同じなら0。
//[state]ボタンが押しっぱなしかどうか。押しっぱなしは1、離れていれば0。
void myJoyHandler( u16 joy, u16 changed, u16 state)
{
	if (joy == JOY_1)
	{
		//Start game if START is pressed(一応サンプル原文のせといた)
		//スタートボタンが押されたか(state)チェックしている。
		if (state & BUTTON_START){

			//スタートボタンが押されたとき、game_onがFALSEであれば
			//startGameを走らせる
			if(game_on == FALSE){
				startGame();
			}
		}
		//Cボタンチェック
		//ジャンプしてない時に押したらジャンプ判定とし、playerの垂直速度(vel_y)を上げる。
		//その時のアニメーションも指定している。
		if (state & BUTTON_C){
			
			if(jumping == FALSE){
				jumping = TRUE;
				player_vel_y = FIX16(-4);
				SPR_setAnim(player,ANIM_JUMP);
			}	
		}
	}
}



//スコア定義
//
//元サンプルに記述のあった"SCORE\0"の\0が意味不明だったんだけど、
//これはchara配列最後に来る"\0"を明示して書いてるんだね。
//知ってなお、これを書いておく良さがマジでわからない(笑)。実際の現場ではどうなんだろう。
int score = 0;
char label_score[6] = "SCORE\0";
char str_score[3] = "0";
bool score_added = FALSE;


//スコア表示
//
//sprintfはCの教本によくでてくる「文字列出力関数」。
//stdio.hの機能ってみたけど使えるってことはSGDKのgenesis.hにもあるのであろう。
//
//最初に指定されるのがバッファ。[str_score]の中に文字をためていく。
//次にフォーマットを決める。””の中に%dなどの指定子を並べて、
//その後に指定子のある順番で入れる値を指定する。
//%dは10進の整数。入れる値は先に作成した[score]。
//こうすることで文字列を成型できるらしい。
//数字一つでここまでする意味はたぶん無くて、ナレッジ提供してくれるサンプル製作者のやさしさ。
void updateScoreDisplay(){
	sprintf(str_score,"%d",score);
	VDP_clearText(1,2,3); //サンプル通り一応書いたけど、x1y2の3文字って座標おかしいような?
	VDP_drawText(str_score,10,2);//指定座標に文字が書かれる
}

//ここまでで情報定義や挙動を記述し、
//次からの[int main]の中でアウトプット処理を行うイメージ。

int main()
{

	//背景色を変更する
	//
	//VDP_setPaletteColor(0,RGB24_TO_VDPCOLOR(0x6dc2ca));
	//というサンプルの[VDP_setPaletteColor]…は、現在使われないコマンドで、
	//PAL_setColorなどを使いなさいとあるので
	//元のコマンドに近しい[PAL_setPaletteColors]を使ってはみたものの。
	//パレットのindex指定してもほかの色めっちゃバグるし
	//サンプルにあるRGB24_TO_VDPCOLORの指定もマジ思った色にならないしで
	//パレットの1色を背景色に置き換えるコマンドでとりあえず終えた。
	VDP_setBackgroundColor(28);

	//コントローラ入力
	//
	//JOY_init();
	//てのをサンプルで書いてるけど書かなくても勝手に呼んでるみたいなのでコメントアウト。
	//下の[JOY_setEventHandler( &myJoyHandler );]と書くことで
	//前に[myJoyHandler]として指定した挙動を読むみたい。
	//
	//"&"は「アドレス演算子」その変数のアドレス、つまり場所そのものを参照しに行っている
	JOY_setEventHandler( &myJoyHandler );
	
	//背景サイズ
	//
	//VDP_setPlanSize(32,32);
	//…というサンプル記述は古いので修正。32か64か128しか指定できない様子。
	//これは背景に設定するキャンバスの広さと理解しています。
	//32*32タイルのキャンバスができて、そこにジグソーパズルのようにおいていくイメージ。
	//画面サイズは320*224=40*28タイルで足らないんだけど、ループするので問題ないみたい。
	//ちなみにこの記述自体消したらバグった。たぶんデフォルトが違う値で、
	//後に出てくる[VDP_fillTileMapRect]の塗りつぶし指定値が足りないからと見えた。
	//背景にどれくらいのVRAM容量を割くかということにもつながるようなので
	//ギリギリを攻めるのがいいんでしょうね。
	//最後のboolはよくわかんなきゃTRUEにしとけって公式解説にあったからそうした。
	VDP_setPlaneSize(32,32,TRUE);
	
	//スクロールモード
	//
	//両方指示することで縦横移動可能となるから入れとくといい…というサンプルの説明だったが
	//あってもなくても同じように動くので、
	//8px単位でスクロールさせたいとかあれば使うでいいよな?なのでコメントアウトで切ってみた。
	//VDP_setScrollingMode(HSCROLL_PLANE,VSCROLL_PLANE);
	
	//タイルをVRAMに保存
	//
	//floor画像をタイルマップのVRAM1に。
	VDP_loadTileSet(floor.tileset,1,DMA);
	//wall画像をタイルマップのVRAM2に。
	VDP_loadTileSet(wall.tileset,2,DMA);
	//light画像をタイルマップのVRAM3に。
	//この画像は6セルのため、VRAM3を指定すると実際は4~8も使われる
	VDP_loadTileSet(light.tileset,3,DMA);

	//パレット変更
	//
	//VDP_setPalette(PAL1, light.palette->data); は古い記述なので改めてます
	//パレットをlightからPAL1(0~3の1。パレット2行目)へ読み込み。 
	PAL_setPalette(PAL1, light.palette -> data,DMA);
	//プレイヤーカラーパレット 今回はlightと共通なので不要だが後学のために分けてみてる
	PAL_setPalette(PAL2, runner.palette -> data,DMA);
	
	//背景を実際に画面に描写するくだり
	//
	//VDP_fillTileMapRectは、背景PLANE,タイル,開始位置x,y,塗りつぶす幅,高さでできている。
	//タイル指定のTILE_ATTR_FULL(attributesは属性)
	//
	//BG_Bにfloor(VRAM1)呼び出し。
	//(32*32のPlaneSizeに)0,16の位置から横32セル、縦1セルで塗りつぶし。主人公が接する面
	VDP_fillTileMapRect(BG_B, TILE_ATTR_FULL(PAL1,0,FALSE,FALSE,1),0,16,32,1);
	//BG_Bにwall(VRAM2)呼び出し。0,17の位置から横32セル、縦14セル塗りつぶし。接する面より下
	VDP_fillTileMapRect(BG_B, TILE_ATTR_FULL(PAL1,0,FALSE,TRUE,2),0,17,32,14);
	//BG_Bにlight(VRAM3)呼び出し。2*3=6セル画像のため、2,3指定。
	//描画順は左から右に、そのあと一つ下に下がる。
	//[1][2]
	//[3][4]
	//[5][6]
	//なお、左右対称などにより同じ内容とみなされたセルはVRAMに保存されない。
	VDP_fillTileMapRectInc(BG_B,TILE_ATTR_FULL(PAL1,0,FALSE,FALSE,3),15,13,2,3);

	//スプライト初期化
	//
	//スプライト(SPR)描写の前にこれを書いておかないとバグる。
	//昔は(0,0,0)だったようだが今はvoidのため入力しない
	SPR_init();


	//プレイヤーの配置と表示
	//
	//[SPR_addSprite]の設定値は
	//spritedefinition(リソースのスプライト名),x初期値、y初期値、attributes。
	//runnerの絵を読み、player_x,player_yの初期位置で、
	//attrはパレット、優先度、上下/左右反転を指定。
	//
	//playerのスプライトに、アニメを関連付け。最上段近くの記述の通りANIM_RUN = 0。
	//0番目を指定しているので、画像最初の歩行パターンを読み込んでいる。
	player = SPR_addSprite(&runner,player_x,player_y,TILE_ATTR(PAL2,0,FALSE,FALSE));
	SPR_setAnim(player,ANIM_RUN);

	//障害物の配置と表示
	//
	//高さが固定なのでふつうに128て書いちゃってる
	obstacle = SPR_addSprite(&rock,obstacle_x,128,TILE_ATTR(PAL2,0,FALSE,FALSE));

	//スタートメッセージ表示
	//
	//最初から表示して1回きりなのでまあどこにあってもいいのであろう…
	showText(msg_start);

	//スコア表示
	//
	//たぶん念のためもう一度初期化して、画面を更新させている。
	//ついでに障害物の位置もリセットしている。
	VDP_drawText(label_score,10,1);
	score = 0;
	updateScoreDisplay();
	obstacle_x = 320;

	//スクロールスピード格納。whileループの直前に必要。
	int offset = 0;

	//ここからのwhileループは、毎フレームの動作に影響するものを書く。
	//例えば何かの動きなど。
	while(1)
	{
		//スクロール
		//
		//1行目でスクロールの定義。
		//冒頭定義した[const int scrollspeed]の通り-2px単位で進んでいくが、
		//このままだとアンダーフローを起こす可能性があるため(実際ならないっぽいけど)
		//2行目でリセットしてあげている。
		//背景の大きさは縦横32セルで定義したため、32×8pxで256px。
		//なので、256進んだところで一度0に戻してあげれば
		//きれいな背景ループが成立する。
		VDP_setHorizontalScroll(BG_B, offset -= scrollspeed);
		if(offset <= -256) offset = 0;

		//ジャンプ速度 
		// 
		//player_y = fix16Add(player_y, player_vel_y); 
		//…の[fix16Add]は廃止、ふつうに加算してって公式に怒られたのでふつうに加算した。 
		player_y = player_y + player_vel_y;

		//重力
		// 
		//fix16Addの部分はジャンプ速度同様普通の加算に変更。 
		//ジャンプ状態の時、player_vel_y(ジャンプ時-4)にgravity(0.2)が毎フレーム足され、
		//山なりにジャンプしたように見える 
		if(jumping == TRUE) player_vel_y = player_vel_y + gravity; 

		//プレイヤーが地面にいるかの判定 
		// 
		//ジャンプ状態、かつ(&&)、
		//床の高さの位置数がプレイヤーの位置数以上(床と同じかそれより下にいる)時、
		//ジャンプ状態を解除し、速度(velocity)を0にして落ちないようにし、
		//プレイヤーを床の上に移動(床の高さからプレイヤーの高さを引いてメリコミ防止)し、
		//アニメを走る状態に戻し、スコアも加算しない状態にする。
		 if(jumping == TRUE && fix16ToInt(player_y) + player_height >= (floor_height)){
		jumping = FALSE;
		player_vel_y = FIX16(0);
		player_y = intToFix16(floor_height-player_height);
		SPR_setAnim(player,ANIM_RUN);
		score_added = FALSE;
		}

		//障害物の動き
		//
		//[-scrollspeed]とすることで障害物の速度に地面のスピードと
		//同じ速度をあて(2に"-"つけて-2)、
		//次の行で障害物の位置に速度を加算して動くように(0 + -2 = -2)設定。
		//if文は、障害物が左端を超えたら(障害物が8pxのためそれを超えたら)
		//また横位置を元の320に戻している。
		obstacle_vel_x = -scrollspeed;
		obstacle_x = obstacle_x + obstacle_vel_x;
		if(obstacle_x < -8) obstacle_x = 320;

		//障害物との衝突
		//
		//簡単な作りのゲームなので、判定は水平位置のみ、ジャンプ中かどうかで判定させる。
		//最初のif文で、プレイヤーと障害物は双方8pxの中にいるかを確認し、
		//次にジャンプ中じゃなかった場合はゲーム終了、
		//ジャンプ中だった場合(else)、
		//スコアが増えていなければインクリメント(++)してスコア増boolをTRUEにする。
		//スコア増boolがないと、ジャンプ中に毎フレーム判定されて1回に7点ほど入っちゃう。
		if(player_x < obstacle_x + 8 && player_x + 8 > obstacle_x){
			if(jumping == FALSE){
				endGame();
			}
				else{
		    		if(score_added == FALSE){
					score++;
					updateScoreDisplay();
					score_added = TRUE;
					}
				}
		}

		//ゲームが開始されたときの処理
		//
		if(game_on == TRUE){
		//障害物を置く
		SPR_setPosition(obstacle,obstacle_x,112);
		//プレイヤーを置く
		SPR_setPosition(player,player_x,fix16ToInt(player_y));
		//スプライト初期化。ゲームを始めるときに。
		SPR_update();
		}

 		//これ、最後に絶対必要。
		//テレビの走査線が上から右に行き、1段下がってまた右に…と
		//描画していって(H(orizontal)blank)、最後一番右下に到達した後
		//機械的に一番左上に戻る、その瞬間を[V(ertical)Blank]と呼ぶ。
		//VBlank中は当然ながら画面描写されないため、何か処理をする絶好の機会となる。
		//このコマンドを入れることでこれまで記述したVDPやSPRといったコマンドが
		//VBlank中に実行されて画面に反映される。
		SYS_doVBlankProcess();
	}
 
	//mainの戻り値。あんまり意味わかってない(笑)。
	//必ず最後に書く、C言語のお約束かなくらいに思っている。
	return 0;
}

 

 

 

 

そんなこんなでコンパイルしたゲームはこちら。

残業多い民なので、1年かけてようやっとこんなカンジです。
ライフワークにしたいとはいえ、もうちょっとペース上げたいですけど…。
もう年の瀬。来年もがんばっていきたいですね。


おまけ。
C言語の学習にこれまで使用したモノ一覧。一応書籍はヨドバシ、電子書籍はamazonのリンクです。
アフィリエイトブログじゃないんでワタシに何の得もないです。

書籍「苦しんで覚えるC言語」
通称苦C。サイトでも読めるんだけど、だるくて書籍買った。省略やおまじない扱いが無くて良い。
参考書読んでて意味不明なモノが出てくると気になりすぎて先に進めないタイプなんですが
(多分普段ビットとかワードなんて単語にまみれたレガシーな仕事を普段しているせい)これは大分いいっす。
サイズ、装丁込みで全体的に辞書っぽさがある。電書じゃないほうがいいと思う。自分内C言語のバイブル。

電書「スッキリわかるC言語入門 第2版」
通称スッキリC。理解しやすいと思う。茶番多め(笑)。でも著者の教えようという意思が伝わってくる。
構成のおかげか電書でも読みやすかったし、内容も入ってきた。これと苦Cは出会えてよかったと思う。
おまじない扱いにする処理を「序盤のルール」として明示し、物語の伏線のように展開している構成が
「あとで教えてくれるのね」っていう安心感にもなっていて、本当にうまい作り。立ち止まらず最後まで読めた。

書籍「新・明解 C言語入門編 第2版」
書き方も構成もかなり”教科書”。ナナメ読みしないで、一歩一歩いける人にいいと思う。
ネットワークの良書で「マスタリングTCP/IP」っていう本があるんですけどあれ読んでた頃を思い出した…。
真剣にプログラミングで食べていく気のある人は絶対これ1冊解くといいと思います。
個人的には…基本文系だなって自認ある人は、最初スッキリCのほうがいいと思います。
ワタシ文系ベースから強制的に理系詰め込んできたようなヒトなので…。

書籍「ゲーム開発で学ぶC言語入門」
ゲームの技術論がメイン。名前にひかれて買ったけど”入門”はウソ(笑)。脱初級くらいの人が読む本。
あれ、もしかしてゲーム開発の入門であって、Cの入門ではないのか…?
技術論がメインなのできっといつか役に立つのだと思う。ワタシには早い!ちゃんと読めるようになりたい本。

Udemy「ゼロからC言語を覚えてWinAPIで画像ビューワーを作成しよう」
Udemyの講座、いくつか買ったけどこれが一番分かりやすかった。通勤時間で見たり、家で問題解いたり。
単元ごとで例題が出て、問題にもちょっとしたひっかけがあったりして、
たぶん講師の方は実務や後進の育成でいろいろ苦労された方なんだろうなって勝手に思うなどした(笑)。
スチャダラパーのSHINCOさん似。ちなビューワーは作ってない。

Web「京都産業大学」の謎講義資料
意味わかんないと思うんですけど、京都産業大学のドメインが出してるC言語のページが結構わかりやすい。
たぶん教授個人に割り当てられたドメインに講義資料がアップロードされていて、
それをそのまま公開しちゃってんじゃないかなと思う。まとまったメニューはなさそう…?
わからない単語を検索して京都産業大学がヒットしたら有用度合いが高いので見に行くとよい、という話(笑)。
なので個別のリンクははりません。


SGDKに関する部分はどうしたんじゃいってことなんですけど、
OHSATさんの講座をgoogle日本語訳したのと、SGDK公式にひっかかった単語を日本語訳して勉強しました。
現行環境かつ日本語でドキュメント残されてるのはkaru_gamoさんのnoteと、かねださんのnoteくらい。
世界中でいろんな方が公開しているSGDKの話やメガドライブの技術資料も参考になりました。
インターネット翻訳さまさま。


ちなみに…C言語学習の過程ではマジ役に立たないと感じたモノももちろんあって、
それも下書きでは書いてたんですが、公開はやめました(笑)。
ファクトチェックの働いてないメディアやスクールのWeb記事、
わかってる人がわからない人向けに考えた、この程度はわかるっしょ(わかんねえよ)を地で行く動画、
ハンパに分かったやつを増やすだけの用語集サイトとかはもう本当に誰のためにもならないから無くなってほしい。

タイトルとURLをコピーしました