にもっくん?

何をとちくるったのか、amazonのリコメンドエンジンが薦めてきたので、買ってしまった。

ゲームプログラマになる前に覚えておきたい技術

ゲームプログラマになる前に覚えておきたい技術

なんかとんでもなくでかい。分厚い。なんか鈍器だなーとか思いながら、ぱらぱらと読み始めた。初っ端からかの有名な「荷物を指定した場所に運ぶあのゲーム」を作る、というテーマだ。しかも、、、

できれば一旦本を閉じて、自力で作ってみてほしい。馬鹿にするなと思うかもしれないが、実際にやってみると結構苦労するはずだ。一時間でできれば大した物だし、半日で終わればとりあえずは問題ないだろう。しかし二日以上かかるようならちょっと焦った方が良いかもしれない。

とか挑発的な事が書いてある。やらいでか

やぱりC++かな?

この本の趣旨がどうも、プログラムはC++で、みたい。うーんちゃんと体得できてないのだよなー。1時間!1時間!、とそればかり考えながら必死になってスタート。まあいいや、設計とかまじめにしない。とにかく汚くても良いから書く。としてみた。

なんとか書ききる

なんとか書ききって文法エラーをはじくはじく。コードは最高に汚い。グローバル使いまくりの、定数ばかばか使ってサイズ決めうちだらけだし、vector使えば良いところを配列で流すし、string使えば良いところを・・・ともう最悪状態。ポインタとか参照とかで頭使いたくなかったので思わずnewせず全部実体。リークしまくりんぐ。
いや、こりゃ本当にひどい。けどなんとか動いてるみたい。2カ所程勘違いを直したら問題なく動いた。
さて、最初にtouchしておいたファイルとくらべてみると時間は。。。

  • rw-r--r-- 1 kuro staff 0 12 16 01:43 start
  • rwxr-xr-x 1 kuro staff 18424 12 16 03:44 nimotsukun*

ぬわーーー、二時間超えた ToT 二時間もかけてこんな汚いコード吐いてるようではダメだ。もっと精進しよう。けどまあ久々に面白かった。

wsadで上下左右。cin使ってるのでそのままだといちいちenterする必要あり。UN*Xライクな環境なら stty -icanon をするとリアルタイムに拾ってくれて幸せかも。まあ、遊んでくれる奇特な方がいればご参考にどうぞ。

Sprite.h
#ifndef __SPRITE__
#define __SPRITE__
struct Sprite : public Point {
	char face;
};
#endif
Point.h
#ifndef __POINT__
#define __POINT__
struct Point {
	int x;
	int y;

	Point() {
		x = y = 0;
	}

	Point(int x,int y) {
		this->x = x;
		this->y = y;
	}

	const Point& operator+(const Point& p) const {
		return *(new Point(this->x + p.x, this->y + p.y));
	}
};
#endif
GameBoard.h
#include <iostream>
#include "Point.h"
#include "Sprite.h"

using std::cout;
using std::endl;

class GameBoard { 
	static const int N_SPRITE = 128;
		
	int width, height;
	char* screen;
	Sprite sprite_list[N_SPRITE];
	int sprite_max;
public:
	GameBoard(int width, int height) {
		this->width = width;
		this->height = height;
		this->sprite_max = 0;
		screen = new char[width*height];
		init_screen();
	}

	void  load_map(char* p) {
		char* q = screen;
		for ( ; *p;) {
			*q++ = *p++;
		}
	}

	void  init_screen() {
		char* p = screen;
		for (int i = 0; i < width*height; i++) {
			*p++ = ' ';
		}
	}

	bool inrange(const Point& p) const {
		return (p.x < 0 || p.x > width ||
			    p.y < 0 || p.y > height ) ;
	}

	char get(const Point& p) const {
		if (inrange(p)) throw "out of range";
		return *(screen + p.x + p.y*width);
	}

	void set(const Point& p, char c) {
		if (inrange(p)) throw "out of range";
		*(screen + p.x + p.y*width) = c;
	}

