RTS制作までの道順について(初心者)

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
小人

RTS制作までの道順について(初心者)

#1

投稿記事 by 小人 » 14年前

初めまして。
規約には目を通したのですが、至らぬ点がありましたらご指摘下さい。

最初にまず質問の要約ですが

・2Dのリアルタイムストラテジーを個人で製作するための道順を教えて頂きたく質問しました。(法学部所属のためプログラムは趣味の範囲内とみなされてしまうのですが、企業に提出出来るレベルの物を仕上げれればなと思ってます)

(モナークモナークというゲームをご存知でしょうか?ロードモナークシリーズの一つなのですが、あのゲームは2DRTSですよね・・・?3Dではないと勝手に思っていたのですが・・・)

といってもRTSは相当技術の必要な難しいジャンルだと各所で言われてますし、ここはまず先にFFTやVantage Masterの様な敏捷値の高いMobから順番に行動していくターン制戦闘システムのプログラムを作る事を目指そうかと思ってます。

敵のAIを考えたり等RTSにも使える技術が多いと思うのですが、この発想は間違ってないでしょうか?


一応合っていたとしてターン制の戦闘(戦闘以外の場面は一枚絵から次のステージを選択するだけの物にする予定です)を作ろうと思い勉強を開始しました。

初めは初心者にも優しいとされるRPGツクールの様なツール系やHSPから入ろうかと思ったのですが、どうもこれではRTSは作れないそうで・・・。
(HSPは頑張れば作れるらしいのですが、ここまで頑張るくらいなら普通にC++で良いよね という製作者の声を聞いたので)

C言語の学習を始めました。

取り敢えず本に書いてあるサンプルコードをVC++でコンパイルして一冊を終え、ゲームプログラミングの館様でDXライブラリの勉強をしつつ、C++を勉強してみているのですが全くターン制のプログラムが頭に浮かんできません。

敵のAIに関しては取り敢えず後回しにし(これが出来たら他の部分も難なく作れると思うので)、マップ表示くらいなら出来るだろうと思っていたのですが、あの斜め上からマップを見下ろす感じ(俯瞰視点?)にする事さえ出来ませんでした・・・orz 

過去の質問を眺めていると、取り敢えずまずは簡単なゲームを作った方が良いという事でしたのでモノクロのとても粗末なブロック崩しやこちらのサイトのシューティングも制作(これはほとんどコピペしてしまったため、経験したとは呼べないかもしれませんorz)したのですが、未だに目標のさわりすら出来ずにいます。


勉強当初はその内出来るだろうと気楽に考えていたのですが、まだ少しも進展が無いためかなり不安になってきています。
とりあえず骨格だけでもとターン制の物やRTSのプログラムの参考書を必死に探してみたのですがどうやら無い?みたいで・・・。


こんな不甲斐ない自分ですが、目標に向かって努力したいとは思ってますが一体どういう道順を踏んでいけば良いのでしょうか・・・?

出来れば大体の学習期間の目安も教えて頂けると幸いです。自分はどうもゆったりとした思考のため目安は若干早めくらいに設定して頂けると有難いです(長めだと怠けてしまう恐れもありますし)


後RTSには色々な要素(AIやらステータスやら諸々)が含まれているためC++のクラスなどの知識は不可欠だと思い込んでいたのですが、実際どうなのでしょうか?
最終的にはベターCの様にC++は必要最低限に抑えた方が現実的かなと思っています。


思ったより長文になってしまった;
初投稿のため、何かと至らぬ所があるかもしれませんが、どうか宜しくお願いします。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#2

投稿記事 by softya(ソフト屋) » 14年前

ロードモナークはプレイしましたが、モナークモナークは未プレイです。
とりあえずRTSと言って良いと思いますね。

ターン制ということですが、参考サイトをご紹介します。
それとAIが簡単なオセロとか、はさみ将棋とかもターン制の勉強にはなりますよ。

「シミュレーションゲーム作成工房 より強力な思考ルーチンを求めて」
http://www.jyouhoukaiseki.com/index.html
「Article: ゲームプログラミング講座」
http://gumina.sakura.ne.jp/CREATION/OLD ... index.html

それとC言語でも十分組むことは可能です。
ロードモナークとか絶対時代的にC言語だと思いますよ。

>あの斜め上からマップを見下ろす感じ(俯瞰視点?)にする事さえ出来ませんでした・・・orz 
とりあえず真上からで良いと思います。


>出来れば大体の学習期間の目安も教えて頂けると幸いです。自分はどうもゆったりとした思考のため目安は若干早めくらいに設定して頂けると有難いです(長めだと怠けてしまう恐れもありますし)

これは難しいですね。数年は掛かると思います。
着実に数カ月単位の目標を決めて、それに向かって作っていったほうが良いでしょうね。
その件についても、相談に乗らさせていただきます。

答え忘れがあったら、もう一度聞いてくださいね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

hidden

Re: RTS制作までの道順について(初心者)

#3

投稿記事 by hidden » 14年前

モナークモナークですか。モナモナ自体はやったことないですがロードモナークは大好きだったりします。下手ですが。
以下、まだまだプログラム初心者にあたるくらいの人間の回答ですw

C言語を学ぶこととゲームのプログラムを学ぶことは地味に隔たりがあると思うんです。
それこそ英語学習の文法と実用の英語の関係みたいな感じで
なので、C言語学んだけどゲームの作り方がわからない ってのは結構通る人多いんじゃないかなと思います。

FFTってファイナルファンタジータクティクスですかね?
あんまり略称用いないほうがいいかもです。ここの人たちは必ずしもゲームの方向に詳しい人だけって訳ではないみたいなので。
あまりゲーム内容については知らないのですが、
AI等についてはある程度勝手に動くゲームなら必要な要素ですから、色々学びながら作っていけば身に付くと思います。

ターン制ですが、例えばオセロであるとか五目並べであるとか
そういったもののプログラムは想像付きませんか?その辺から手をつけたりするといいかもしれません。

それと、C言語であるとかC++であるとかあんまり気にしなくてもいいんじゃないでしょうか。
私はクラスを使いながらもC言語と混ざったままゲームプログラミングやってます。
経験が薄いようなのでどんどん作っていく方向でやっていくといいと思いますよ。

小人

Re: RTS制作までの道順について(初心者)

#4

投稿記事 by 小人 » 14年前

>>softya(ソフト屋さん)
お返事有難う御座います!
大学二年生になったという事もあって出来れば今年中・・・無理でも来年内には完成させたいなぁというのが本音です;
大学二年なんてもう暇の塊みたいなものですから、一日の時間は結構ありますっ。

おっしゃる通りで、いきなり目標の物は無理ですよね・・・。

数ヶ月単位ですと、どのくらいの規模の物を目標にしていけば宜しいでしょうか?

取り敢えず下に行き当たりばったりで作ったオセロのソースコード載せておきました。(起動は確認済み。書き方などで変な所はないでしょうか?)

対CPUにするという当初の目的忘れて気付いたら人間vs人間になってましたが・・・orz(AIの勉強にならんので後で新しいの作る予定です)


>>hiddenさん
お返事有難う御座います!

モナークモナーク知ってる方ってほんとに少なくて・・・切ない。。
かなりグラフィックも良くなっていて名作だと個人的に思ってるのですが知名度が;

略称に関しては気付きませんでした! 
以後気をつけます。

ターン制の戦闘もオセロなどの延長線上なのですかね~。
全く見当も付かない世界です・・・。


>それと、C言語であるとかC++であるとかあんまり気にしなくてもいいんじゃないでしょうか。

