ポンクソフト

7ならべの作成 - C言語とelで様々なゲームを作ろう

前ページ C言語とelで様々なゲームを作ろう TOP 次ページ

目次

  1. C言語とelで様々なゲームを作ろう
  2. Visual C++ .NET での設定
  3. テンプレートファイルの解説
  4. シューティングゲームの作成(チュートリアル)
  5. パックマン的ゲームの作成(チュートリアル)
  6. ブロック崩しの作成
  7. 15パズルの作成
  8. 横スクロールジャンピングゲームの作成
  9. オセロの作成
  10. 神経衰弱の作成
  11. 7ならべの作成
  12. テトリスの作成
  13. ぷよぷよの作成

ソース・プロジェクトファイル

ソースファイル
プロジェクトファイル(Visual C++ 6.0)

ゲームの説明

トランプの7ならべです。左クリックでカードを場にだし、右クリックでパスです。コンピュータ3人と対戦します。本当の7ならべとは違い、場に7が最初から出ていて、ジョーカーはありません(プログラムを簡単にするため)。

プログラムの解説

構造体

struct {
  int top;
  int end;
} ba[4] = {6, 6, 6, 6, 6, 6, 6, 6};
場に出されている先頭のカードと末尾のカードの数字を持ちます。ba[0] がスペード、ba[1] がハート、ba[2] がダイヤ、ba[3] がクローバーの行を表わします。top が先頭、end が末尾のカードの数字で、1 ~ K をそれぞれ 0 ~ 12 で表わします。例えば、ハートの行が [6][7][8][9][10][J] と並んでいた場合、ba[1].top は 5、ba[1].end は 10 となります。今回は最初に7を並べておくので、初期値として top と end 両方に 6 を入れておきます。
struct {
  int num;
  bool pass;
  int mark[12];
  int no[12];
} pl[4];
各プレイヤーの情報を持つ構造体です。pl[0] が人間、pl[1] ~ pl[3] がそれぞれコンピュータ1~コンピュータ3の情報を持ちます。各メンバの意味は以下の通り。
mark は、0 がスペード、1 がハート、2 がダイヤ、3 がクローバーです。

定数・グローバル変数

DDOBJ bmpCard, bmpMark, bmpNumber;
カード(表と裏)、マーク(4種類)、番号(13種類)のスプライトオブジェクトです。使用画像は神経衰弱の時と同じですが、裏向きのカードは今回使いません。
suijaku_bmp.gif

ウインドウ生成関数 画像読み込み処理

bmpCard = elDraw::LoadObject("card.bmp");
bmpMark = elDraw::LoadObject("mark.bmp");
bmpNumber = elDraw::LoadObject("number.bmp");
カード・マーク・番号の各画像をスプライトに読み込んでいます。

ウインドウ生成関数 シャッフル処理

