Jump to content
  • Advertisement

dp304

Member
  • Content count

    7
  • Joined

  • Last visited

Community Reputation

0 Neutral

About dp304

  • Rank
    Newbie

Personal Information

  • Role
    Programmer
  • Interests
    Programming
  1. You may be right, probably I was once again too efficiency-conscious too early in development. Thanks for the feedback from all of you! My idea for loading/managing asset data in memory was basically exactly what @lawnjelly suggested in the first paragraph. Some loader functions would act as factories for the appropriate asset type (doing allocation and filling it with data). My "asset manager/locator" thing would maintain different kinds of assets in one place, so these would be handled internally as char (smart) pointers anyway. In case of plain old C structs, the assets could be deleted via these char pointers without any problem. But since you all recommended against this forced single allocation I had had in mind, in the end I will probably do it the C++ way instead (with a contained std::vector or something similar in the asset, like in the first Texture example). Of course, then the above method won't work: when deleting the char pointer, the destructor of the contained std::vector won't be called - this is what I meant when I wrote about risks of missed destructor calls. What I plan to do is make a base class with nothing else but an empty virtual destructor, and all assets will be subclasses of this. Instead of char pointers, I will maintain pointers of the base class. When the smart pointers (in my case all shared_ptrs pointing to the same asset) are invalidated, delete is automatically called -> due to the virtual destructor, the destructor of the specific asset (e.g. Texture) will be called -> the destructor of std::vector will also be called.
  2. Hi! I reckon that to handle for example some texture asset in a game, the following would be a more or less elegant C++ way (not counting that everything is public here) : struct Texture { int width, height, pixel_size; std::vector<char> pix; }; Then I could create an instance "tex", get the image width, height, and bytes per pixel value from the image file, set the appropriate fields, then tex.pix.resize() accordingly and fill the vector with the pixel data. Anything that processes the pixels (OpenGL for example) could then use &tex.pix[0] as a base pointer. However, after I allocate dynamically for the Texture, the pixel array will need to be allocated separately by std::vector. I would like to avoid this (some reasons are below), instead, I would like the array be part of the struct, which is hard, because my array does not have a fixed size. There is something in C99 called "flexible array members", which allows the following syntax: struct Texture { int width, height, pixel_size; char pix[]; }; but only if the array without given size is at the end of the struct. This is only in C99, not in the C++ standard, but gcc apparently supports it for C++. Another trick is to use an array of 1 element, like this: struct Texture { int width, height, pixel_size; char pix[1]; }; In this case, one could use it like this: // obtain image width, height and bytes per pixel, // and store them in local variables w, h, n char * ptr = new char[sizeof(Texture) - 1 + w*h*n]; // pix array will have w*h*n elements Texture * tex = reinterpret_cast<Texture*>(ptr); tex->width = w; tex->height = h; tex->pixel_size = n; // fill tex->pix[] with pixel data // use the texture delete [] ptr; My first question: is it safe? Some say that it leads to undefined behaviour when a fixed (here: 1) size array is overindexed, because higher optimisation levels could lead to something unwanted. But I've seen an excellent open source C++ game use this technique at one point (allocating with calloc() instead of new char[]), so it should work (most of the time?). Some reasons I desire something like this are: 1. "Data locality" i.e. I would like the metadata be close to the actual data in memory, so that it is not necessary to follow another pointer to a potentially far away location. 2. I would like to keep the asset data structures (such as this Texture example) as plain old C data (without non-trivial destructors or virtual functions etc.), to avoid the overhead of another allocation/deallocation, or the risk of leaks caused by missed destructor calls. I wouldn't really take advantage of the dynamically-expanding std::vector anyway: the size of the asset doesn't change after loading. A simple character array is almost good enough for me: if the asset is a long text, I don't need anything else, but for textures I also need width, height, bytes per pixel, for PCM data I need sample size, number of channels etc. In short, an asset in most part consists of a large byte sequence, but in many cases I would like to smuggle some metadata in front of it. How should I do this? Is there anything more suited than structs with flexible array members? Or should I follow a completely different approach?
  3. Not everything is clear to me: what class hierarchy (if any) you currently have; what programming language you use etc., but I will try to help. If you are talking about one type of item with several instances (I suppose a power plant doesn't create just a single Energy item and/or there may be multiple power plants) then I don't find this at all overkill; in fact this is what I might do. I wouldn't do this, especially if the Item class already has some abstract superclass or virtual functions (I'm assuming C++ or some similar language here). It may be a better idea to have a virtual bool isTradeable() or bool isEnergy() function, and override it in the Energy subclass (what you suggested in point 3) so that it returns false instead of true (or vice versa). If by abstracting out the energy class you mean creating a separate Energy class (or some other entity) independent from the Item class, then imho this can be a sufficiently elegant solution in case you hadn't intended to subclass Item (or in general use some sort of common class hierarchy for both Energy and regular Items) in the first place. (At least right now I can't come up with something better.)
  4. dp304

    Writing subjects in present and past is hard

    "Make" becomes "Maked", "Destroy" becomes "Destroied" -- or did I miss something?
  5. Your answers have been very helpful. Especially the last paragraph with the timing estimates. Probably I won't bother optimising my game loop (at least not the way I described). Not even when working on a computer from '98. Thanks again!
  6. Thanks for the quick answer! I was suspecting something like that, but looking at the trend of reducing the amount of inheritance and virtual function calls in games (e.g. by using Entity-Component-System frameworks instead), I became unsure; although probably the trend is due to those tens of thousands of virtual calls (involving game objects). (But if I understand correctly, there used to be a time when even a couple of virtual calls per frame was a concern that needed to be optimised - there were games developed in C++ as early as 1998, weren't there? If I ever target a very low-end platform, on the level of a Pentium-II - which I probably won't - should I refactor virtual calls? Or in that case should I forget about C++ altogether?)
  7. Hello! As far as I understand, the traditional approach to the architecture of a game with different states or "screens" (such as a menu screen, a screen where you fly your ship in space, another screen where you walk around on the surface of a planet etc.) is to make some sort of FSM with virtual update/render methods in the state classes, which in turn are called in the game loop; something similar to this: struct State { virtual void update()=0; virtual void render()=0; virtual ~State() {} }; struct MenuState:State { void update() override { /*...*/ } void render() override { /*...*/ } }; struct FreeSpaceState:State { void update() override { /*...*/ } void render() override { /*...*/ } }; struct PlanetSurfaceState:State { void update() override { /*...*/ } void render() override { /*...*/ } }; MenuState menu; FreeSpaceState freespace; PlanetSurfaceState planet; State * states[] = {&menu, &freespace, &planet}; int currentState = 0; void loop() { while (!exiting) { /* Handle input, time etc. here */ states[currentState]->update(); states[currentState]->render(); } } int main() { loop(); } My problem here is that if the state changes only rarely, like every couple of minutes, then the very same update/render method will be called several times for that time period, about 100 times per second in case of a 100FPS game. This seems a bit to make dynamic dispatch, which has some performance penalty, pointless. Of course, one may argue that a couple hundred virtual function calls per second is nothing for even a not so modern computer, and especially nothing compared to the complexity of the render/update function in a real life scenario. But I am not quite sure. Anyway, I might have become a bit too paranoid about virtual functions, so I wanted to somehow "move out" the virtual function calls from the game loop, so that the only time a virtual function is called is when the game enters a new state. This is what I had in mind: template<class TState> void loop(TState * state) { while (!exiting && !stateChanged) { /* Handle input, time etc. here */ state->update(); state->render(); } } struct State { /* No update or render function declared here! */ virtual void run()=0; virtual ~State() {} }; struct MenuState:State { void update() { /*...*/ } void render() { /*...*/ } void run() override { loop<MenuState>(this); } }; struct FreeSpaceState:State { void update() { /*...*/ } void render() { /*...*/ } void run() override { loop<FreeSpaceState>(this); } }; struct PlanetSurfaceState:State { void update() { /*...*/ } void render() { /*...*/ } void run() override { loop<PlanetSurfaceState>(this); } }; MenuState menu; FreeSpaceState freespace; PlanetSurfaceState planet; State * states[] = {&menu, &freespace, &planet}; void run() { while (!exiting) { stateChanged = false; states[currentState]->run(); /* Runs until next state change */ } } int main() { run(); } The game loop is basically the same as the one before, except that it now exits in case of a state change as well, and the containing loop() function has become a function template. Instead of loop() being called directly by main(), it is now called by the run() method of the concrete state subclasses, each instantiating the function template with the appropriate type. The loop runs until the state changes, in which case the run() method shall be called again for the new state. This is the task of the global run() function, called by main(). There are two negative consequences. First, it has become slightly more complicated and harder to maintain than the one above; but only SLIGHTLY, as far as I can tell based on this simple example. Second, code for the game loop will be duplicated for each concrete state; but it should not be a big problem as a game loop in a real game should not be much more complicated than in this example. My question: Is this a good idea at all? Does anybody else do anything like this, either in a scenario like this, or for completely different purposes? Any feedback is appreciated!
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!