そうですねー。取り敢えずC++は置いておいて、DXライブラリとC言語を煮詰めていく方向が良さそうなのかな。

コード:

#include <stdio.h>

#define BOARD_SIZE 10		//盤面の規模

#define EMPTY 0				//マス目の状態定義(何もなし
#define BLACK 1				//マス目の状態定義(黒が置かれている
#define WHITE 2				//マス目の状態定義(白が置かれている
#define start 3				//石をひっくり返す思考の際の始点と終点の役割

#define cpu(player)(3-(player))		//どちらのコマを使うか

char board[10][10];

void default_board(int board[10][10])			//盤面初期化関数
{
	int i;
	for(i = 1 ; i < 9 ; i++)
	{
		board[0][i] = start;    //全マス初期化
		board[0][9] = start;
		board[1][i] = EMPTY;
		board[1][9] = start;
		board[2][i] = EMPTY;
		board[2][9] = start;
		board[3][i] = EMPTY;
		board[3][9] = start;
		board[4][i] = EMPTY;
		board[4][9] = start;
		board[5][i] = EMPTY;
		board[5][9] = start;
		board[6][i] = EMPTY;
		board[6][9] = start;
		board[7][i] = EMPTY;
		board[7][9] = start;
		board[8][i] = EMPTY;
		board[8][9] = start;
		board[9][0] = start;
		board[9][i] = start;
		board[9][9] = start;
	}
	board[4][5] = BLACK;		//初期駒の配置
	board[5][4] = BLACK;
	board[4][4] = WHITE;
	board[5][5] = WHITE;
}

void display(int board[10][10])				//盤面の表示関数
{
	int i, j;
	for(i = 1; i < 9; i++)
	{
		for(j = 1; j < 9; j++)
		{
			switch(board[i][j])
			{
			case EMPTY:
				printf("□");
				break;
			case BLACK:
				printf("○");
				break;
			case WHITE:
				printf("●");
				break;
			default:
				printf("ER");
				break;
				}
		}
		printf("\n");
	}
}

int checkflip(int board[10][10], int player, int a, int b, int x, int y)	//裏返るコマの数を調べる
{
	int i;
	for(i = 1; board[a + i*x][b + i*y] == cpu(player); i++)
	{};
	if(board[a + i*x][b + i*y] == player) 
	{
		return i-1;
	}	
	else
	{
		return 0;
	}
}

void flip(int board[10][10], int player, int a, int b)		//コマを裏返すための関数
{
	int check, x, y, i;
		for(x = -1; x < 2; x++)			//周辺調査
		{
			for(y = -1; y < 2; y++)
			{
				if( x == 0 && y == 0) continue;
				check = checkflip(board, player, a, b, x, y);
				for (i = 1; i <= check; i++)
				{
					board[a + i*x][b + i*y] = player;		//裏返す
				}
			}
		}
		board[a][b] = player;		//自コマを置く
}

int can_put(int board[10][10], int player, int a, int b)		//置けるコマが少なくとも一箇所、あるか調べる関数
{
	if(a < 1 || a > 8 || b < 1 || b > 8) return 0;				//盤外
	if(board[a][b] != 0) return 0;								//先客あり
	if(checkflip(board, player, a, b, -1, 0)) return 1;			//上のコマが取れる
	if(checkflip(board, player, a, b,  1, 0)) return 1;			//下
	if(checkflip(board, player, a, b,  0, -1)) return 1;		//左
	if(checkflip(board, player, a, b,  0, 1)) return 1;			//右
	if(checkflip(board, player, a, b, -1, -1)) return 1;		//左上
	if(checkflip(board, player, a, b, -1, 1)) return 1;			//右上
	if(checkflip(board, player, a, b,  1, -1)) return 1;		//左下
	if(checkflip(board, player, a, b, -1, 1)) return 1;			//右下
	return 0;
}

int search(int board[10][10], int player)						//↑で調べた場所が存在しているか探す関数
{
	int i, j;
	for(i = 1; i < 9; i++)
	{
		for(j = 1; j < 9; j++)
		{
			if(can_put(board, player, i, j)) return 1;
		}
	}
	return 0;
}

void select(int board[10][10], int player, int *a, int *b)		//任意の場所にコマを置く
{
	char str[100];
	printf("Player %d", player);
	while(1)
	{
		printf("> ");
		fgets(str, sizeof(str), stdin);
		*b = str[0] - 'a'+1;
		*a = str[1] - '1'+1;
		if(can_put(board, player, *a, *b)) return;
	}
}

void count(int board[10][10], int *p1, int *p2)					//コマを数える
{
	int i, j;
	*p1 = 0;
	*p2 = 0;
	for(i = 1; i < 9; i++)
	{
		for(j = 1; j < 9; j++)
		{
			switch(board[i][j])
			{
			case 1 :
				(*p1)++;
				break;
			case 2 :
				(*p2)++;
				break;
			}
		}
	}
}

int game()													//ゲームの流れ
{
	int a, b, player, p1, p2;
	int board[10][10];
	default_board(board);
	player = 1;
	while (1)
	{
		display(board);
		if(!search(board, player))
		{
			printf("Player %d 置ける場所がありません > pass\n", player);
			player = cpu(player);
			if(!search(board, player))
			{
				printf("Player %d 置ける場所がありません > pass\n", player);
				break;
			}
		}
		select(board, player , &a, &b);
		flip(board, player, a, b);
		player = cpu(player);
	}
	count(board, &p1, &p2);
	printf("試合終了\nPlayer 1 : %d\nPlayer 2 : %d\n", p1, p2);
	return p1 - p2;
}