カードをシャッフルして各プレイヤーに配る処理を行なう部分です。
int mk, no, cn, pn;
シャッフル用のワーク変数で、mk はカードのマーク、no はカードの数字、cn は各プレイヤーの何枚目のカードを配っているか、pn はプレイヤー番号を表わします。
int ck[4][13] = {0};
シャッフルをして、配り終えたカードをチェックする配列です。配り終えたカードの配列要素には 1 が入ります。
for (mk = 0; mk < 4; mk++) ck[mk][6] = 1;
7のカードは配らないので、先にチェックしておきます。
for (pn = 0; pn < 4; pn++) {
プレイヤーを4人分ループします。
for (cn = 0; cn < 12; cn++) {
最初は各プレイヤーに 12 枚ずつ配るので、12 回ループします。
do {mk = rand() % 4; no = rand() % 13;} while (ck[mk][no] == 1);
配るカードのマーク (mk) と番号 (no) をランダムでひとつ選んでいます。配り終えていないカードが選択されるまでループします。
pl[pn].mark[cn] = mk;
pl[pn].no[cn] = no;
プレイヤーに選ばれたカードを配ります。
ck[mk][no] = 1;
配り終えたチェックを入れます。
pl[pn].num = 12;
pl[pn].pass = false;
各プレイヤーの初期値として、持っているカード枚数を 12 枚とし、パスフラグを false とします。

CardPrint カードを表示する関数

void CardPrint(int x, int y, int no, int mark)
(x, y) の位置に、数字が no、マークが mark のカードを表示する関数です。
elDraw::Layer(x, y, bmpCard, 0, 0, 40, 64);
elDraw::Layer(x + 6, y + 5, bmpMark, mark * 28, 0, mark * 28 + 28, 28);
elDraw::Layer(x + 6, y + 33, bmpNumber, no * 28, 0, no * 28 + 28, 26);
まずカードの土台を描画して、その上にマークと数字を描画します。

CardPut 指定されたカードを場に出す関数

bool CardPut(int pn, int cn)
番号 pn プレイヤーの手持ちから cn 番目のカードを場に出す関数です。戻り値として、実際に場に出すことができれば true、出せなければ false を返します。
bool ret = true;
ret は戻り値です。まず最初はカードを出せると仮定しておきます。
int mk = pl[pn].mark[cn];
mk には出そうとするカードのマークが入ります。
if (pl[pn].no[cn] == ba[mk].top - 1) ba[mk].top--;
pl[pn].no[cn] は出そうとするカードの数字です。この数字が場の先頭の数字-1ならばカードを出し、場の先頭の数字をひとつ減らします。
else if (pl[pn].no[cn] == ba[mk].end + 1) ba[mk].end++;
出そうとするカードの数字が場の末尾の数字+1ならばカードを出し、場の末尾の数字をひとつ増やします。
else ret = false;
場に出せなければ戻り値を false とします。
if (ret) {
  for (int i = cn + 1; i < pl[pn].num; i++) {
    pl[pn].no[i - 1] = pl[pn].no[i];
    pl[pn].mark[i - 1] = pl[pn].mark[i];
  }
  pl[pn].num--;
}
ret が true の場合、つまりカードを場に出せた場合に、出したカード以降のカードをひとつ前に詰める処理を行い、手持ちのカード数をひとつ減らします。

メイン画面 変数

static int nowpl = 0;
現在のプレイヤー番号が入る変数です。
static float wait = 0;
コンピュータの処理は一瞬で終わるので、どのコンピュータがどんなカードを出したかは分かりにくくなります。そこでコンピュータの番では時間待ちを行なっているのですが、それを管理する変数が wait です。
char* pname[4] = {"You", "Com1", "Com2", "Com3"};  // プレイヤー名
各プレイヤーの名前です。

メイン画面 人間の番

if (MouseCL) {
マウスが左クリックされたときは、カードを場に出す処理を行ないます。
int xs = (640 - pl[0].num * 40) / 2;
xs には、手持ちのカードの先頭の x 座標が入ります。640 は画面の横幅です。これから全部のカードを足した幅を引き、2 で割ることによってカードを中央に表示しています。
if (MousePX >= xs && MousePX < xs + pl[0].num * 40 &&
  MousePY >= 384 && MousePY < 384 + 64) {
マウスが手持ちのカードの上に乗っているとき、if 文内の処理を行ないます。
cn = (MousePX - xs) / 40;
cn に何番目のカードをクリックしたかが入ります。
if (CardPut(0, cn)) {
  pl[0].pass = false;
  nowpl++;
}
クリックしたカードが場に置ければ、パスフラグをクリアして、次のプレイヤーの番にします。
} else if (MouseCR) {
  pl[0].pass = true;
  nowpl++;
}
マウスが右クリックされたときは、パスフラグをセットして、次のプレイヤーの番にします。

メイン画面 コンピュータの番

wait += FrameTime;
if (wait > 0.3) {
  wait = 0;
まず wait にフレーム時間を足し、0.3 秒以上になれば wait をクリアしてからコンピュータの処理を行ないます。FrameTime は el のシステム変数で、各フレームの経過秒数を保持しています。
int ck[12] = {0};
ck は手持ちのカードからどのカードが出せないかをチェックする配列です。出せないカードの要素には 1 が入ります。
pl[nowpl].pass = true;
まず仮にパスフラグをセットしておきます。
for (int i = 0; i < pl[nowpl].num; i++) {
カードを場に出せるかを、手持ちのカード全てにおいてチェックするループです。
do cn = rand() % pl[nowpl].num; while (ck[cn]);
出すカードをランダムで選びます。チェック済みでないカードが選ばれるまでループします。
ck[cn] = 1;
出すカードをチェックします。
if (CardPut(nowpl, cn)) {
  pl[nowpl].pass = false;
  break;
}
選んだカードが場に置ければ、パスフラグをクリアし、ループを抜けます。
if (++nowpl >= 4) nowpl = 0;
次のプレイヤーにし、4人目まで行ったら1人目に戻します。

メイン画面 カード表示処理

for (int mk = 0; mk < 4; mk++) {
  for (int no = 0; no < 13; no++) {
    if (no >= ba[mk].top && no <= ba[mk].end)
      CardPrint(60 + no * 40, 64 + mk * 64, no, mk);
  }
}
場の先頭以上で末尾以下の数字のカードを CardPrint 関数によって表示しています。
for (cn = 0; cn < pl[0].num; cn++) {
  CardPrint((640 - pl[0].num * 40) / 2 + cn * 40, 384, pl[0].no[cn], pl[0].mark[cn]);
}
人間の手持ちのカードを表示している部分です。(640 - pl[0].num * 40) / 2 の計算によってカードを中央にセンタリングして表示しています。

メイン画面 プレイヤー情報表示処理

for (i = 0; i < 4; i++) {
  int x = 8 + i * 100;
  SHOW(x, 8, pname[i]);
  SHOW2(x, 24, "残り%d枚", pl[i].num);
  if (pl[i].pass) SHOW(x, 40, "Pass!");
}
各プレイヤーの名前・残りカード枚数・今回パスをしたかどうかを画面に表示している部分です。

メイン画面 勝利者チェック処理

for (i = 0; i < 4; i++) {
  if (pl[i].num <= 0) {
    MESG("%s Win!!", pname[i]);
    elDraw::Exit();
  }
}
プレイヤーの残り枚数が0になったら、プレイヤーの名前と勝利メッセージを表示して終了します。
前ページ C言語とelで様々なゲームを作ろう TOP 次ページ
このエントリーをはてなブックマークに追加 そっか0

このページに関するコメントをどうぞ

お名前:


kuv
このプログラムでは、連結判定の時はつながっているぷよごとに結合番号をつけ、消せる場合はelist配列の結合番号と同じ要素番号のところにフラグを立てる、という方法をとっています。
ですが、例えばぷよをわざとつながらないように置いていった場合、結合番号の最大数がelist配列の要素数をオーバーしてしまうのではないでしょうか?
それに対応するにはelist配列の要素数をフィールドの大きさと同じ6*13=78にしなければいけないと思うのですが、30にしているのには何か意図があるのでしょうか?
2017/07/11 14:52

しお
vectorではなくlistを使っているのはなぜなのでしょうか?
2017/05/07 19:06

surach
なぜ弾のX座標が sx = px + 24;で時機の中央になるんですか?
2017/05/05 11:09

何度やってもできないー^^
ファイルが見つかりませんとなるのですが・・・
どうすれば?
2017/01/20 17:23

ww
dxlibは外部から自分で導入するものです。しっかり導入しましたか?
2016/12/26 12:29

ww
IncludePathとLibraryPathはdxライブラリを導入したときのフォルダパスが間違っているため、dxlib.hがあるフォルダパスを設定しなおせば動きます。
2016/12/26 12:28

ww
MTdは、プロジェクトのプロパティの設定項目から変更できる。
Debugモードの設定が、違うようですね↓
2016/12/26 12:26

笑子
実行したところ、白い画面が出てきてしまいます。解決策は、あるのでしょうか?
2016/11/10 23:38


背景を挿入したいのですが,どこにどんな風にプログラムを加えればいいのでしょうか?
2016/09/24 21:44

華仙 学
オセロプログラムに関してです。
Visual Studio 2015を使ってます。このプログラムを打ち込んだところ、「'RuntimeLibrary'の不一致が検出されました。値'MTd_StaticDebug'がMDd_DynamicDebugの値'othello.obj'と一致しません」と出てしまいます。いろいろ調べていますがなかなか解決しません。何か解決策はあるでしょうか?
2016/08/17 21:12

残りを読む »