前回(d:id:hecomi:20100727),画像の2値化を行ないました.次にこれを継承して,画像を弾幕の素へと変換するクラスを作りたいと思います.前回同様,中身は「龍神録プログラミングの館(http://dixq.net/rp/)」の第55章(http://dixq.net/rp/55.html)のコードを参考にしています.というかそのままです.
前回から多少変更がありましたので,画像を2値化するクラス新しいCLoadBmpは以下に置いておきます.
LoadBmp.cpp
LoadBmp.h
BMPを読み込んで弾幕(の素)を作るクラス
ImgToBulletCurtain.h
#include <vector>
#include "LoadBmp.h"
#define BULLET_NUM_MAX (3000)
typedef struct {
float X, Y;
} _BULLET_POS;
typedef struct {
int Num;
_BULLET_POS Bullet[BULLET_NUM_MAX];
} _IMG_BULLET_OUTPUT_DATA;
class CImgToBulletCurtain : public CLoadBmp{
private:
int State;
double Len;
int Img[10];
int ImgNum;
_BULLET_POS Bullet[BULLET_NUM_MAX];
int Num;
public:
CImgToBulletCurtain(const char* fileName);
void CalcBulletPosition();
void ChangeState();
void ChangeBulletColor(int n);
void ChangeLen(double dl);
void Draw();
void Output(const char* fileName);
};
Dixqさんのコードを参考に,クラスを設計したらこんな感じになりました.
ImgToBulletCurtain.cpp
#include "ImgToBulletCurtain.h"
#include <fstream>
#include <DxLib.h>
#define MIN_BULLET_MARGIN (1)
#define DEFAULT_BULLET_MARGIN (5)
using namespace std;
CImgToBulletCurtain::CImgToBulletCurtain(const char* fileName)
: CLoadBmp(fileName), Len(DEFAULT_BULLET_MARGIN), State(0), ImgNum(0), Num(0)
{
Binarization();
CalcBulletPosition();
}
void CImgToBulletCurtain::CalcBulletPosition()
{
Num = 0;
for (int x=0; x<Width; x++) {
for (int y=0; y<Height; y++) {
if (Num >= BULLET_NUM_MAX ) {
break;
}
if (BitTable(x,y)) {
bool flag = true;
for (int i=0; i<Num; i++) {
double dx, dy;
dx = x - Bullet[i].X;
dy = y - Bullet[i].Y;
if (dx*dx + dy*dy < Len*Len) {
flag = false;
break;
}
}
if (flag) {
Bullet[Num].X = static_cast<float>(x);
Bullet[Num].Y = static_cast<float>(y);
Num++;
}
}
}
}
}
void CImgToBulletCurtain::ChangeState()
{
State = (State+1)%3;
}
void CImgToBulletCurtain::ChangeLen(double dl)
{
Len += dl;
if (Len <= MIN_BULLET_MARGIN) {
Len = MIN_BULLET_MARGIN;
}
CalcBulletPosition();
}
void CImgToBulletCurtain::ChangeBulletColor(int n)
{
ImgNum = ( ImgNum + n ) % ( sizeof(Img)/sizeof(int) );
}
void CImgToBulletCurtain::Draw()
{
static bool ifFirst = false;
if (!ifFirst) {
LoadDivGraph("bullet/small.png", 10, 10, 1, 16, 16, Img);
ifFirst = true;
}
static int
Black = GetColor(0,0,0),
White = GetColor(255,255,255);
int bgColor = White, fontColor = Black;
if (State == 1) {
bgColor = Black;
fontColor = White;
}
if (State == 0) {
CLoadBmp::Draw();
} else {
DrawBox(0, 0, Width, Height, bgColor, TRUE);
}
if (State <= 2) {
for (int i=0; i<Num; i++) {
DrawRotaGraphF(Bullet[i].X, Bullet[i].Y, 1.0, 0, Img[ImgNum], TRUE);
}
}
DrawFormatString(0, 0, fontColor, "間隔 = %.1f", Len);
DrawFormatString(0, 30, fontColor, "弾数 = %u", Num);
}
void CImgToBulletCurtain::Output(const char *fileName)
{
_IMG_BULLET_OUTPUT_DATA outputData;
outputData.Num = Num;
memcpy(&(outputData.Bullet), &Bullet, sizeof(Bullet));
for (int i=0; i<Num; i++) {
outputData.Bullet[i].X -= Width/2;
outputData.Bullet[i].Y -= Height/2;
outputData.Bullet[i].X /= Width/2;
outputData.Bullet[i].Y /= Height/2;
}
ofstream ofs(fileName, ios::out | ios::binary);
if (ofs.fail()) {
printfDx("file open error: %s\n", fileName);
return;
}
ofs.write(reinterpret_cast<char*>(&outputData), sizeof(outputData));
}
1ピクセルずつ調べて,2値化後の黒い点にぶち当たったら,過去に置かれた弾との距離をすべて調べて,距離が変数Lenより離れていればそこを新しい弾の位置として登録していく,という操作を繰り返しています.うまく考えられてますね.
実際に実行してみた
次のコードをmain.cppとして実行しました.例のごとくKeyMouse.hは51章のものを用いています.
#include <DxLib.h>
#include "ImgToBulletCurtain.h"
#include "KeyMouse.h"
#define DEFAULT_WINDOW_WIDTH (100)
#define DEFAULT_WINDOW_HEIGHT (100)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
CImgToBulletCurtain bmp("bmp/hecomi.bmp");
SetGraphMode(bmp.GetWidth(), bmp.GetHeight(), 16);
SetWindowSizeChangeEnableFlag(TRUE);
if (DxLib_Init() == 1 || SetDrawScreen(DX_SCREEN_BACK) != 0) {
return -1;
}
bool Flag = true;
int Key[256];
while (
ProcessMessage() == 0 &&
ClearDrawScreen() == 0 &&
GetHitKeyStateAll_2(Key) == 0 &&
Key[KEY_INPUT_ESCAPE] == 0
) {
if (Key[KEY_INPUT_SPACE] == 1) bmp.ChangeState();
if (Key[KEY_INPUT_RIGHT] == 1 || Key[KEY_INPUT_RIGHT] > 20) bmp.ChangeLen(0.2);
if (Key[KEY_INPUT_LEFT] == 1 || Key[KEY_INPUT_LEFT] > 20) bmp.ChangeLen(-0.2);
if (Key[KEY_INPUT_UP] == 1 || Key[KEY_INPUT_UP] > 20) bmp.ChangeBulletColor(1);
if (Key[KEY_INPUT_DOWN] == 1 || Key[KEY_INPUT_DOWN] > 20) bmp.ChangeBulletColor(-1);
if (Key[KEY_INPUT_O] == 1) {
bmp.Output("output.dat");
printfDx("Output was done.\n");
}
bmp.Draw();
ScreenFlip();
}
DxLib_End();
return 0;
}
キーを押すと,クラスのメンバ変数が変更されるようになってます.それに従ってDrawします.「o」キーを押すとバイナリで構造体を出力します.
実行結果
上手く動作して,出力もできました.次のエントリでは,実際に弾幕にした結果を紹介したいと思います.