classでキャラを用意し、
class内でLoadGraphを使用する。
ごく普通の光景だが、
同じ敵を複数用意したときは、
どんどんキャラの絵を読み込み、
無駄にメモリを浪費してしまう。
それを避けるための策を
講じてみる。
一度読んだ絵を覚えておく
もう一度読まないようにしたい。
そんな時は、
LoadGraphをするとき
「絵の名前」と
「キャラ番号」を
紐づけて覚えておくんだ。
それを配列に登録しておく。
次にLoadGraphをするとき、
「絵の名前」がその中にあれば、
紐づけた「キャラ番号」を返すだけ。
無ければ普通にLoadGraphをし、
「絵の名前」と「キャラ番号」を
配列に登録して、
その「キャラ番号」を返す。
そのような関数を作るのがいい。
配列は固定だと使いにくいので、
前に習った「std::vector」で
プログラミングするのがいいのかな
プログラミングしようかな。
WindowsのUNICODE対応のため、
文字にTCHARが使用されており、
ちゃんとプログラミングしないと、
SJISとUNICODEが対応できなくなるんだ。
DxLibで使用できるC++ソースファイル
再度読まなくする関数
#include <vector>
/**
* キャラ構造体
*/
struct ImageData
{
TCHAR pathName[ MAX_PATH ] = { '\0' };
int imageNo = -1;
int counter = 0;
};
/// 構造体の配列
std::vector< ImageData > images;
/**
* キャラ読み込み
* @param name : ファイル名
* @return 絵の番号
*/
int LoadGraphEx( const TCHAR* name )
{
for ( auto& img : images )
{
if ( _tcscmp( img.pathName, name ) == 0 )
{
img.counter++;
return img.imageNo;
}
}
ImageData dat;
_tcsncpy_s( dat.pathName, name, MAX_PATH-1 );
dat.imageNo = LoadGraph( name );
dat.counter = 1;
images.push_back( dat );
return dat.imageNo;
}
/**
キャラ使用終了
@param no : 絵の番号
*/
void DeleteGraphEx( int no )
{
if ( no < 0 )
return;
for ( auto itr=images.begin(); itr!=images.end(); ++itr )
{
auto& img = *itr;
if ( img.imageNo == no )
{
if ( --img.counter <= 0 )
{
DeleteGraph( img.imageNo );
images.erase( itr );
break;
}
}
}
}
DeleteGraphEx()を
用意したよ。
今使っているLoadGraph()を
LoadGraphEx()に変更すれば、
同じ絵は読み込まず、
前に読んだ番号と同じものを
返すようにしている。
あと、DeleteGraph()を
DeleteGraphEx()に変更すれば、
使用しない絵はこれで解放できる。
まだ使っている場合は
解放しない。
普段見慣れないものが
ありますね…。
挙げていきます~
わかりにくい箇所のソースコードの説明
for ( auto& img : images )
C++11から使うことができる。
配列名を : の右に書くと、
その前の変数に順に入ってくれる。
autoは自動的に型を推測し
&がついてるので、
imagesの中の変数そのものを指す。
通常関数に渡すのは、値です。 参照渡しというのは、 その値を入れている入れ物を 渡す方法で、入れ物の中には、 別の数を入れて値を変更することができます。 通常の関数は「値渡し」関数int Func( int[…]
if ( _tcscmp( img.pathName, name ) == 0 )
C++のstrcmpを、
SJIS用、UNICODE用に
切り替えてくれる。
文字列シリーズすべて、
「_tcs」を前に
つけるような形になる。
上の文は、
img.pathNameと、
nameが同じかなら
次の文を実行します。
という意味です。
for ( auto itr=images.begin(); itr!=images.end(); ++itr )
{
auto& img = *itr;
その後の
auto itr=images.begin()
は、一体何なのかわかりません。
ポインタみたいなもので、
それを「++itr」で動かして、
image.end()の最後まで進みませます。
最後までいってなければ、
imgの中に、イテレータで
刺している内容を入れます。
要するに次の文と全く動作は変わりません。
for ( int i=0; images.size(); ++i )
{
auto& img = images[i];
この方法だと、
イメージを削除するときは、
itrが無いので、
以下のように行います。
images.erase( images.begin() + i );
可変長配列の仕組みについては、以下もご参考にしてみてください。
固定配列から動的配列へこうすけC++では標準で配列があるけど、 これは固定の長さの配列です。これは次のようにプログラミングするよ。 可変長の配列// 固定長[…]