int main(){
	char buf[100];
	printf("スタート\n");
	do
	{
		game();
		printf("もう一度プレイ?(yes/no) > ");
		scanf("%s", buf);
	}
	while(buf[0] == 'y');
	return 0;
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#5

投稿記事 by softya(ソフト屋) » 14年前

>数ヶ月単位ですと、どのくらいの規模の物を目標にしていけば宜しいでしょうか?

例えば、
・簡易AIのあるオセロ。+DXライブラリ化。
・はさみ将棋
・簡単なターン制戦術SLG(生産なしなど)
・斜め見下ろしのターン制戦術SLG
・斜め見下ろしのRTS化。
などとステップアップして行く形で計画を立てます。
それぞれの期間は、よく分からないものは、とりあえずざっと3ヶ月と決めましょう(はさみ将棋までは1ヶ月も掛からないと思いますが)。
[補足]一日あたり4~5時間で週末休みなしを想定します。
あとで色々見えてきてから計画は綿密に立て直します。

それと設計手法になりますが、各々を更に細かいステップに分けます。
それについても、おいおいとやっていきましょう。

ところで、このオセロですがヒューマン・インターフェイスに問題があります。
・aやら1やら何処か分からない。
・入力を間違えてもエラーが出ない。

それとコード自体ですが、10x10の端のstartで埋め尽くされた枠は不要だと思います。
あと、player1がd3に置いてplayer2がc3に置けるはずですが置けません。
[追記でコードの気になること]
・char board[10][10];がグローバル変数にもある。
・BOARD_SIZEを定義しているのに使っていない。
・cpuと言うマクロは、マクロの定番で大文字にしたほうが良いのともっと長い名前にしてください。CPUでも有りませんし。
・flipとcan_putは似た処理ですので共通化できませんか(置くか置かないかの違い)?
・上記に書いたバグの件で

コード:

    if(checkflip(board, player, a, b, -1, -1)) return 1;        //左上
    if(checkflip(board, player, a, b, -1, 1)) return 1;         //右上
    if(checkflip(board, player, a, b,  1, -1)) return 1;        //左下
    if(checkflip(board, player, a, b, -1, 1)) return 1;         //右下
が怪しいです。


[追記]
アルゴリズムの参考もご紹介しておきます。

「オセロプログラムの作り方」
http://hp.vector.co.jp/authors/VA015468/platina/algo/
「Fible's Laboratory」
http://fible.s5.xrea.com/laboratory/othello/

それと資料ですね。
「地球にやさしいアルゴリズム---目次 - 地球にやさしいアルゴリズム:ITpro」
http://itpro.nikkeibp.co.jp/article/COL ... ST=develop

「Amazon.co.jp: ゲームのアルゴリズム 改訂版 思考ルーチンと物理シミュレーション: 橋口 ゆうすけ: 本」

「Amazon.co.jp: ゲーム開発者のためのAI入門: David M. Bourg, Glenn Seemann, 株式会社クイープ: 本」

「Amazon.co.jp: 実例で学ぶゲームAIプログラミング: Mat Buckland, 松田 晃一: 本」

「Amazon.co.jp: コンピュータゲームのアルゴリズム&ネットワーキング: Jouni Smed/Harri Hakonen, 加藤 諒, 中本 浩: 本」
http://www.amazon.co.jp/gp/product/toc/ ... 8&n=465392
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人

Re: RTS制作までの道順について(初心者)

#6

投稿記事 by 小人 » 14年前

>>ソフト屋さん
オセロの指摘有難う御座いますm(_ _)m 修正しておきました。

計画通りにDXライブラリを用いてとの事でしたのでオセロの盤面から作成をしようと思い、どうせならSLGのマップだけ作っちゃうか ・・・と始めたのですが関数足りませんエラーで躓いてしまいましたorz

やりたい事としてはマップを座標単位で数値化し、数値ごとに適応したフィールド(平原や山など)を画像で表示したいのですが・・・。

コード:

#include "GV.h"

#define MAP_SIZE	64			// マップチップ一つのドットサイズ

#define MAP_WIDTH	10			// マップの幅
#define MAP_HEIGHT	10			// マップの縦長さ
#define Heichi 0				// マップ 平地
#define Wall   -1				// マップの行き止まり
#define Mountain 1				// マップ 山

// マップのデータ
int MapData[ MAP_HEIGHT ][ MAP_WIDTH ] =
{
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } ,
	{ -1, 1, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 1, 0, 1, 1, 1, 1, 1, 1, -1 } ,
	{ -1, 1, 0, 1, 1, 0, 0, 0, 1, -1 } ,
	{ -1, 1, 1, 1, 1, 0, 0, 0, 1, -1 } ,
	{ -1, 1, 0, 1, 0, 0, 0, 0, 1, -1 } ,
	{ -1, 1, 1, 1, 1, 1, 1, 1, 1, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } 
} ;

int img_ch[1][1];

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
						 LPSTR lpCmdLine, int nCmdShow )
{
	ChangeWindowMode(TRUE); // ウィンドウモードに設定

	int i , j ;

	SetGraphMode( 640 , 480 , 16 ) ;
	if( DxLib_Init() == -1 )	// DXライブラリ初期化処理
	{
		 return -1;		// エラーが起きたら直ちに終了
	}

	// マップを描く
	for( i = 0 ; i < MAP_HEIGHT ; i ++ )
	{
		for( j = 0 ; j < MAP_WIDTH ; j ++ )
		{
			if( MapData[ i ][ j ] == 0 )
			{
				DrawGraph("画像/平原.bmp");
			}
		}
	}

	// キー入力待ち
	WaitKey() ;

	DxLib_End() ;		// DXライブラリ使用の終了処理

	return 0 ;		// ソフトの終了
}
このDrawGraphがどう見てもおかしいのは分かるのですが、DXライブラリのHPへ行ってみても適当な関数が見当たらなかったため仕方なく・・・。

GV.hにはDXライブラリを読み込ませ、load.hには

コード:

#include "GV.h"

extern int img_ch[1][1]; 

void load(){
	LoadDivGraph("画像/平原.bmp" , 1 , 1, 1, 70, 70, img_ch[0]);
}
といった形を取ってますが、色々怪しいので暫定です・・・。

エラーは'DxLib::DrawGraph' : 関数に 1 個の引数を指定できません。 のみですが、抜本的に変えていかないと駄目そうだという気がしていますorz

座標に数字を当てはめていくと、行けない場所を作ったり、マップに色々オブジェクトを設置するにも簡単かなぁとぼんやり思っているのですが、他に上手い方法あります・・・?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#7

投稿記事 by softya(ソフト屋) » 14年前

load.hにプログラムを書いていますが、load.hは宣言だけで実体はload.cppに書くようにしてください。
それと
LoadDivGraph("画像/平原.bmp" , 1 , 1, 1, 70, 70, img_ch[0]);
は1つしか読まなならLoadGraphで十分です。

確かにいろいろ怪しいので、ここのコンテンツである
「新・C言語 ~ゲームプログラミングの館~ [DXライブラリ]」
http://dixq.net/g/
を一通り勉強してください。

それから、マップ表示に関しては私の書いたRPG講座を読んでもらうと参考になるかも知れません。
http://dixq.net/forum/blog.php?u=114&sd=a&c=2
ただし、ある程度のC言語の熟練度が必要かと思います。

[追記]
マップ表示をやりたい気持ちはわかりますが、やりならマップ表示とオセロは同時並行でやって下さいね。
オセロをやって損な事はありませんから。[補足]AIは奥が深いのでやりすぎるといつまでも作り続けることになるので、ほどほどに。

[追記の追記]
直したソースコードは貼ってもらったほうが良いと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: RTS制作までの道順について(初心者)

#8

投稿記事 by bitter_fox » 14年前

小人 さんが書きました: 取り敢えず下に行き当たりばったりで作ったオセロのソースコード載せておきました。(起動は確認済み。書き方などで変な所はないでしょうか?)
既に多くをsoftyaさんが指摘なさってますが、僕的に気持ち悪い点を指摘します。
小人 さんが書きました:

コード:

void default_board(int board[10][10])           //盤面初期化関数
{
    int i;
    for(i = 1 ; i < 9 ; i++)
    {
        board[0][i] = start;    //全マス初期化
        board[0][9] = start;
        board[1][i] = EMPTY;
        board[1][9] = start;
        board[2][i] = EMPTY;
        board[2][9] = start;
        board[3][i] = EMPTY;
        board[3][9] = start;
        board[4][i] = EMPTY;
        board[4][9] = start;
        board[5][i] = EMPTY;
        board[5][9] = start;
        board[6][i] = EMPTY;
        board[6][9] = start;
        board[7][i] = EMPTY;
        board[7][9] = start;
        board[8][i] = EMPTY;
        board[8][9] = start;
        board[9][0] = start;
        board[9][i] = start;
        board[9][9] = start;
    }
    board[4][5] = BLACK;        //初期駒の配置
    board[5][4] = BLACK;
    board[4][4] = WHITE;
    board[5][5] = WHITE;
}
ここの処理はforを二重にしたりして、もっとうまく書けるのではないでしょうか?
小人 さんが書きました:

コード:

#define BLACK 1             //マス目の状態定義(黒が置かれている
#define WHITE 2             //マス目の状態定義(白が置かれている
#define cpu(player)(3-(player))     //どちらのコマを使うか
cpuマクロに出てくる3という数字がマジックナンバーになってしまっています。
この3という数字はBLACK+WHITEに対応するので、
(BLACK+WHITE-(player))と言った風にするべきです。

小人

Re: RTS制作までの道順について(初心者)

