合計 昨日 今日

きょーちゃん
 
記事: 11
登録日時: 2010年10月13日(水) 00:40
日記: 日記を見る (17)
日記
- 3月 2011
ボランティア (0)
   2011年3月26日(土) 00:06
物理とか詳しい方何か思いつきませんか? (7)
   2011年3月19日(土) 18:49
仕事のこと。 (7)
   2011年3月02日(水) 00:34

+ 2月 2011
+ 11月 2010
+ 10月 2010
カテゴリー
カテゴリに指定された日記はありません
フィード
1つ前へ次へ

できたできたヽ(´▽`)ノ

パーマリンクby きょーちゃん on 2011年2月12日(土) 20:48

とりあえず1画面分マップ表示できました!
やった~♪

画像

次はスクロールさせたり、レイヤー重ねてみたり
夢が膨らみます(*^▽^*)
最後に編集したユーザー きょーちゃん [ 2011年2月12日(土) 21:58 ], 累計 1 回

コメント数: 4 閲覧数: 3102

出来るには出来たが

パーマリンクby きょーちゃん on 2011年2月12日(土) 14:39

DXライブラリとfmfファイル使ってマップ描画するコード出来ました。
こんな感じになります。
画像

ループとかいろんな代入とか怪しいのでやってなくて、マップチップをひとつだけ表示する代物ですが。
でも私にとっては大きな一歩で、出来たときかなり感動でした^^;


ただ、しばらくすると強制終了しちゃうんですよね・・・
「グラフィックの数が許容量を越えました終了します」だそうです。
たぶんメモリの開放?とか何もやってないせいだ・・・
まだまだまだまだ勉強が必要ですな・・・

コメント数: 6 閲覧数: 3722

初心者の私にはコードはこう見えます。。。(その2)

パーマリンクby きょーちゃん on 2011年2月12日(土) 11:25

前回の続き。

コード[C++]: 全て選択
1
2
3
4
5
6
7
//-----------------------------------------------------------------------------
// マップが開かれているか
//-----------------------------------------------------------------------------
BOOL CFmfMap::IsOpen() const
{
    return m_pLayerAddr != NULL;
}



NULLでなければレイヤーデータへのポインタ(m_pLayerAddr)を返すということか?
constの位置がちょっと不思議な気がする。
よく分からん。パス。

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
//-----------------------------------------------------------------------------
//  マップメモリを開放
//-----------------------------------------------------------------------------
void CFmfMap::Close(void)
{
    if (m_pLayerAddr != NULL)
    {
        delete [] m_pLayerAddr;
        m_pLayerAddr = NULL;
    }
}


delete:
new演算子を使って確保したメモリを解放する演算子。
どうやらnewとセットで使うのが決まりらしい。
なんでそのあとにNULLを入れてるのかとか
深く考えないことにする。

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//-----------------------------------------------------------------------------
//  指定レイヤの先頭アドレスを得る
//  引数:   レイヤ番号
//  戻り値:  正常終了    = レイヤデータのアドレス
//          エラー       = NULL
//  各レイヤデータは連続したメモリ領域に配置されてるので
//  指定レイヤデータのアドレスを計算で求める。
//-----------------------------------------------------------------------------
void* CFmfMap::GetLayerAddr(BYTE byLayerIndex) const
{
    // メモリチェック、範囲チェック
    if ((m_pLayerAddr == NULL) || (byLayerIndex >= m_fmfHeader.byLayerCount))
        return NULL;
 
    BYTE bySize = m_fmfHeader.byBitCount / 8;
    return m_pLayerAddr + m_fmfHeader.dwWidth * m_fmfHeader.dwHeight * bySize * byLayerIndex;
}


このあたりから具体的な感じで面白みが増してきますね。
byLayerIndexは、この後出てくるのですがレイヤーの番号です。
m_fmfHeader.byLayerCountはレイヤーの数。

レイヤーデータがきちんと読まれてない、もしくは読み込もうとするレイヤー番号が実際のレイヤー数より大きい場合はエラー。

で、その下。m_fmfHeader.byBitCountはレイヤデータのビットカウントらしい。
ビットカウント…よくわからんが、platinumで新規作成するとき、8ビットと16ビットを選択できる。たぶんあれのことだと思う。
bySizeは8ビットなら1、16ビットなら2になる。
8ビットで作ったデータでは、1つのマップチップ情報を保存するのに1バイト、16ビットなら2バイト必要ってことだろう。
レイヤーデータは連続したメモリ領域に配置されてると見出しに書かれている。

たぶんこんな感じだ。

<レイヤー0>
123   
456
789
<レイヤー1>
a b c
d e f
g h i

という風な配置のマップデータがあったとして、メモリ領域では

123456789abcdefghi

という風に配置してるんだと思う。
ここでの計算は、(byLayerIndex)番のレイヤー情報を読むにはどこから読み始めたらいいの~?ってのを計算しているようだ。
その式が最後の一行。言い換えると、
レイヤー情報の先頭の位置 + マップの横幅 * マップの縦幅 * ビット数 * レイヤー番号 

ビット数ってのがややこしいけど、上の例でレイヤー1の情報の先頭の位置を知りたければ、「a」の情報が入っている場所が分かれば良い。
ぱっと見では10番目。
だけどメモリ上では、「1」の情報を保存するのに8ビット使われている(8ビットで作った場合)。
つまり「a」の情報が入っている場所は10番目ではなく、10*8=80番目ということになる。
ビットでそのまま計算すればいいのに、なぜわざわざバイトに直すのかと思うけれど、
コンピュータは情報の記憶や処理、伝達をバイト単位で行うことが多い、ってことらしい。たぶんそれでだ。


コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//-----------------------------------------------------------------------------
// レイヤ番号と座標を指定して直接データを貰う
// 引数:
//  byLayerIndex    = レイヤ番号
//  dwX             = X座標(0~m_fmfHeader.dwWidth - 1)
//  dwY             = Y座標(0~m_fmfHeader.dwHeight - 1)
// 戻り値:
//  正常終了    = 座標の値
//  エラー       = -1
//-----------------------------------------------------------------------------
int CFmfMap::GetValue(BYTE byLayerIndex, DWORD dwX, DWORD dwY) const
{
    int nIndex = -1;
 
    // 範囲チェック
    if (byLayerIndex >= m_fmfHeader.byLayerCount ||
        dwX >= m_fmfHeader.dwWidth ||
        dwY >= m_fmfHeader.dwHeight)
        return nIndex;
 
    if (m_fmfHeader.byBitCount == 8)
    {
        // 8bit layer
        BYTE* pLayer = (BYTE*)GetLayerAddr(byLayerIndex);
        nIndex = *(pLayer + dwY * m_fmfHeader.dwWidth + dwX);
    }
    else
    {
        // 16bit layer 
        WORD* pLayer = (WORD*)GetLayerAddr(byLayerIndex);
        nIndex = *(pLayer + dwY * m_fmfHeader.dwWidth + dwX);
    }
 
    return nIndex;
}


ますます盛り上がってきました!
さっきの関数でほしいレイヤーの先頭のアドレスは分かったので、
今度ははそのレイヤー上の、座標(dwX、dwY)のマップのデータを取り出すための関数。

もうエラー処理はいいかな…飛ばします。

8ビットの場合から見てみる。
まず、pLayerにレイヤーの先頭のデータが書かれているアドレスを入れます。
で、ほしい座標のデータはそこから数えて何番目~?ってのを計算してます。
m_fmfHeader.dwWidthはマップの横幅です。
さっきも言ったとおり、マップ情報はずらーっと一列に並んでいるので、
n行目のデータを見るにはマップの横幅×n個分データを進める必要があるってことですね。

16ビットも式が同じで、処理同じじゃ~んって思ったら、データ型がWORDになってる。
WORDとは16ビット符号なし整数の型らしい。
う~ん、いろいろ決まりごとがあるんだろう。

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//-----------------------------------------------------------------------------
// レイヤ番号と座標を指定してデータをセット
//-----------------------------------------------------------------------------
void CFmfMap::SetValue(BYTE byLayerIndex, DWORD dwX, DWORD dwY, int nValue)
{
    // 範囲チェック
    if (byLayerIndex >= m_fmfHeader.byLayerCount ||
        dwX >= m_fmfHeader.dwWidth ||
        dwY >= m_fmfHeader.dwHeight)
        return;
 
    if (m_fmfHeader.byBitCount == 8)
    {
        // 8bit layer
        BYTE* pLayer = (BYTE*)GetLayerAddr(byLayerIndex);
        *(pLayer + dwY * m_fmfHeader.dwWidth + dwX) = (BYTE)nValue;
    }
    else
    {
        // 16bit layer 
        WORD* pLayer = (WORD*)GetLayerAddr(byLayerIndex);
        *(pLayer + dwY * m_fmfHeader.dwWidth + dwX) = (WORD)nValue;
    }
}


う~んここちょっと分からん。
さっきとまったく同じ処理をしてnValueに代入している気がするんだけど・・・
データを貰うのとセットするのって何が違うんだ・・・
読み進めたらわかるかな。とりあえずパス。
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//-----------------------------------------------------------------------------
// マップの横幅を得る
//-----------------------------------------------------------------------------
DWORD CFmfMap::GetMapWidth(void) const
{
    return m_fmfHeader.dwWidth;
}
//-----------------------------------------------------------------------------
// マップの高さを得る
//-----------------------------------------------------------------------------
DWORD CFmfMap::GetMapHeight(void) const
{
    return m_fmfHeader.dwHeight;
}
//-----------------------------------------------------------------------------
// チップの横幅を得る
//-----------------------------------------------------------------------------
BYTE CFmfMap::GetChipWidth(void) const
{
    return m_fmfHeader.byChipWidth;
}
//-----------------------------------------------------------------------------
// チップの高さを得る
//-----------------------------------------------------------------------------
BYTE CFmfMap::GetChipHeight(void) const
{
    return m_fmfHeader.byChipHeight;
}
//-----------------------------------------------------------------------------
// レイヤー数を得る
//-----------------------------------------------------------------------------
BYTE CFmfMap::GetLayerCount(void) const
{
    return m_fmfHeader.byLayerCount;
}
//-----------------------------------------------------------------------------
// レイヤーデータのビットカウントを得る
//-----------------------------------------------------------------------------
BYTE CFmfMap::GetLayerBitCount(void) const
{
    return m_fmfHeader.byBitCount;
}


なんやかんやいろいろ。
なんでいちいち関数にするのか。代入でいいんじゃないのと思ってしまうのはきっと素人考え。
あと順序もよくわからん。こいつら最初に書けばよさそうなのに…そこは好みの問題なのか?


って、そんなことを言ってる間にcppファイルは終わってしまった。
あれ??SetValue関数何に使うの???


なるほど分からん。
よし、次からマップ描画のためのコード書きに挑戦だ♪

コメント数: 5 閲覧数: 3563

初心者の私にはコードはこう見えます。。。

パーマリンクby きょーちゃん on 2011年2月12日(土) 00:07

*おことわり*
これはプログラミング超初心者の私が、platinumというマップエディタに付属の
fmfファイルを読み込むためのサンプルコードを一生懸命解読してみたものです。
新しく覚えたことや、疑問に思ったことを思うがままに書きなぐってます。
乱筆(?)乱文のとても読みづらい(そして長い)ものですが、間違ってる点などご指摘いただければ幸いです。

※引用しているコードはHyperDevice Softwareさんが製作されたものです。
著作権などまずかったら削除します…

[fmfmap.cpp]より

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
#include "fmfmap.h"
 
//-----------------------------------------------------------------------------
//  コンストラクタ
//-----------------------------------------------------------------------------
CFmfMap::CFmfMap(void) : m_pLayerAddr(NULL)
{}
//-----------------------------------------------------------------------------
//  デストラクタ
//-----------------------------------------------------------------------------
CFmfMap::~CFmfMap()
{
    Close();
}



この辺はよく分からんからパス^^;クラスの何かですね…今度勉強する。。。


コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
//-----------------------------------------------------------------------------
//  マップを開いてデータを読み込む
// 引数:    szFilePath  = マップファイルのパス
// 戻り値:   正常終了    = TRUE
//          エラー       = FALSE
//-----------------------------------------------------------------------------
BOOL CFmfMap::Open(const char *szFilePath)
{
    Close();
 
    // ファイルを開く
    HANDLE hFile = CreateFile(szFilePath, GENERIC_READ, FILE_SHARE_READ, NULL,  OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
        return FALSE;



ここではパブリック関数の宣言をしている。
CFmfMapがクラス名、Openが関数名、(const char *szFilePath)は引数。
ちなみにBOOLは型名。真か偽か判定。代入する値はtrueかfalse。

そっから下がずーっと関数の内容。なが~い!

Close()はファイルハンドルをクローズする関数。ここで何してるかはよく分からん。

CreateFileはAPI。オブジェクトを作成または開く。そのオブジェクトのハンドルを返す。
式はこんな感じ。

HANDLE CreateFile(LPCTSTR lpPathName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);

lpPathName:作成または開くオブジェクト名
dwShareMode:オブジェクトへのアクセス・タイプ
dwShareMode:共有方法の設定
lpSecurityAttributes:セキュリティ設定
dwCreationDisposition:ファイルの扱い方を設定
dwFlagsAndAttributes:ファイルの属性とフラグ
hTemplateFile:テンプレート・ファイルのハンドル

ここでは、
作成または開くオブジェクト名:szFilePath
オブジェクトへのアクセス・タイプ:GENERIC_READ(読み取り)
共有方法の設定:FILE_SHARE_READ(読み取り)
セキュリティ設定:NULL(デフォルト)
ファイルの扱い方:OPEN_EXISTING:既存のファイルを開く。指定ファイルが存在しない場合、関数失敗
ファイルの属性とフラグ: FILE_ATTRIBUTE_NORMAL(設定なし)、フラグは省略されてるっぽい。
テンプレート・ファイルのハンドル:NULL(何もない??)

ごちゃごちゃ書いてるが、要はszFilePathで指定したファイルを開いて、それを操作するためのハンドルをhFileに代入しているようだ。

そしてとりあえずここまでの成否を判定。
INVALID_HANDLE_VALUEというのはこのAPIが失敗したときの戻り値。
失敗したらOpen関数はFALSE値を返すということ。

Open関数はまだまだ続く・・・
こっからはヘッダ情報に関して。

コード[C++]: 全て選択
1
2
3
4
5
6
7
 
 
// ヘッダ情報を読む
    DWORD dwReadBytes;
    if (!ReadFile(hFile, &m_fmfHeader, sizeof(FMFHEADER), &dwReadBytes, NULL) ||
        dwReadBytes != sizeof(FMFHEADER))
        goto error_return;



DWORDは型名。32ビット符号なし整数。???^^;
ReadFileはファイルを読み取るAPI。冒頭の!は・・・論理演算子か?

ReadFileの型は以下のとおり。
ReadFile(
HANDLE hPort,
LPVOID lpBuffer,
DWORD NumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);

hPort:CreateFileで得られたハンドル。
lpBuffer:読み取ったデータを受け取るバッファのアドレス
NumberOfBytesToRead:読み取るデータのサイズをバイト単位で指定
lpNumberOfBytesRead:実際に読み取ることができたデータサイズを受け取る変数
lpOverlapped:オーバーラップ構造体と呼ばれる構造体のアドレス

ここでは、
CreateFileで得られたハンドル:hFileに代入されてるやつ。
読み取ったデータを受け取るバッファのアドレス:&m_fmfHeader
読み取るデータのサイズをバイト単位で指定:sizeof(FMFHEADER)

:sizeofは演算子。渡された型や変数のメモリサイズを調べる。

[fmfmap.h]によると、FMFHEADERは構造体のようだ。
以下[fmfmap.h]より
typedef struct tag_FMFHeader
{
DWORD dwIdentifier; // ファイル識別子 'FMF_'
DWORD dwSize; // ヘッダを除いたデータサイズ
DWORD dwWidth; // マップの横幅
DWORD dwHeight; // マップの高さ
BYTE byChipWidth; // マップチップ1つの幅(pixel)
BYTE byChipHeight; // マップチップ1つの高さ(pixel)
BYTE byLayerCount; // レイヤーの数
BYTE byBitCount; // レイヤデータのビットカウント
}FMFHEADER;
つまりFMFHEADERのサイズ分データを読み取る。過不足なしってことか。

実際に読み取ることができたデータサイズを受け取る変数:&dwReadBytes
何のためにあるのかわからんが不要でもNULLにできないらしい。

オーバーラップ構造体と呼ばれる構造体のアドレス:NULL(よくわからんが不要なときはNULLらしい)。

この後に出てくる||はorの意味。!=は両辺が等しくない。
ここのif分は条件式がtrueならgoto error_returnを実行することになっている。
!ReadFile=true→not ReadFile=true→ReadFile=falseということだろう。
つまりReadFileが失敗(読み取り失敗)または
読み取るべきデータのサイズと実際に読み取れたデータのサイズが一致しない場合、error_returnに飛ぶ。

次いってみよう!

コード[C++]: 全て選択
1
2
3
// 識別子のチェック
    if (memcmp(&m_fmfHeader.dwIdentifier, "FMF_", 4) != 0)
        goto error_return;



memcmpはn バイトメモリブロックの比較を行う関数。

int memcmp(const void *buf1, const void *buf2,size_t n);

const void *buf1 : 比較元メモリブロック1
const void *buf2 : 比較元メモリブロック2
size_t n : 比較バイト数

ここでは、&m_fmfHeader.dwIdentifier
(さっきヘッダ情報を読み込んだバッファのファイル識別子が入ってるはずのところ)と、

"FMF_"を4バイト比較している。
"_"まで比較する必要があるのね…

で、戻り値はこう。
正 : buf1 > buf2
0  : buf1 = buf2
負 : buf1 < buf2
ここでは戻り値が0じゃなかったらerror_returnに飛びます。

次~

コード[C++]: 全て選択
1
2
3
4
// メモリ確保
    m_pLayerAddr = new BYTE[m_fmfHeader.dwSize];
    if (m_pLayerAddr == NULL)
        goto error_return;



[fmfmap.h]によると、
// レイヤーデータへのポインタ
BYTE* m_pLayerAddr;
さらにウインドウズにもともとあるっぽい定義から、
typedef unsigned char BYTE;


unsigned:
int型とchar型の変数に対して負の数を扱えないようにするために使う型修飾子。
符号を記憶しておく必要がなくなるため、より大きい正数が扱えるようになるらしいが、その必要性全くわからず。
まあいい、次。

new:
メモリの動的確保を行う演算子。関数ではないってとこがポイントらしいがよく分からん。
ここで、
dwSize; // ヘッダを除いたデータサイズ
ということだったので、

m_pLayerAddr = new BYTE[m_fmfHeader.dwSize];
というのは
m_pLayerAddrに、BYTE型の構造体(要素数はm_fmfHeader.dwSize)を入れるための領域を確保する感じか。
で、もしm_pLayerAddrが空っぽになっちゃったらerror_returnへ。

で、ここまで来てようやくレイヤーデータを読めるらしい・・・

コード[C++]: 全て選択
1
2
3
4
// レイヤーデータを読む
    if (!ReadFile(hFile, m_pLayerAddr, m_fmfHeader.dwSize, &dwReadBytes, NULL) ||
        dwReadBytes != m_fmfHeader.dwSize)
        goto error_return;



また出たなReadFile。
ところで、この流れってデータ読み取りしてから内容をチェックするんじゃなくて、
内容をチェックするためのif文を書くだけで読み取りもしてくれるってことなのか??
まあいい。
ここは、m_pLayerAddr(さっき作ったやつね)に読み取ったデータを入れて、
サイズをチェックして、読み取りがちゃんとできてないようならerror_return。
さっきのはヘッダ部分だけだったけど、今度はレイヤーデータで同じチェックをするらしい。ご苦労なこって。

そしていよいよフィニッシュです。

コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
// 正常終了
    CloseHandle(hFile);
    return TRUE;
 
error_return:
    // エラー終了
    CloseHandle(hFile);
    Close();
    return FALSE;
}



ここは二つまとめて。
CloseHandle:
これもウインドウズにもともとあるっぽい関数で、開いているオブジェクトハンドルを閉じる。
hFileはもう用済みってことか。
ここまで問題なくきた場合にはTRUEが返り、どこかでerror_returnに飛ばされた場合にはFALSEが返る。

ふぅ・・・ここまででひとつの関数だ。
BOOL型のOpenという名前の関数で、真か偽か判定するものです。すっかり忘れry
ここで復習Open関数の流れ。
ファイルを開く

ヘッダ情報を読む

識別子がFMFであることを確かめる

レイヤーデータを入れるメモリを確保

そこにデータを入れる

この関数がうまく行ったかどうか判定

ってことですね。
全部見出しにかいてあるんですが、ここまで調べないと何のことだか分からなかったのです^^;

なんでファイルを開く所だけerror_returnじゃなくて直接FALSEを返してるのかね??

コメント数: 3 閲覧数: 3036

できたー

パーマリンクby きょーちゃん on 2011年2月09日(水) 14:25

マップ表示できました!


画像


・・・すいません嘘です。
ただbmpを表示しただけです。
BitBltで表示できるようにはなったのですが、ほぼサンプルのコピーなので使えるようになったとは言いがたい・・・

というかそもそもDXライブラリでプログラムしていくんだからBitBlt使えてもあんまり意味ないのか?
無駄に時間を使ってしまった気がする・・・

コメント数: 2 閲覧数: 2753

オンラインデータ

登録ユーザー: みけCAT