配列の宣言について。
お久しぶりです。
今回は配列の宣言について。
例によって例のごとくノベルゲームの(ryを組んでた際にエラーが発生
◯◯◯.exe の 0x00ac4bc4 でハンドルされていない例外が発生しました: 0xC0000005: 場所 0xdadad240 に書き込み中にアクセス違反が発生しました。
エラー発生時に実行されていた関数がこちら
void setScript(Script* pScript, const char* str) {
int i, start = 1, size;
const char* pStr;
for (i = 0; i < 3; ++i){
// ","を探す
pStr = strstr(&str[start], ",");
if (pStr == NULL ) // ,がない場合、行末(改行)を探す
pStr = strstr(&str[start], "\n");
size = pStr - &str[start];
char Buf[ ] = {0};
switch(i){
case 0:
strncpy(Buf, &str[start-1], size+1);
Buf[size+1] = '\0';
pScript->Name = Buf;
break;
case 1:
strncpy(Buf, &str[start-1], size+1); // ここでエラー発生
Buf[size+1] = '\0';
pScript->Message = Buf;
break;
case 2:
// キーワードに合わせて読み込む画像を変える
strncpy(Buf, &str[start-1], size+1);
Buf[size+1] = '\0';
if(strcmp(Buf, "A"))
pScript->bgGraph = LoadGraph(FILENAME_A);
else if(strcmp(Buf, "B"))
pScript->bgGraph = LoadGraph(FILENAME_B);
else if(strcmp(Buf, "C"))
pScript->bgGraph = LoadGraph(FILENAME_C);
else if(strcmp(Buf, "D"))
pScript->bgGraph = LoadGraph(FILENAME_D);
break;
default:
break;
}
start = start + size + 2;
}
}
pScriptは以下の通り
struct Script{
char* Name,Message;
int bgGraph;
}script;
Script *pScript = &script;
FILENAME_◯は画像ファイルのパス(文字列)で定義済。
switch文のcase 1:とcase 2:の違いなんて代入先だけじゃないか!!と思いつつ色々調べてみる。
すると、問題があったのは......
\ switch文の前のBufの宣言!! /
char Buf[ ] = {0};
要素数の動的確保、こいつが穴だった。
要素数を指定しなかった場合、配列の利用時に要素数が確定される。
→case 1:で確定される。== case 2:以降では既に確定している。
文字列の長さがNameに代入したい値よりも、Messageに代入したい値の方が長かった場合、配列の最終要素(書き込み禁止!!)にはみ出た分の最初の文字を代入しようとし、エラーを吐いた、ということだったようだ。
動的確保は便利だけど長さが未定で何度も使い回す場合には最初っから多めに確保したほうが良さそう。
動的に再定義とかできたら無駄がなくていいんだろうけど、そこまでの知識は俺には無いからなぁ......(´-ω-`)
これもまた調べたいことに追加して今回も終了、かな。
PS.冬コミまで日が無くなってきて焦ってます。ダレカタスケテー
2014/11/22 Sat.
【C++】ノベルゲームのスクリプト - その2.5
前回に引き続きスクリプトについて。
といっても今回はスクリプトそのものではなくその扱い方についてなんだが。
前回の記事を振り返って、無駄に気付いた。
構造体の配列を利用するということは、それだけメモリを必要としてしまうため単純計算でいくと原稿が2倍になれば必要なメモリも2倍になってしまう。
例えばDVD-ROM3枚組の某型月さんのFate/stay night。
もしその原稿を前回のに読み込ませようとした場合どれだけのメモリが必要になることやら......
というわけで
配列に詰めるのはやめよう!!
では、どうするか。
今回も検証してみないことにはわからないけども案として、
構造体を1つ生成して一回の発言分をメンバ変数にセット
描画
これでループすればいいのでは無いだろうか。
また、以前にも書いたがDxLibの利用を前提に考えているのだが、
LoadGraph()関数で画像をメモリに読み込むとDeleteGraph()関数で削除するまでメモリに残留してしまうらしい。
【C/C++】画像描画について【DxLib】 • C言語交流フォーラム ~ mixC++ ~
そこでメッセージを描画し終わったら構造体を破棄し、デストラクタでDeleteGraph関数を呼んであげればメモリ圧迫はしなくて済むのでは無いだろうか。
ただし、この手法では描画ループ内で毎回
・構造体の生成
・構造体に代入(画像や音声ロード含む)
・構造体を破棄
を行うので処理速度が落ちる。
この処理速度の落ち具合によっては構造体配列を要素数を決めて生成して、ある程度まとめて行ったほうが良いかもしれない。
いずれにしても実際に走らせてみないことにはなんとも言えないねw
時間ができれば実装して検証しようと思います。
もうひとつこれに関して気になることがあるけど、それは下のリンクで質問中
【C++/DxLib】LoadGraph()関数について|teratail
色んなとこで質問してる無知っぷりを晒しつつ、今回はこの辺で。
2014/11/15 Sat.
【C++】ノベルゲームのスクリプト - その2
前回に引き続きスクリプトについて考えてみる。
前回はデータを受け取る構造体だけを考えたので、今回はそれにデータを受け渡す方法を考えようと思う。(いちいち構造体のデータを記述するのは面倒なため)
まず初めに思いついたのはExcelファイルを読み込むこと。
横で要素数、縦でデータ内容を統一すれば管理が楽だからだ。
問題は、Microsoftさんのライセンス品であるExcelのデータを読み込むのは骨が折れそうなのと、完成後に一般公開しようと思っているため利用するときに何かの有料アプリケーションが必須という事態は避けたいというという方針に反すること。
そこでまた色々調べてみるとCSVファイルなるものがあるらしい。
曰く、Excelで言うところの横列が行・縦列はその行の中で「,」で区切って表現されるらしい。
これを利用すればデータの入力は楽になるのではないだろうか。
前回のコードを再掲
struct Script{
char* Name;
char* Message;
int BGIHandle;
int PersonHandle;
int BGMHandle;
};
Script *p = new Script;
メンバ変数は5つ。
// CSVファイルから読み込んだデータをScript構造体に設定
void SetScript(struct Script* pScript, const char* Str) {
int i = 0, Start = 1, Size;
char* pStr,pResource;
char Buff[100];
for (i = 0; i < 5; ++i) {
// ","を探す
pStr = strstr(&Str[start], "\",\"");
if (pStr == NULL )
// ","がない場合、最後の"を探す
pStr = strstr(&str[start], "\"\n");
size = pStr - &str[start];
switch (i) {
case 0:
// 発言者
strncpy(pScript->Name, &str[start], size);
break;
case 1:
// メッセージ
strncpy(pScript->Message, &str[start], size);
break;
case 2:
// 背景画像
strncpy(pResource, &str[start], size);
pScript->BGIHandle = LoadGraph(pResource);
break;
case 3:
// キャラ画像
strncpy(pResource, &str[start], size);
pScript->PersonHandle = LoadGraph(pResource);
break;
case 4:
// BGM
strncpy(pResource, &str[start], size);
pScript->BGMHandle = LoadSoundMem(pResource);
break;
default :
break;
}
start = start + size + 3;
}
}
という関数を組んで、引数に構造体配列の要素とCSVファイルを1行づつ渡して、EOFまでループしてあげればいいはず。
ファイル読み込みにはfgets()関数を使えばいいかな。
fgets()に渡すバッファサイズとかは適宜調整してくださいな。
これでCSVファイルで書かれた原稿を渡すだけでScript構造体が使えるはず。(検証自体は未済、あくまでもアイデアのメモですので。)
毎回CSVの原稿を読み込むのは面倒だし、構造体のデータを書き出せるようにして、書きだしたデータをエンジン側で読み込めればいいのかなぁ。
次回はその構造体のファイル書き出しによるデータ保持について纏めようと思う。
......ということは、これはスクリプト生成用のプログラムであってエンジンそのものには搭載されない気ががががが
まぁ、一緒にパッケージングして配布とかになるかなぁ、(俺主観でだけど)需要はありそうだし。
2014/11/14 Fri.
以下、参考サイト様
【C++】ノベルゲームのスクリプト - その1
現在ノベルゲームのエンジンを作成中。
それにあたっていちいちソースコード側でメッセージや画像を切り替えるのは汎用性に欠けるからゲーム本体に実装するための外部スクリプトを作れたらいいなぁ、と思い実装方法を考えてみた。
まず、スクリプトに必要なデータ。
・発言者
・表示メッセージ
・背景絵(のパス)
・キャラ絵(のパス)
・BGM(のパス)
・効果音(のパス)
まぁ、こんなところかな。
実際には効果音不要の場合があったり、キャラ絵にも表示位置などがあったりするからもう少し複雑になるけど、今回は構想を纏めるだけなので割愛しよう。
ふむ。
必要なデータはどのシーンでも共通だ。
となるとスクリプト構造体を定義し、その配列で管理するのがベター。
要素数は原稿の長さに依存するのでnewを利用して動的にメモリを確保する。
struct Script{
char* Name;
char* Message;
int BGIHandle;
int PersonHandle;
int BGMHandle;
};
Script *p = new Script;
こんな感じにして受け手を確保してあげればいいんじゃないかなぁ......
これでメインフレームでの描画ループのカウンタを、ロードする構造体の要素数に当てていけば運用しやすくなる(ハズ)
ただ、このままだと各要素の代入は手打ちになるから外部で作成した意義があるとは言えそうにない。
次回はこの構造体に外部ファイルからデータを読み込む方法を考えようと思う。
2014/11/13 Thu.
環境パス設定。
本日の部活であった出来事。
開発環境
・OS:Windows8(64bit)
・キーボード:JIS配列
・コンパイラ:Borland C++ Compiler 5.5
・エディタ:Tera Pad
部員T「C++でグラフィック処理したいんだけど、どうすれば出来る?」
俺「***.rcファイルで管理してwindows.h使うか、DxLib.h使うかかな。弄ってみた感じは後者」の方が簡単かな」
〜数分後〜
T「大体サイトと同じようにやったはずなんだけど何かエラー出とる......」
PC交代して作業スタート
まずソースコード
#include <stdio.h>
#include "DxLib.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
if(DxLib_Init() == -1)
return -1;
else
MessageBox( NULL, "DxLib初期化完了", "Debug", MB_OK);
return 0;
}
ソース自体は改変してあるものの
・初期化に失敗したら強制終了
・初期化に成功したらメッセージボックスを表示して終了
といった仕様で特に問題はない。
続いてコンパイル
GUIアプリケーションだから -W
とりあえず警告は一旦無視するため -w-
実行。
bcc32 -W -w- dx_sample.cpp↵
Borland C++ 5.5.1 for Win32 Copyright (c) 1993, 2000 Borland
Turbo Incremental Link 5.00 Copyright (c) 1997, 2000 Borland
エラー E2209 I:\pcclub\T\prog\dx_sample.cpp 2: インクルードファイル 'DxLib.h' をオープンできない
エラー E2141 I:\pcclub\T\prog\dx_sample.cpp 4: 宣言の構文エラー
*** 2 errors in Compile ***
** error 1 ** deleting Debug\dx_sample.obj
Make End !!
なるほど。
どうやらヘッダファイルのインクルードに失敗している模様。
bcc32.cfgを確認すると
-I"I:\pcclub\T\borland\bcc\bin\Include"
-L"I:\pcclub\T\borland\bcc\bin\Lib; I:\pcclub\T\borland\bcc\bin\Lib\PSDK"
一見、問題が有るようには見えない。
そこで環境パス設定について調べてみた。
すると、
「""で囲んであればフォルダ名に空白を含んでも問題無い。」
これが実は問題だったのだ。
どうやら2行目の後半が
「I」ドライブではなく、
「 I」ドライブ(Iの前に空白有り)として読み込まれていたようだ。
というわけでbcc32.cfgを
-I"I:\pcclub\T\borland\bcc\bin\Include"
-L"I:\pcclub\T\borland\bcc\bin\Lib; I:\pcclub\T\borland\bcc\bin\Lib\PSDK"
から
-I"I:\pcclub\T\borland\bcc\bin\Include"
-L"I:\pcclub\T\borland\bcc\bin\Lib;I:\pcclub\T\borland\bcc\bin\Lib\PSDK"
に書き換えて再コンパイルしたところ無事解決。
基本的に俺はMacで開発しているが、覚えておいたほうが(というよりはいつでも思い出せるようにしておいた方が)良いかなぁ、と思い投稿した。
2014/11/11 Tue.
はてなブログ開設。
初めまして。
旧:ふくとも、と申します。
このたびはプログラマ(見習い)仲間がブログを書いているのを知り、私も自分の備忘録的に書いたものを公開してみようかと思い、本ブログを開設しました。
初投稿ということで軽く自己紹介でもしておきますね。
1997/5/2生まれなので、2014年現在は高2です。
趣味(同人活動含む)でプログラミングをしていて、メインで使ってるのはC/C++です。
その他は申し訳程度にHTML/CSS/PHPも使えないことはない、といった感じです。
現在は情報オリンピック予選に向けてのC/C++の勉強と冬コミに出展するノベルゲームのエンジン開発が主です。
Twitterでも割と活発に活動しております。
敬語は苦手なので次回からは常態、一人称は普段同様「俺」で綴らせて頂くことをお許し下さい。
では今回はこの辺りで。
2014/11/10 Mon.