#9

投稿記事 by 小人 » 14年前

コード:

#include "DxLib.h"

#define BAN_SIZE	64			// 盤目一つのドットサイズ

#define BAN_WIDTH	10			// 盤の幅
#define BAN_HEIGHT	10			// 盤の縦長さ
#define BAN_LEFTUP_X 0			//一番左上の盤目の左上端X座標
#define BAN_LEFTUP_Y 0			//一番左上の盤目の左上端Y座標
#define BAN 0				    // 盤目
#define BLACK   1				// 黒コマ
#define WHITE 2					// 白コマ
#define WALL -1					// 折り返し用の壁

// 盤のデータ
int BanData[ BAN_HEIGHT ][ BAN_WIDTH ] =
{
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 2, 1, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 1, 2, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } 
} ;

void Display(int BanData[ BAN_HEIGHT ][ BAN_WIDTH ])
{
	int i, j;
	int GHandle1;
	int GHandle2;
	int GHandle3;
	int GHandle4;

	GHandle1 = LoadGraph("画像/オセロ盤目.bmp");
	GHandle2 = LoadGraph("画像/背景.bmp");
	GHandle3 = LoadGraph("画像/黒コマ.bmp");
	GHandle4 = LoadGraph("画像/白コマ.bmp");

		// 盤を描く
		DrawGraph(0,0,GHandle2,TRUE);

		for( i = 0 ; i < BAN_HEIGHT ; i ++ )
		{
			for( j = 0 ; j < BAN_WIDTH ; j ++ )
			{
				switch(BanData[ i ][ j ] )
				{
					case BAN : DrawGraph(i*BAN_SIZE, j*BAN_SIZE, GHandle1, FALSE); break;
					case BLACK : DrawGraph(i*BAN_SIZE, j*BAN_SIZE, GHandle3, FALSE); break;
					case WHITE : DrawGraph(i*BAN_SIZE, j*BAN_SIZE, GHandle4, FALSE); break;
				}
			}
		}
}

//向きごとの移動量
char vec_y[] = {-1,-1,0,1,1,-1,0,-1};
char vec_x[] = {0,1,1,1,0,-1,-1,-1};


//vecで指定された向きについてひっくり返るコマがあるか確認する
int checkFlip(int y,int x,int turn,int vec)
{
	int flag = 1;
	while(1){
		y += vec_y[vec];
		x += vec_x[vec];
		
		//盤面の外に出ていたら終了
		if( x < 1 || y < 1 || x > BAN_WIDTH-2 || y > BAN_HEIGHT-2) return 0;
		
		//空きマスだったら終了
		if(BanData[y][x] == BAN) return 0;
		
		//相手のコマがあったらフラグを立てる
		if(BanData[y][x] == (turn ? BLACK : WHITE)){
			flag = 1;
			continue;
		}
		
		//もしフラグがたっていればループ脱出。いなければ終了
		if(flag == 1) break;
		return 0;
	}
	return 1;
}


//その場所に置くことができるかを確認する関数
int check(int y,int x,int turn)
{
	int vec;
	
	//どれか一方向でもひっくり返るか確認
	for(vec = 0 ; vec < 8 ; ++vec){
		if(checkFlip(y,x,turn,vec) == 1) return 1;
	}
	
	return 0;
}


//実際に裏返す関数
void flip(int y,int x,int turn,int vec){
	while(1){
		y += vec_y[vec];
		x += vec_x[vec];
		
		//自分のコマがあったら終了
		if(BanData[y][x] == (turn ? WHITE : BLACK)) break;
		
		//それ以外なら自分のコマで塗りつぶす
		BanData[y][x] = (turn ? WHITE : BLACK);
	}
}


//入力を受けて裏返せるか確かめる関数
int put(int y,int x,int turn){
	int vec,flag=0;
	
	//空白でなければ終了
	if(BanData[y][x] != BAN) return 0;
	
	//全方向について確認
	for(vec=0 ; vec < 8 ; ++vec){
		if(checkFlip(y,x,turn,vec) == 1){
			//裏返す
			flip(y,x,turn,vec);
			flag = 1;
		}
	}
	if(flag == 1){
		//この場所にコマを置く
		BanData[y][x]= (turn ? WHITE : BLACK);
		return 1;
	}
	return 0;
}

//入力関数
void input(int turn)
{
	int oldinput;
	int nowinput;
	int mouse X,mouseY;
	int edgeinput;
	int x, y;

	oldinput = 0 ;							// マウスの入力情報を初期化する
	nowinput = 0 ;
	mouse X = 0 ;
	mouseY = 0 ;

	while( ProcessMessage() == 0 )			// メインループ(何かキーが押されたらループを抜ける)
	{
		oldinput = nowinput ;				// マウスの入力情報を更新する
		nowinput = GetMouseInput() ;
		edgeinput = nowinput & ~oldinput ;
		GetMousePoint( &mouse X, &mouseY ) ;

		x = ( mouseY - BAN_LEFTUP_X ) / BAN_SIZE ;			// 盤目の座標を算出する
		y = ( mouse X - BAN_LEFTUP_Y ) / BAN_SIZE ;

		if( ( edgeinput & MOUSE_INPUT_LEFT ) != 0 )				// マウスの左ボタンが押されたら盤目の値を変化させる
		{
			if( x >= 1 && x < BAN_HEIGHT -1 &&				// 盤目の範囲内にある場合のみマップの状態を変化させる
				y >= 1 && y < BAN_WIDTH -1 )
			
				put(y,x,turn) == 1, BanData[ y ][ x ] = turn;

				else  MessageBox(NULL,"盤外です","ERROR",MB_OK);
		}
	}
}

void CPU_AI(int turn)				//CPUのAI
{
	int x, y;

	int val_table[BAN_HEIGHT][BAN_WIDTH] =
	{  
		{-100,-100,-100,-100,-100,-100,-100,-100,-100,-100},
		{-100,120, -20,  20,   5,   5,  20, -20, 120, -100},  
		{-100,-20, -40,  -5,  -5,  -5,  -5, -40, -20, -100},  
		{-100, 20,  -5,  15,   3,   3,  15,  -5,  20, -100},  
		{-100,  5,  -5,   3,   3,   3,   3,  -5,   5, -100},  
		{-100,  5,  -5,   3,   3,   3,   3,  -5,   5, -100},  
		{-100, 20,  -5,  15,   3,   3,  15,  -5,  20, -100},  
		{-100,-20, -40,  -5,  -5,  -5,  -5, -40, -20, -100},  
		{-100,120, -20,  20,   5,   5,  20, -20, 120, -100},
		{-100,-100,-100,-100,-100,-100,-100,-100,-100,-100}
	};
	for (y = 1; y < 9; y++){
		for (x = 1; x < 9; x++){
			switch (val_table[y][x] )
			{
				case 120 : (put(y-1,x-1,turn) == 1); break;
				case  20 : (put(y-1,x-1,turn) == 1); break;
				case  15 : (put(y-1,x-1,turn) == 1); break;
				case   5 : (put(y-1,x-1,turn) == 1); break;
				case   3 : (put(y-1,x-1,turn) == 1); break;
				case  -5 : (put(y-1,x-1,turn) == 1); break;
				case -20 : (put(y-1,x-1,turn) == 1); break;
				case -40 : (put(y-1,x-1,turn) == 1); break;
			}
		}
	}
}