	void print() const {
		char* p = screen;
		cout << "\x1b[2J";
		cout << "\x1b[1;1H";
		for (int y = 0; y< height; y++) {
			for (int x = 0; x< width; x++) {
				int sp;
				for (sp = 0; sp < sprite_max; sp++) {
					if (x == sprite_list[sp].x && y == sprite_list[sp].y) {
						cout << sprite_list[sp].face;
						break;
					}
				}
				if (sp == sprite_max) cout << *p;
				++p;
			}
			cout << endl;
		}
	}

	void clear_sprite() {
		for (int i=0; i< N_SPRITE; i++) {
			sprite_list[i].x = 0;
			sprite_list[i].y = 0;
			sprite_list[i].face = 0xff;
		}
	}

	int  create_sprite(const Point& p, int c) {
		sprite_list[sprite_max].x = p.x;
		sprite_list[sprite_max].y = p.y;
		sprite_list[sprite_max].face = c;
		return sprite_max++;
	}

	void set_sprite(int no, const Point& p) {
		sprite_list[no].x = p.x;
		sprite_list[no].y = p.y;
	}
};

nimotukun.cpp

#include <iostream>
#include "GameBoard.h"

using std::cin;
using std::cout;

const int width =10, height=10;
char map[] = 
//0123456789
 "##########"
 "#       P#"
 "##    # ##"
 "###   ####"
 "### O   ##"
 "####O   ##"
 "## O O####"
 "##   .####"
 "##   ...##"
 "##########";

int keyin()
{
	char c;
	cin >> c;
	return c;
}

struct Character : public Point {
	int id;
	Character& operator=(const Point& p) {
		this->x = p.x; this->y = p.y;
	}
};

Character  goods[32];
Character  player;
int ngoods;
int OK = 0;

int compile_map(GameBoard& board, char* map)
{
	int n = 0;
	for (char* p = map; *p; p++) {
		if (*p == 'O' || *p == 'P') {
			Point pos = Point((p-map) % width, (p-map) / width);
			int id = board.create_sprite(pos, *p);
			if (*p == 'O') {
				goods[n].id = id; 
				goods[n] = pos;
				n++;
			} else {
			   	player.id = id;
			   	player = pos;
			}
			*p = ' ';
		}
	}
	return n;
}


void make_board(GameBoard& board)
{
	board.clear_sprite();
	ngoods = compile_map(board, map);

	board.load_map(map);

}

int collision(const Point& p)
{
	for (int i=0; i< ngoods; i++) {
		if (goods[i].x == p.x && goods[i].y == p.y) //collide!
			return i;
	}
	return -1;
}

bool free_or_canpush(GameBoard& board, const Point& from, const Point& diff) 
{
	Point dist = from + diff;
	int pushed;
	if ((pushed=collision(dist))>=0) {
		if (collision(dist+diff)>=0 || board.get(dist+diff) == '#')
			return false;
		if (board.get(goods[pushed]) == '.') --OK;
		goods[pushed] = goods[pushed] + diff;
		board.set_sprite(goods[pushed].id, goods[pushed]);
		if (board.get(goods[pushed]) == '.') ++OK;
	}
	return true;
}

main()
{
	GameBoard board(width, height);
	make_board(board);

	bool cont = true;
	while (cont) {
		Point newpos;

		board.print();
		if (OK == ngoods) {
			cout << "********* congraturation!!!! ********" << endl;
			break;
		}

		int c = keyin();
		switch (c) {
		case 'd':
			newpos = Point(+1, 0); //right
			break;
		case 'a':
			newpos = Point(-1, 0); //left
			break;
		case 'w':
			newpos = Point( 0,-1); //up
			break;
		case 's':
			newpos = Point( 0,+1); //down
			break;
		case 'q':
			cont = false; //quit;
			break;
		default:
			break;
		}
		char there = board.get(player+newpos);
		if ((there == '.' || there == ' ') && free_or_canpush(board, player, newpos))
		   	player = player+ newpos;

		board.set_sprite(player.id, player);
	}
	// exit
	return 0;
}