とりあえずゲームシステムとしての基盤が見えてきた気がします。基本的な設計が完了し(正しい(効率的である)とは言っていない)
必要な処理をぶん投げてブロック崩しっぽい見た目になってきました。 コメントはいっぱいつけた。マジックナンバー? しらんな。これから減らしていきます()
やはりあたり判定回りがすっきりしない設計。Blocks_tのUpdateで結局なんもやってないし。 全部FieldのUpdateに投げてる、でも処理を二回に分けると効率が悪いっていうか...
どうしたこった。
それと矩形対円のあたり判定はそれっぽくしてますけど...なんというか、とんでもなくめり込みやすそうなコードです。つか普通にめり込みます。
どうやんのかさっぱり...
そのほかの処理も現時点では動けばいい主義なので適当な部分が多いです。
これからスコア機能とかボールの挙動を変えたりとかタイトル画面であるとか...そういうのを追加していくんですかね。
オブジェクト指向とかいってるけど拡張性皆無だよこのコード...
突っ込んでください(切実) コピペで動きます。
► スポイラーを表示
#include "DxLib.h"
/******************全体で使う値*******************/
const int WINDOW_X = 640;
const int WINDOW_Y = 480;
/**********************クラス宣言***********************/
class Bar_t
{
private:
int x; //座標
int y;
int rect_x; //サイズ
int rect_y;
int color;
bool flag; //何かに使うかもしれない
public:
Bar_t();
~Bar_t(){}
int get_x(){ return x; }
int get_y(){ return y; }
int get_rect_x(){ return rect_x; }
int get_rect_y(){ return rect_y; }
void Init();
void Update();
void Draw();
};
class Ball_t
{
private:
//初期位置と現在位置用に二つ
float x[2]; //座標
float y[2];
float v_x[2]; //軸の速度
float v_y[2];
int r[2];
int color[2];
bool flag[2]; //何かに使うかもしれない
//ボールが矩形に対してどの面に当たっているかを示す変数
bool Horizon;
bool Stripe;
bool Lean;
public:
Ball_t();
~Ball_t();
//ボールの位置とか半径を返す
float get_x(){ return x[0]; }
float get_y(){ return y[0]; }
int get_r(){ return r[0]; }
//呼ぶと各フラグが有効になる
void isHitted_Horizon(){ Horizon = true; }
void isHitted_Stripe(){ Stripe = true; }
//void isHitted_Lean(){ Lean = true; }
//初期化 x座標、y座標、x方向への速度、y方向への速度、半径
void Init(float arg_x, float arg_y, float arg_v_x, float arg_v_y, int arg_r);
void Reset();
void Update();
void Draw();
};
class Blocks_t
{
private:
typedef struct Block
{
int x;
int y;
int color;
bool isThere;
bool isHit;
}Block;
const int block_x = 8;
const int block_y = 8;
const int blocksize_x = (int)580 / block_x;
const int blocksize_y = (int)256 / block_y;
const int space = (int)64 / block_x;
Block block[8][8];
//Block block[block_x][block_y]; とするとエラー
//「静的でないメンバー参照は特定のオブジェクトを基準とする相対参照である必要があります。」
public:
Blocks_t();
~Blocks_t();
//x,y番目のブロックの座標とそれぞれのサイズを返す
int get_x(int num_x, int num_y){ return block[num_x][num_y].x; }
int get_y(int num_x, int num_y){ return block[num_x][num_y].y; }
int get_rect_x(){ return blocksize_x; }
int get_rect_y(){ return blocksize_y; }
//x,y番目のブロックの状態を取得する
bool get_flag(int num_x, int num_y){ return block[num_x][num_y].isThere; }
//x,y番目のブロックの有効化、無効化
void isthere(int num_x, int num_y){ block[num_x][num_y].isThere = true; }
void isnotthere(int num_x, int num_y){ block[num_x][num_y].isThere = false; }
void Init();
void Reset();
void Update();
void Draw();
};
class Field_t
{
private:
//ボールの状態
enum{
NO,
HORIZON,
STRIPE,
BOTH,
};
//とりあえずフィールド上のオブジェクトたち
Ball_t *ball;
Blocks_t *blocks;
Bar_t *bar;
//フィールドの状態
int state;
const int FIELD_X = 640;
const int FIELD_Y = 480;
public:
Field_t();
//あたり判定の関数、実装は現時点では適当(円 対 矩形)
//円のx座標、円のy座標、円の半径、矩形の各座標たち
int HitTest(int arg_cx, int arg_cy,int arg_cr, int arg_bx,int arg_by,int arg_bvx,int arg_bvy);
void Init();
void Update();
void Draw();
void Reset();
};
/*************クラスの中身*************/
//バーに関して
//コンストラクタ、とりあえず初期化
Bar_t::Bar_t()
{
x = WINDOW_X / 2;
y = WINDOW_Y - 40;
rect_x = 96;
rect_y = 16;
color = GetColor(64, 255, 255);
}
//今のところ無し
void Bar_t::Init()
{
}
//更新はマウスの座標取得のみ
void Bar_t::Update()
{
GetMousePoint(&x, NULL);
}
//描画はマウスの座標に従いバーを描画
void Bar_t::Draw()
{
DrawBox(x, y, x + rect_x, y + rect_y, color, true);
}
//ボールに関して
//初期化
Ball_t::Ball_t()
{
x[0] = 0.0;
y[0] = 0.0;
v_x[0] = 0.0;
v_y[0] = 0.0;
r[0] = 0;
color[0] = GetColor(255, 64, 32);
}
//ゲームの初期化
void Ball_t::Init(float arg_x,float arg_y,float arg_v_x,float arg_v_y,int arg_r)
{
//初期状態ではどの面に対しても接触していない
Horizon = false;
Stripe = false;
Lean = false;
//値を代入
x[0] = arg_x;
y[0] = arg_y;
v_x[0] = arg_v_x;
v_y[0] = arg_v_y;
r[0] = arg_r;
//初期位置に代入
x[1] = x[0];
y[1] = y[0];
v_x[1] = v_x[0];
v_y[1] = v_y[0];
r[1] = r[0];
}
//初期状態にボールの位置、速度、方向、半径を戻す
void Ball_t::Reset()
{
//現在位置を初期位置に
x[0] = x[1];
y[0] = y[1];
v_x[0] = v_x[1];
v_y[0] = v_y[1];
r[0] = r[1];
}
//更新の関数、
void Ball_t::Update()
{
//壁際では跳ね返るよね
if (x[0] Init(320, 400, 3, 3, 8);
}
//リセットする
void Field_t::Reset()
{
ball->Reset();
blocks->Reset();
}
//主にボール対ブロック、 ボール対バーのあたり判定と各オブジェクトの移動計算
void Field_t::Update()
{
//バーに対してとブロックに対しての判定の状況を保存する
int ball_bar = NO,
ball_block[8][8];
//結果的にボールの状態はどうなるか。
int hit_ball = NO;
//ball_barにボールとバーの接触状態を代入
ball_bar = HitTest(ball->get_x(), ball->get_y(), ball->get_r(), bar->get_x(), bar->get_y(), bar->get_rect_x(), bar->get_rect_y());
//全ブロック分に対してあたり判定、ただし有効でないブロックに対してはあたり判定を行わない
for (int i = 0; i get_flag(i, k))
{
//ball_blocksにi,k番目のブロックとボールの接触状態を代入
ball_block[i][k] = HitTest(ball->get_x(), ball->get_y(), ball->get_r(), blocks->get_x(i, k), blocks->get_y(i, k), blocks->get_rect_x(), blocks->get_rect_y());
//接触していたら(NOでなければ)そのブロックを無効化する
if (ball_block[i][k] != NO) blocks->isnotthere(i, k);
//バー、ブロックに対して何れかが接触していれば、ボールは接触状態になる
if (ball_bar == STRIPE || ball_block[i][k] == STRIPE) hit_ball = STRIPE;
if (ball_bar == HORIZON || ball_block[i][k] == HORIZON) hit_ball = HORIZON;
if (ball_bar == BOTH || ball_block[i][k] == BOTH) hit_ball = BOTH;
//同フレームに、二つ以上消えないようにするため何か一つブロックがボールに接触したらループを抜ける
if (ball_block[i][k] != NO)break;
}
}
//実際にballクラス内の値を変える
if (hit_ball == STRIPE) ball->isHitted_Stripe();
if (hit_ball == HORIZON) ball->isHitted_Horizon();
//if (hit_ball == BOTH) ball->isHitted_Lean(); 保留
//デバッグ用
DrawFormatString(0, 420, GetColor(255, 0, 0), "State:%d", hit_ball);
//というわけで各オブジェクトを更新
ball->Update();
blocks->Update();
bar->Update();
}
void Field_t::Draw()
{
bar->Draw();
blocks->Draw();
ball->Draw();
}
//毎フレーム必須の処理
bool SystemUpdate()
{
if (
ScreenFlip() == 0 &&
ProcessMessage() == 0 &&
ClearDrawScreen() == 0 &&
CheckHitKey(KEY_INPUT_ESCAPE) == 0
) return true;
return false;
}
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
//ウィンドウやDxLibの初期化
ChangeWindowMode(TRUE);
DxLib_Init();
SetDrawScreen(DX_SCREEN_BACK);
//フィールドの作成(ゲームメイン)
Field_t gamemain;
//ゲームメインの初期化
gamemain.Init();
//メインループ
while (SystemUpdate())
{
//ゲームメインの更新
gamemain.Update();
//ゲームメインの描画
gamemain.Draw();
}
//DxLibの終了処理
DxLib_End();
return 0;
}
//終わり