int checkEnd(int turn)
{
	int i,j;
	//置ける場所があるか確認
	for(i = 1 ; i < BAN_WIDTH -1 ; ++i){
		for(j = 1 ; j < BAN_HEIGHT -1 ; ++j){
			//あれば普通に続行
			if(BanData[i][j] == BAN && check(i,j,turn) == 1) return 0;
		}
	}
	//場所が無かったので交替して探す
	turn = (turn + 1) % 2;
	for(i = 1 ; i < BAN_WIDTH -1 ; ++i){
		for(j = 1 ; j < BAN_HEIGHT -1 ; ++j){
			//あればpassして続行
			if(BanData[i][j] == BAN && check(i,j,turn) == 1) return 1;
		}
	}
	
	//なかったのでゲーム終了
	return 2;
}

//勝者判定
void checkWinner(){
	int i,j,b=0,w=0;
	
	//コマを数え上げる
	for(i = 1 ; i < BAN_WIDTH -1 ; ++i){
		for(j = 1 ; j < BAN_HEIGHT -1 ; ++j){
			switch(BanData[i][j]){
			case BLACK:
				++b;
				break;
			case WHITE:
				++w;
				break;
			default :
				break;
			}
		}
	}
	//勝者を表示
	if(b > w)  MessageBox(NULL,"黒が勝ちました","結果発表",MB_OK);
	else if(b < w) MessageBox(NULL,"白が勝ちました","結果発表",MB_OK);
	else MessageBox(NULL,"引き分けです","結果発表",MB_OK);	
}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
						 LPSTR lpCmdLine, int nCmdShow )
{
	ChangeWindowMode(TRUE); // ウィンドウモードに設定

	SetGraphMode( 640 , 640 , 32 ) ;
	
	DxLib_Init();	// DXライブラリ初期化処理

	int USERturn;
	int turn = 0;

	Display(BanData);		//盤面の初期化

	if(MessageBox(NULL,"先手を取りますか?","選択",MB_YESNO) == IDYES)			//プレーヤー、先手か後手か選択
		USERturn=BLACK;
	else
		USERturn=WHITE;

	while(turn < 2)								//ゲームのメインループ
	{																			
		Display(BanData);						//盤面表示
		
		//入力
		switch(turn){
		case 0:
			turn = BLACK;
			input(turn);
			break;
		case 1:
			CPU_AI(turn);
			break;
		default:
			MessageBox(NULL,"予期せぬエラー","ERROR",MB_OK);
			return -1;
		}
		
		//手番交替
		turn = (turn + 1) % 2;
		
		//終了判定
		switch(checkEnd(turn)){
		case 1:
			MessageBox(NULL,"置ける場所が無いためパスします","確認",MB_OK);
			turn = (turn + 1) % 2;
			break;
		case 2:
			MessageBox(NULL,"ゲームが終了しました","確認",MB_OK);
			turn = 2;
			break;
		default:
			break;
		}	
	}

	checkWinner();

	DxLib_End() ;		// DXライブラリ使用の終了処理

	return 0 ;		// ソフトの終了
}
近況報告の様なもの。
ここまで頑張りましたが、マウスの座標地点が上手く扱えず終了しました・・・orz
先手後手の表示も今は飾りですし、評価関数で敵のAI組み立ててみましたが、多分失敗しました。
もうこいつをいじるのは絶望的なので一から出直す予定です。

一応ボードを画像で表示する技術は習得しましたが、それだけでしたorz

マウスが絡んでくると難しいですね・・・。
目標のゲームは全部マウス操作のみの物ですから、頑張らないと駄目ですが;

小人

Re: RTS制作までの道順について(初心者)

#10

投稿記事 by 小人 » 14年前

追記
CPUのAI関数内
switch文の中にあるcase・・・ 何故か座標全部-1になってますがミスです。

コード:

case 120 : (put(y, x, turn) == 1); break;
case  20 : (put(y, x, turn) == 1); break;
case  15 : (put(y, x, turn) == 1); break;
case   5 : (put(y, x, turn) == 1); break;
case   3 : (put(y, x, turn) == 1); break;
case  -5 : (put(y, x, turn) == 1); break;
case -20 : (put(y, x, turn) == 1); break;
case -40 : (put(y, x, turn) == 1); break;
が正しい形です。

そういう問題じゃないくらい他がオカシイですが・・・。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#11

投稿記事 by softya(ソフト屋) » 14年前

そのまま動かせいないので、ぜひ画像くださいね~。
で、Windowsのプログラムとして幾つか問題があります。

「新・C言語 ~ゲームプログラミングの館~ [DXライブラリ]」
http://dixq.net/g/
にあるDXライブラリ入門編で書かれている 「1.9 ゲームプログラムの骨格の完成」 の形を守っていただかないと色々と問題が出ます。
例えば、今のこのプログラムはウィンドウのxマークを押しても終了できません。マウスがちゃんと動かないのも構造的な原因では?
コンソールアプリとウィンドウアプリ(ゲーム)は基本的に構造が違いますのでコンソールアプリの作りかたは一度完全に捨てる必要がありますよ。
そういう意味でも、根本的な構造を見なおしたほうが良いと思います。

あとLoadGraphとDrawGraphが同じ関数にあるのはメモリリークするのでやめてください。LoadGraphを何度も繰り返してしまいます。
LoadGraphはメインループに入るまでに先に終わらせておきましょう。

大雑把には以上ですが、明日また気になった事を書かせていただきます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人

Re: RTS制作までの道順について(初心者)

#12

投稿記事 by 小人 » 14年前

一回起動するとタスクマネージャー使わないと落とせないのはそれが原因ですね・・・。
裏画面設定や画面クリア辺りを追加して置かないと駄目だとは分かっていたのですが、何かいつの間にか消してましたorz

画像については添付の仕方が分からなかった事と、上に書いた様に起動するとメモリ食って危険なので と思ってましたけど要らない心配でした。
添付はどのようにすれば宜しいでしょうか?

LoadとDrawGraphについては了解しました。修正します。

マウス自体は利いていた(自分だけコマを自由に置ける状態でしたが・・・)のですが、書き加えていく間に機能しなくなってしまった辺り、確かに構造の問題だと思います。

オセロ製作でもヘッダファイルやソースコードを複数用意した方が良さそうでしょうか。
いずれ絶対に複数用意しないとやっていけなくなりそうですし、練習も兼ねてそちらも頑張ってみます。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#13

投稿記事 by softya(ソフト屋) » 14年前

小人 さんが書きました:一回起動するとタスクマネージャー使わないと落とせないのはそれが原因ですね・・・。
裏画面設定や画面クリア辺りを追加して置かないと駄目だとは分かっていたのですが、何かいつの間にか消してましたorz
ProcessMessage()を一定周期で呼び出すのはウィンドウゲーム(DXライブラリ)のお約束ですね。
小人 さんが書きました:画像については添付の仕方が分からなかった事と、上に書いた様に起動するとメモリ食って危険なので と思ってましたけど要らない心配でした。
添付はどのようにすれば宜しいでしょうか?
ここの「 mixC++」にユーザー登録してもらうか、何処かのアップローダを使ってください。
「mixC++」にユーザーしてもらうと返答を書くところの下のオプションの隣にファイル添付が追加されます。
小人 さんが書きました:オセロ製作でもヘッダファイルやソースコードを複数用意した方が良さそうでしょうか。
いずれ絶対に複数用意しないとやっていけなくなりそうですし、練習も兼ねてそちらも頑張ってみます。
分け方も
「新・C言語 ~ゲームプログラミングの館~ [DXライブラリ]」
http://dixq.net/g/
に書かれてますので参考にしてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#14

投稿記事 by 小人 » 14年前

画像添付しました。
.bmpは対応してない様でしたので、一時的に.jpegに変換しています。読み込む際には.bmpへ変換をお願い致します。

