纹理管理/指针问题
我正在为我的一个小型副项目开发纹理管理和动画解决方案。虽然该项目使用 Allegro 进行渲染和输入,但我的问题主要围绕 C 和内存管理。我想将其发布在这里,以获取对该方法的想法和见解,因为我在指针方面很糟糕。
本质上,我想做的是将所有纹理资源加载到中央管理器(textureManager)中 - 它本质上是包含 ALLEGRO_BITMAP 对象的结构数组。存储在纹理管理器中的纹理大部分是完整的精灵表。
从那里,我有一个动画结构,其中包含特定于动画的信息(以及指向纹理管理器中相应纹理的指针)。
为了给您一个想法,以下是我设置和播放玩家“行走”动画的方法:
createAnimation(&player.animations[0], "media/characters/player/walk.png", player.w, player.h);
playAnimation(&player.animations[0], 10);
渲染当前帧的动画只是位块传输存储在纹理管理器中的精灵表的特定区域的情况。
作为参考,这里是 anim.h 和 anim.c 的代码。由于多种原因,我确信我在这里所做的可能是一种糟糕的方法。我想听听他们的事!我是否会让自己陷入任何陷阱?这会像我希望的那样起作用吗?
anim.h
#ifndef ANIM_H
#define ANIM_H
#define ANIM_MAX_FRAMES 10
#define MAX_TEXTURES 50
struct texture {
bool active;
ALLEGRO_BITMAP *bmp;
};
struct texture textureManager[MAX_TEXTURES];
typedef struct tAnim {
ALLEGRO_BITMAP **sprite;
int w, h;
int curFrame, numFrames, frameCount;
float delay;
} anim;
void setupTextureManager(void);
int addTexture(char *filename);
int createAnimation(anim *a, char *filename, int w, int h);
void playAnimation(anim *a, float delay);
void updateAnimation(anim *a);
#endif
anim.c
void setupTextureManager() {
int i = 0;
for(i = 0; i < MAX_TEXTURES; i++) {
textureManager[i].active = false;
}
}
int addTextureToManager(char *filename) {
int i = 0;
for(i = 0; i < MAX_TEXTURES; i++) {
if(!textureManager[i].active) {
textureManager[i].bmp = al_load_bitmap(filename);
textureManager[i].active = true;
if(!textureManager[i].bmp) {
printf("Error loading texture: %s", filename);
return -1;
}
return i;
}
}
return -1;
}
int createAnimation(anim *a, char *filename, int w, int h) {
int textureId = addTextureToManager(filename);
if(textureId > -1) {
a->sprite = textureManager[textureId].bmp;
a->w = w;
a->h = h;
a->numFrames = al_get_bitmap_width(a->sprite) / w;
printf("Animation loaded with %i frames, given resource id: %i\n", a->numFrames, textureId);
} else {
printf("Texture manager full\n");
return 1;
}
return 0;
}
void playAnimation(anim *a, float delay) {
a->curFrame = 0;
a->frameCount = 0;
a->delay = delay;
}
void updateAnimation(anim *a) {
a->frameCount ++;
if(a->frameCount >= a->delay) {
a->frameCount = 0;
a->curFrame ++;
if(a->curFrame >= a->numFrames) {
a->curFrame = 0;
}
}
}
I'm working on a texture management and animation solution for a small side project of mine. Although the project uses Allegro for rendering and input, my question mostly revolves around C and memory management. I wanted to post it here to get thoughts and insight into the approach, as I'm terrible when it comes to pointers.
Essentially what I'm trying to do is load all of my texture resources into a central manager (textureManager) - which is essentially an array of structs containing ALLEGRO_BITMAP objects. The textures stored within the textureManager are mostly full sprite sheets.
From there, I have an anim(ation) struct, which contains animation-specific information (along with a pointer to the corresponding texture within the textureManager).
To give you an idea, here's how I setup and play the players 'walk' animation:
createAnimation(&player.animations[0], "media/characters/player/walk.png", player.w, player.h);
playAnimation(&player.animations[0], 10);
Rendering the animations current frame is just a case of blitting a specific region of the sprite sheet stored in textureManager.
For reference, here's the code for anim.h and anim.c. I'm sure what I'm doing here is probably a terrible approach for a number of reasons. I'd like to hear about them! Am I opening myself to any pitfalls? Will this work as I'm hoping?
anim.h
#ifndef ANIM_H
#define ANIM_H
#define ANIM_MAX_FRAMES 10
#define MAX_TEXTURES 50
struct texture {
bool active;
ALLEGRO_BITMAP *bmp;
};
struct texture textureManager[MAX_TEXTURES];
typedef struct tAnim {
ALLEGRO_BITMAP **sprite;
int w, h;
int curFrame, numFrames, frameCount;
float delay;
} anim;
void setupTextureManager(void);
int addTexture(char *filename);
int createAnimation(anim *a, char *filename, int w, int h);
void playAnimation(anim *a, float delay);
void updateAnimation(anim *a);
#endif
anim.c
void setupTextureManager() {
int i = 0;
for(i = 0; i < MAX_TEXTURES; i++) {
textureManager[i].active = false;
}
}
int addTextureToManager(char *filename) {
int i = 0;
for(i = 0; i < MAX_TEXTURES; i++) {
if(!textureManager[i].active) {
textureManager[i].bmp = al_load_bitmap(filename);
textureManager[i].active = true;
if(!textureManager[i].bmp) {
printf("Error loading texture: %s", filename);
return -1;
}
return i;
}
}
return -1;
}
int createAnimation(anim *a, char *filename, int w, int h) {
int textureId = addTextureToManager(filename);
if(textureId > -1) {
a->sprite = textureManager[textureId].bmp;
a->w = w;
a->h = h;
a->numFrames = al_get_bitmap_width(a->sprite) / w;
printf("Animation loaded with %i frames, given resource id: %i\n", a->numFrames, textureId);
} else {
printf("Texture manager full\n");
return 1;
}
return 0;
}
void playAnimation(anim *a, float delay) {
a->curFrame = 0;
a->frameCount = 0;
a->delay = delay;
}
void updateAnimation(anim *a) {
a->frameCount ++;
if(a->frameCount >= a->delay) {
a->frameCount = 0;
a->curFrame ++;
if(a->curFrame >= a->numFrames) {
a->curFrame = 0;
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您确定需要一个指向
anim
中ALLEGRO_BITMAP **sprite;
的指针吗?IIRC Allegro 位图句柄已经是指针,因此无需双重引用它们,因为您似乎只想为每个动画存储一个位图。
您应该在
anim
中使用ALLEGRO_BITMAP *sprite;
。我没有看到您的代码有任何其他问题。
Are you sure you need a pointer to pointer for
ALLEGRO_BITMAP **sprite;
inanim
?IIRC Allegro BITMAP-handles are pointers already, so there is no need double-reference them, since you seem to only want to store one Bitmap per animation.
You ought to use
ALLEGRO_BITMAP *sprite;
inanim
.I do not see any other problems with your code.
您可能需要考虑更灵活的动画结构,其中包含帧结构数组。每个帧结构可以包含帧延迟、x/y 热点偏移等。这样,同一动画的不同帧可以具有不同的大小和延迟。但如果您不需要这些功能,那么您所做的就可以了。
我假设您将以固定帧速率运行逻辑(每秒逻辑帧数恒定)?如果是这样,那么延迟参数应该效果很好。
关于您的代码的快速评论:
textureManager[i].active = true;
在检查位图是否已加载之前,您可能不应该将其标记为活动状态。
另请注意,Allegro 4.9/5.0 完全支持 OpenGL 或 D3D 纹理,因此,大位图将无法在某些显卡上加载!如果您生成大型精灵表,这可能会成为问题。从当前版本开始,您必须自己解决这个问题。
您可以执行以下操作:
需要明确的是:这是显卡限制。因此,大型精灵表可能在您的计算机上运行,但无法在另一台计算机上加载。上述方法将精灵表加载到内存位图中(基本上保证成功),然后每帧创建一个新的、更小的硬件加速视频位图。
You may want to consider a more flexible Animation structure that contains an array of Frame structures. Each frame structure could contain the frame delay, an x/y hotspot offset, etc. This way different frames of the same animation could be different sizes and delays. But if you don't need those features, then what you're doing is fine.
I assume you'll be running the logic at a fixed frame rate (constant # of logical frames per second)? If so, then the delay parameters should work out well.
A quick comment regarding your code:
textureManager[i].active = true;
You probably shouldn't mark it as active until after you've checked if the bitmap loaded.
Also note that Allegro 4.9/5.0 is fully backed by OpenGL or D3D textures and, as such, large bitmaps will fail to load on some video cards! This could be a problem if you are generating large sprite sheets. As of the current version, you have to work around it yourself.
You could do something like:
To be clear: this is a video card limitation. So a large sprite sheet may work on your computer but fail to load on another. The above approach loads the sprite sheet into a memory bitmap (essentially guaranteed to succeed) and then creates a new, smaller hardware accelerated video bitmap per frame.