画像は3枚までらしいのでもう一枚貼ります。
添付ファイル
背景.jpg
背景.jpg (6.87 KiB) 閲覧数: 20367 回
黒コマ.jpg
黒コマ.jpg (2.51 KiB) 閲覧数: 20367 回
オセロ盤目.jpg
オセロ盤目.jpg (1.54 KiB) 閲覧数: 20367 回

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#15

投稿記事 by softya(ソフト屋) » 14年前

遅いかも知れませんが、zipなどで圧縮すれば1つのファイルでbmpを4つとも入れられます。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#16

投稿記事 by 小人 » 14年前

添付完了しました。
ゲームプログラミングの館様のサイトをもう一度じっくり読み返してみたいと思います。

まだSLG製作のスタートラインにも立ててないと思うと情けない限りです。。

あぁ返信今来ました。
次からは圧縮しますm(_ _)m
添付ファイル
白コマ.jpg
白コマ.jpg (2.81 KiB) 閲覧数: 20365 回

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#17

投稿記事 by softya(ソフト屋) » 14年前

小人 さんが書きました:添付完了しました。
ゲームプログラミングの館様のサイトをもう一度じっくり読み返してみたいと思います。

まだSLG製作のスタートラインにも立ててないと思うと情けない限りです。。

あぁ返信今来ました。
次からは圧縮しますm(_ _)m
まだ数カ月なら順調だと思いますよ。情け無いなんてとんでもない。
画像データは受け取れました。
zip添付はソースが大きかったりファイルが多くなったときにお使いください。
全部掲示板に張るとな長くなり過ぎますからね。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#18

投稿記事 by 小人 » 14年前

出だしのファイル分割から詰まってしまいましたorz
ウインドウは立ち上がり、ESCで閉じる事は可能なのですが何も表示されない・・・。

取り敢えずソースコードです。
defain.h

コード:

#define BAN_SIZE	64			// 盤目一つのドットサイズ

#define BAN_WIDTH	10			// 盤の幅
#define BAN_HEIGHT	10			// 盤の縦長さ
#define BAN_LEFTUP_X 0			//一番左上の盤目の左上端X座標
#define BAN_LEFTUP_Y 0			//一番左上の盤目の左上端Y座標
#define BAN 0				    // 盤目
#define BLACK   1				// 黒コマ
#define WHITE 2					// 白コマ
#define WALL -1					// 折り返し用の壁
ボード.cpp

コード:

#include "宣言.h"

void Display_Haikei(){

// 背景のデータ
int HaikeiData[ BAN_HEIGHT ][ BAN_WIDTH ] =
{
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } 
} ;

int i, j;

DrawGraph(0,0,GHandle2,FALSE);

		for( i = 0 ; i < BAN_HEIGHT ; i ++ )
		{
			for( j = 0 ; j < BAN_WIDTH ; j ++ )
			{
				switch(HaikeiData[ i ][ j ] )
				{
					case BAN : DrawGraph(i*BAN_SIZE, j*BAN_SIZE, GHandle1, TRUE); break;
				}
			}
		}
}

void Display_Ban(){
	int i , j;

	for( i = 0 ; i < BAN_HEIGHT ; i ++ )
		{
			for( j = 0 ; j < BAN_WIDTH ; j ++ )
			{
				switch(BanData[ i ][ j ] )
				{
					case BLACK : DrawGraph(i*BAN_SIZE, j*BAN_SIZE, GHandle3, TRUE); break;
					case WHITE : DrawGraph(i*BAN_SIZE, j*BAN_SIZE, GHandle4, TRUE); break;
				}
			}
		}
}
多分こいつが原因なんじゃないかと睨んでます。

今までの盤の表示方法だと、マップの上にキャラを立たせることが出来ない(マップの値自体を変化させてしまっていたため)
ため、今回から背景のマップチップを表示させつつコマをその上に表示させようと試行錯誤しておりますm(_ _)m


宣言.h

コード:

#include "DxLib.h"
#include "define.h"

static int GHandle1;
extern int GHandle2;
extern int GHandle3;
extern int GHandle4;

static int func_state;

static unsigned int stateKey[256];

static int GetHitKeyStateAll_2(){
    char GetHitKeyStateAll_Key[256];
    GetHitKeyStateAll( GetHitKeyStateAll_Key );
    for(int i=0;i<256;i++){
        if(GetHitKeyStateAll_Key[i]==1) stateKey[i]++;
        else                            stateKey[i]=0;
    }
    return 0;
}

static int CheckStateKey(unsigned char Handle){
        return stateKey[Handle];
}


// 盤のデータ
static int BanData[ BAN_HEIGHT ][ BAN_WIDTH ] =
{
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 2, 1, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 1, 2, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } 
} ;
画像ロード.cpp

コード:

#include "宣言.h"

int GHandle2;
int GHandle3;
int GHandle4;

void load(){
	int GHandle1;
	int GHandle2;
	int GHandle3;
	int GHandle4;

	GHandle1 = LoadGraph("画像/オセロ盤目.bmp");
	GHandle2 = LoadGraph("画像/背景.bmp");
	GHandle3 = LoadGraph("画像/黒コマ.bmp");
	GHandle4 = LoadGraph("画像/白コマ.bmp");
}
複重インクルードを防ぐ方法として宣言.hで static にするか、それとも extern にして他で普通に宣言させるか悩んでいるのですが、どちらが一般的に良い方法なのでしょうか・・・?

メイン.cpp

コード:

#include "宣言.h"

void load();
void Display_Haikei();
void Display_Ban();

//ループで必ず行う3大処理
int ProcessLoop(){
    if(ProcessMessage()!=0)return -1;//プロセス処理がエラーなら-1を返す
    if(ClearDrawScreen()!=0)return -1;//画面クリア処理がエラーなら-1を返す
	GetHitKeyStateAll_2();//現在のキー入力処理を行う
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){
        ChangeWindowMode(TRUE);		 //ウィンドウモード変更
		if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;	//初期化と裏画面化

		while(ProcessLoop()==0){		//メインループ
			switch(func_state){
                        case 0:
                                load();				 //データロード
                                Display_Haikei();		//背景の初期化
                                func_state=100;
                                break;
                        case 100:
                                Display_Ban();		//ボードの表示
                                break;
                        default:
                                printfDx("不明なfunc_state\n");
                                break;
		}

        if(CheckStateKey(KEY_INPUT_ESCAPE)==1)break;		//エスケープが入力されたらブレイク
        ScreenFlip();//裏画面反映
    }
        
        DxLib_End(); // DXライブラリ終了処理
        return 0;
}

以上です。

かなり長文になってしまい申し訳御座いませんorz
色々本屋で立ち読みなどしているのですが、一人でプログラミングする分には、無理にファイルを分割する必要は無いみたいですね・・・。
分割した方が見易し良いかなと思っていたのですが、エラー多発してしまいましたしorz

しかしSLG規模のゲームになってくるとファイル一つで、というのも無理があるような気がしてきますし(作った事も見た事もないので推測でしかないですが)、一人だとしても分割した方が良い様にも思えます。

将来的にどちらが良いのでしょう・・・?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#19

投稿記事 by softya(ソフト屋) » 14年前

とりあえずソースは後で見させていだきますが、SLGやRTSだとファイル分割しないと恐ろしいファイルのサイズになって管理が大変だと思います。
後々のことを考えれば、機能単位でファイルを分けることは合理的でバグを減らす有効な手段でもあります。

[追記]
ちなみにRPG講座の最終回のファイル数はcppが20個、ヘッダが19個です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#20

投稿記事 by 小人 » 14年前

なるほど・・・、やはりしっかり分割技術を今の内に学んでおくべきですね。

cpp20 h10 ですか・・・・・・。
初心者のためのRPGでその量だと普通に作った場合大変な事になりそうですね;

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#21

投稿記事 by softya(ソフト屋) » 14年前

ざっと気になったことだけ。

static な変数がヘッダにあってはいけません。
これは同じ変数をインクルードしたcpp毎に作ることになるのでお互いに別のものになります。
同様に関数があるのも行けません。
グローバル変数は徹底的に減らしてください。今回の場合は、staticもexternもなしです。

宣言.hとdefine.hを一本化してください。

Display_Haikei(); //背景の初期化
は一回しか呼ばれないので背景は次のフレームでクリアされます。
ゲームの基本は、毎回画面に出るものは全部描くって事になります。
それいう意味で、Display_Ban()に描画は一本化出来ます。
先に背景だけ書いて上に石を置けば良いです。

分け方の基本方針で、下記の書き方に従って書いてみてください。

「新・C言語 ~ゲームプログラミングの館~ [DXライブラリ]」
http://dixq.net/g/
d.1 メイン関数の作り方
d.2 複数のファイルにわけてコンパイルする
d.3 ゲームの設計と分割コンパイル(1)
d.4 ゲームの設計と分割コンパイル(2)
d.5 ゲームの設計と分割コンパイル(3)
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
bitter_fox
記事: 607
登録日時: 14年前
住所: 大阪府

Re: RTS制作までの道順について(初心者)

#22

投稿記事 by bitter_fox » 14年前

小人 さんが書きました:出だしのファイル分割から詰まってしまいましたorz
ウインドウは立ち上がり、ESCで閉じる事は可能なのですが何も表示されない・・・。

画像ロード.cpp

コード:

#include "宣言.h"

int GHandle2;
int GHandle3;
int GHandle4;

void load(){
	int GHandle1;
	int GHandle2;
	int GHandle3;
	int GHandle4;

	GHandle1 = LoadGraph("画像/オセロ盤目.bmp");
	GHandle2 = LoadGraph("画像/背景.bmp");
	GHandle3 = LoadGraph("画像/黒コマ.bmp");
	GHandle4 = LoadGraph("画像/白コマ.bmp");
}
これだとローカルなGHandle1~4に代入してしまってるのではないでしょうか?

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#23

投稿記事 by 小人 » 14年前

>>bitter_foxさん
あぁごめんなさい。そのローカル変数は撤去してやってください。
試行錯誤の途中でコピペしたため消し忘れてました・・・。

ご指摘感謝ですm(_ _)m

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#24

投稿記事 by 小人 » 14年前

Sengen.h

コード:

#include "DxLib.h"

#ifndef _INCLUDE_SENGEN_
#define _INCLUDE_SENGEN_


#define BAN_SIZE	64			// 盤目一つのドットサイズ

#define BAN_WIDTH	10			// 盤の幅
#define BAN_HEIGHT	10			// 盤の縦長さ
#define BAN_LEFTUP_X 0			//一番左上の盤目の左上端X座標
#define BAN_LEFTUP_Y 0			//一番左上の盤目の左上端Y座標
#define BAN 0				    // 盤目
#define BLACK   1				// 黒コマ
#define WHITE 2					// 白コマ
#define WALL -1					// 折り返し用の壁

extern int GHandle1;
extern int GHandle2;
extern int GHandle3;
extern int GHandle4;

//関数のプロトタイプ宣言
void load();
void Display_Ban();

int GetHitKeyStateAll_2();
int CheckStateKey(unsigned char Handle);

#endif
画像ロード.cpp

コード:

#include "Sengen.h"

int GHandle1;
int GHandle2;
int GHandle3;
int GHandle4;

void load(){
	GHandle1 = LoadGraph("画像/オセロ盤目.png");
	GHandle2 = LoadGraph("画像/背景.png");
	GHandle3 = LoadGraph("画像/黒コマ.png");
	GHandle4 = LoadGraph("画像/白コマ.png");
}
ボード.cpp

コード:

#include "Sengen.h"



// 盤のデータ
static int BanData[ BAN_HEIGHT ][ BAN_WIDTH ] =
{
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 2, 1, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 1, 2, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } 
} ;

void Display_Ban(){
		// 背景のデータ
	int HaikeiData[ BAN_HEIGHT ][ BAN_WIDTH ] =
	{
		{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } ,
		{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
		{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
		{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
		{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
		{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
		{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
		{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
		{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
		{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } 
	} ;

int h, w;

//背景の描画
DrawGraph(0,0,GHandle2,FALSE);

		for( h = 1 ; h < BAN_HEIGHT - 1 ; h ++ )
		{
			for( w = 1 ; w < BAN_WIDTH - 1 ; w ++ )
			{
				switch(HaikeiData[ h ][ w ] )
				{
					case BAN : DrawGraph(h*BAN_SIZE, w*BAN_SIZE, GHandle1, TRUE); break;
				}
			}
		}

		//コマの描画
	for( h = 0 ; h < BAN_HEIGHT ; h ++ )
		{
			for( w = 0 ; w < BAN_WIDTH ; w ++ )
			{
				switch(BanData[ h ][ w ] )
				{
					case BLACK : DrawGraph(h*BAN_SIZE, w*BAN_SIZE, GHandle3, TRUE); break;
					case WHITE : DrawGraph(h*BAN_SIZE, w*BAN_SIZE, GHandle4, TRUE); break;
				}
			}
		}
}
Key.cpp

コード:

#include "Sengen.h"

static unsigned int stateKey[256];

int GetHitKeyStateAll_2(){
    char GetHitKeyStateAll_Key[256];
    GetHitKeyStateAll( GetHitKeyStateAll_Key );
    for(int i=0;i<256;i++){
        if(GetHitKeyStateAll_Key[i]==1) stateKey[i]++;
        else                            stateKey[i]=0;
    }
    return 0;
}

int CheckStateKey(unsigned char Handle){
        return stateKey[Handle];
}
メイン.cpp

コード:

#include "Sengen.h"

//ループで必ず行う3大処理
int ProcessLoop(){
    if(ProcessMessage()!=0)return -1;//プロセス処理がエラーなら-1を返す
    if(ClearDrawScreen()!=0)return -1;//画面クリア処理がエラーなら-1を返す
	GetHitKeyStateAll_2();//現在のキー入力処理を行う
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){
        ChangeWindowMode(TRUE);		 //ウィンドウモード変更
		SetGraphMode( 640 , 640 , 32 ) ;
		if(DxLib_Init() == -1 || SetDrawScreen( DX_SCREEN_BACK )!=0) return -1;	//初期化と裏画面化

		int func_state = 0 ;

		while(ProcessLoop()==0){		//メインループ
			switch(func_state){
                        case 0:
                                load();				 //データロード
                                func_state=100;
                                break;
                        case 100:
                                Display_Ban();		//ボードの表示
                                break;
                        default:
                                printfDx("不明なfunc_state\n");
                                break;
		}

        if(CheckStateKey(KEY_INPUT_ESCAPE)==1)break;		//エスケープが入力されたらブレイク
        ScreenFlip();//裏画面反映
    }
        
        DxLib_End(); // DXライブラリ終了処理
        return 0;
}
お陰様で画像表示自体は無事成功したのですが、どうしてもGHandle達がグローバル変数になってしまいますorz

他はすっきりさせたつもりなのですが、この変数だけは色々試した結果 エラーばかりで・・・。

どうやってこいつらを駆除すれば宜しいのでしょうか・・・?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#25

投稿記事 by softya(ソフト屋) » 14年前

ここは、
「新・C言語 ~ゲームプログラミングの館~ [DXライブラリ] d.3章 ゲームの設計と分割コンパイル(1)」
http://dixq.net/g/d_03.html
にあるとおり、
  ・初期化
  ・計算
  ・描画
  ・終了処理
に分けることで解決する方法があります。

画像ロード.cppのload()をボード.cppの初期化処理にしてしまうのです。
そうすれば、
int GHandle1;
int GHandle2;
int GHandle3;
int GHandle4;
はボード.cppでstaticに出来ます。

あとヘッダはソースと一対一のヘッダをお勧めします。
つまり、ボード.cppならボード.hを宣言します。
こうした方が後々使い回しの面で有利になります。

それとボードとコマの描画は、こうしたらシンプルになります。
ソースのインデントを合わせないと後々バグのもとになるので合わせるようにしてください。

コード:

#include "Sengen.h"

// 盤のデータ
static int BanData[ BAN_HEIGHT ][ BAN_WIDTH ] =
{
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 2, 1, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 1, 2, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1 } ,
	{ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 } 
} ;

void Display_Ban(){
	int h, w;

	//背景の描画
	DrawGraph(0,0,GHandle2,FALSE);

	//コマとボードの描画
	for( h = 0 ; h < BAN_HEIGHT ; h ++ )
	{
		for( w = 0 ; w < BAN_WIDTH ; w ++ )
		{
			switch(BanData[ h ][ w ] )
			{
			case BAN : 
				DrawGraph(h*BAN_SIZE, w*BAN_SIZE, GHandle1, TRUE);
				break;
			case BLACK :
				DrawGraph(h*BAN_SIZE, w*BAN_SIZE, GHandle1, TRUE);
				DrawGraph(h*BAN_SIZE, w*BAN_SIZE, GHandle3, TRUE);
				break;
			case WHITE :
				DrawGraph(h*BAN_SIZE, w*BAN_SIZE, GHandle1, TRUE);
				DrawGraph(h*BAN_SIZE, w*BAN_SIZE, GHandle4, TRUE);
				break;
			}
		}
	}
}
最後にソースが多いので、zipで添付してくださいね~。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#26

投稿記事 by 小人 » 14年前

あぁ!なるほど!
元から画像ロードと描画を分けてたのが間違いだったのかぁ~・・・。
・初期化
・計算
・描画
・終了処理
にしないと駄目でしたorz
参考サイトにはもう10回ほどアクセスしたのに、何故気付けなかったのか・・・。

勉強になりました;


ヘッダとソースの一対一、了解しました。
しかしこの場合 例えばボード.hを作成した場合、中に記載するコードは
load

Display_Ban
の関数プロトタイプ宣言と、これらに関係した#define 達でしょうか?

そうすると最終的にメイン.cppの一番上でそれぞれのヘッダファイルをインクルードする形になる・・・・のでしょうか。

そうなると二重インクルード等の心配は必要なくなりそうですね。



ボードとコマの描画方法勉強になりましたm(_ _)m
これならしっかり盤目の上にコマが描画されますね。素晴らしい・・・。


ソースコードのZip了解しました。
次回からそう致します。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#27

投稿記事 by softya(ソフト屋) » 14年前

小人 さんが書きました:ヘッダとソースの一対一、了解しました。
しかしこの場合 例えばボード.hを作成した場合、中に記載するコードは
load

Display_Ban
の関数プロトタイプ宣言と、これらに関係した#define 達でしょうか?

そうすると最終的にメイン.cppの一番上でそれぞれのヘッダファイルをインクルードする形になる・・・・のでしょうか。

そうなると二重インクルード等の心配は必要なくなりそうですね。
基本的には、そのルールでOKです。
loadよりはinitで統一するのをお勧めします。中でstatic関数のload()を呼んでも良いけど入り口はinit(初期化)で。
それとヘッダの2重インクルードのガードはした方がいいですよ。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#28

投稿記事 by 小人 » 14年前

ごめんなさい、自分の浅学故に理解出来なかったのですが
init で統一するというのは一体どういう事でしょうか・・・?

すみません。

二重に対するガードはしました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#29

投稿記事 by softya(ソフト屋) » 14年前

オセロに限った話ではないのですが、
・初期化
・計算
・描画
・終了処理
の各呼び出し関数名が統一されているといちいち関数の機能を調べなくて良いのでソースコードの可読性が増すというメリットがあります。
これは自分で作ったプログラムでも半年後には忘れているので必要な処置です。

よく使われるのが「d.3章 ゲームの設計と分割コンパイル(1)」の例にも書かれている
// 初期化をする
void Player_Initialize();
// 動きを計算する
void Player_Calc();
// 描画する
void Player_Graph();
// 終了処理をする
void Player_Finalize();
などで、モジュール名と機能を名前で表すことです。

この考えを取り入れて、
Dixqさん的なら Board_Initialize()とBoard_Graph()
私の趣味なら Board_Init()とBoard_Draw()
などの名前にされてはどうでしょうか?
処理としては、Board_Init()の中でロードをしても良いですし、中でBoard_load()を呼び出しても構いません。
兎に角Board_Init()を呼び出せば初期化してくれるんだって事にしておくことが大事です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#30

投稿記事 by 小人 » 14年前

なるほど!
理解しました。

感謝ですm(_ _)m

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#31

投稿記事 by 小人 » 14年前

コンパイルエラー無く起動するのにエラー落ちすると何をどうして良いのやら・・・・・・・。。

エラー落ちの感じから、多分何処かしらのループ文がおかしそうですが。

まだ終了に関する関数は作ってません。ちょっとそれ以前の問題でしたのでorz
添付ファイル
オセロ.zip
(5.04 MiB) ダウンロード数: 146 回

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#32

投稿記事 by softya(ソフト屋) » 14年前

小人 さんが書きました:コンパイルエラー無く起動するのにエラー落ちすると何をどうして良いのやら・・・・・・・。。

エラー落ちの感じから、多分何処かしらのループ文がおかしそうですが。

まだ終了に関する関数は作ってません。ちょっとそれ以前の問題でしたのでorz
プログラムは動いてからのエラーを取るのがメインなので、コンパイルエラーが取れてからのデバッグが本番です。
出ているエラーメッセージと出ているエラーのソースの行番号を教えてください。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 14年前
住所: 東海地方
連絡を取る:

Re: RTS制作までの道順について(初心者)

#33

投稿記事 by softya(ソフト屋) » 14年前

とダウンロードしたコードを試したら無限ループのようですね。
この場合は、デバッガ→すべて中断で実行を中断します。

あとは、ステップイン、オーバー、アウトを使ってトレースしてみましょう。
イン:1命令進める、関数呼び出しがあったらそちらに移ってデバッグを続ける。
オーバー:1命令進める、関数呼び出しがあっても関数を処理して1命令としてあるかい関数の中には移らない。
アウト:呼び出し元に戻る。

[追記]
あと、こんなデバッグ方法もあります。
「簡単RPG講座 番外編。 デバッグ入門 • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/blog.php?u=114&b=982&c=2
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#34

投稿記事 by 小人 » 14年前

おぉ!
この機能凄く便利です!

色々直せる所が見えてきました。有難う御座いますm(_ _)m

小人
記事: 78
登録日時: 14年前

Re: RTS制作までの道順について(初心者)

#35

投稿記事 by 小人 » 14年前

長くなってしまったので一旦ここで解決して新しく立てます。

閉鎖

“C言語何でも質問掲示板” へ戻る