Jump to content
  • Advertisement

EWClay

Member
  • Content Count

    231
  • Joined

  • Last visited

Everything posted by EWClay

  1. Yes. You might for example use a physics engine such as Box 2D. That doesn't stop you having a physics component to handle the connection between physics and game logic, it would just not be responsible for actually updating the physics and would not need to store the physics data.
  2. 1. Split the components even more. For AI I have a pathfinding component, a chase component, an attack component and so on. I don't have a problem with very specific components for certain tasks either, such as a sliding door component. You may be surprised at how easily a complex system can be broken up and how much simpler the design becomes when you do that. 2. Very low level systems such as physics and rendering that need to know about changes immediately should be managing their own data directly (and the components only carry an ID). For the rest I just iterate through the entity list. 3. Yes, share the data outside the component system and make the components distinct. 4. I would not use inheritance within components at all, except perhaps to derive from an abstract base component. The example of a vertex buffer is a poor one also as rendering APIs do not use inheritance in that way. 5. It doesn't matter much whether you use IDs or pointers. For me, pointers are slightly more accessible because you dont need to go back to the system to get at the data. I would use smart pointers at least to avoid the risk of memory leaks or dangling pointers.
  3. EWClay

    OOP Theory

    Then in terms of OOP theory, the design pattern you want is this: http://en.wikipedia.org/wiki/Template_method_pattern SDL_Flip is needed for every game, so factor it out and don't have the (derived) game state class even be aware of it.
  4. Normally you would classify a point such as the camera position as in front of or behind a plane using the plane equation, which will give a distance which is either positive or negative. This is independent of the coordinate system so you need build the BSP tree only once. Don't worry about the efficiency of the test; it is just a dot product and very quick compared to the cost of actually traversing the data.
  5. I've considered using inheritance within components, but always backed off from it, choosing to either use multiple components or to make the distinction in a data-driven way. This is partly because it would require a more complex system to find components. As it is, I can look up components on an entity by type. But with inheritance I would have to check every component to see whether it might derive from the one I was looking for. Then there's the possibility of multiple components of the same base type - which one did I want? This type of confusion is exactly why I went for components over inheritance in the first place.
  6. EWClay

    OOP Theory

    In terms of the basic rendering, I would just have a function to set the screen as a render target, and then functions to render to the current screen. There isn't much reason to link these things together. What do you mean by updating the screen? It's a good idea to have a framework that allows you to plug different sections of the game such as menu screens into the main update/render loop. In this case the call to update would be coming from the application, not the user code.
  7. EWClay

    Archiving C++ objects to JSON

    Last time I talked about serialization of C++ objects to and from an abstract archive format, using a templated interface similar to Boost Serialization. This time I want to get a little more specific and explain how objects can be converted to JSON (http://www.json.org/). It's quite instructive to look at JSON as a format because it maps very well the the way that objects are represented in computer code. Other formats such as XML can do the same job, but in my opinion not in such an elegant and minimal way. JSON has objects (made of key/value pairs), and a few basic types. That's it. Can that possibly be enough? Actually, yes. C++ code has many more ways of representing data, but in terms of actual content you don't need more than JSON provides. To write and parse JSON I wrote my own code, which was not very difficult. But there are many libraries available, such as SimpleJSON. I'll link all my code, including the serialization, archive and parser in a future update. Basic Types JSON has numbers, strings, a boolean type and a null value. These map to C++ types quite easily. All numerical values - integers, floats and doubles become numbers. I treat std::string as a basic type and bool is written as true or false. Objects and Classes JSON doesn't use classes; its objects are self-describing. When writing a C++ object as a JSON object, the keys come from the member names in the class, and the values come from the data in the object. This allows objects to change over time by adding or removing keys. An extra member in the class could be loaded with a default value, or a missing member ignored. What this doesn't allow for is a change of structure, which would have to be dealt with by creating a new object, but for the most part versioning is not necessary with JSON. Arrays and Containers Every C++ container is a sequence of values, so I simply map every C++ container type to a JSON array, whether a vector, list or anything else. I use templated functions to read and write the different container types from the standard library because I prefer to use those to raw arrays. Enums As part of keeping the format readable, I don't want to be writing enums as numerical values. Besides, the value might change in the code and that would make the data invalid (which is more likely than the name changing). So enums are written out as strings, and converted back from a table when loaded. I use some macros to make this neater. Inheritance JSON does not support inheritance. This doesn't matter. In terms of data, a base class is just like another member. I write it out using the name of the base class as the key. Pointers and Polymorphism This is the point where something is really missing, because JSON does not have pointer or reference types. And there's no way I can get away without it because I use polymorphic types in my data, and I need to be able to archive them. In fact, I only use smart pointers so I'm not concerned about how to represent references generically in JSON, but only how to represent a specific type of data object, which is something it is well able to cope with. To begin with I need a way to uniquely identify a type, and I get this from a string identifier which is unique to the class. I add a virtual function (using a macro) to each type that I want to serialize polymorphically. The identifier also has to be registered with the serialization system so that a handler object can be created. The handler is going to instantiate the template functions to read and write the object. (That actually amounts to two lines of extra code per polymorphic type, so nothing major in terms of interface). These polymorphic types can only be written out using smart pointers, which have a templated serialization function. What that does is write out a JSON object containing a unique identifier for the object, (which is the memory address) and the unique identifier for the type (a string) followed by the object itself (but only once per archive in the case of a shared pointer). When reading the object back I know first of all whether it needs to be created (because there may have been a previous reference to it), then I can look up the handler from the type, and then create and read the object. There's a fair amount of factories and templates behind the scenes to make this work. Next steps Does data need to begin its existence in C++? What about creating types outside of the source code, building objects using those definitions, and still being able to access those inside the program. That will be my subject next time.
  8. EWClay

    Where does game play come in?

    You can't treat game logic as totally abstract. Depending on the type of game, you might need a level editor in which you can create arbitrary objects such as trees and squirrels in a totally data driven way, and link them through mechanisms as described in the previous post. Or you might be looking for a scripting system. Is it a very specific case that a squirrel appears? Will there be many different behaviours like that in the game? Writing tree and squirrel classes might make sense if these are very common concepts throughout the game and you always want them to behave the same way. Still, you would want to minimise the responsibility of these classes and make them work as much as possible using other, more generic systems.
  9. EWClay

    OOP Newb: Am I doing this right?

    There's no reason for all those statics in Program. It can just be a class with normal members like any other.
  10. EWClay

    Game data and serialization

    Getting data into a game is a big subject and I want to talk about some of the systems I'm using in some detail, so it may take a few posts to get through it. I will narrow the subject a little first though: this is not going to be about asset loading - textures, meshes, sounds and so on. Rather it's the data that makes up the game itself, so that would be levels, entities, and all the associated information. Since in the game these are C++ objects, there needs to be a mechanism for interacting with such objects in order to save and load them. So the first step is serialization, which is simply the process of translating an object into a stream of data and vice versa. Obviously this is a well studied problem, but still a subtle one. C++ does not provide serialization out of the box. One of the best known solutions is the Boost Serialization library (http://www.boost.org/doc/libs/1_53_0/libs/serialization/doc/index.html). It's not entirely suitable for my purposes, for several reasons which I hope will become clear later, but it does offer some very useful concepts. First among these is the separation of the process of serialization from the archive format. This is one of the fundamental goals of Boost Serialization; whether it achieves it is another matter. It also puts some care into the serialization interface so that it is fairly simple (at least, for simple classes) and economical in terms of the amount of code and maintenance required. So, points that I like about Boost Serialization (and naturally intend to steal): Templated serialization functions. Virtual base classes for archives is the alternative, but this seems to be the best implementation for flexibility and efficiency. Non-intrusive code, as far as possible. Serialization has a minimal impact on the original class. Automatic serialization of containers and other common types. And what I don't like: The versioning system. Versioning is explicit in Boost Serialization and I prefer it to be automatic, as in, I can add or remove members without having to write special code to do that. Automatic object and pointer tracking. This complicates the archive format and the serialization interface. In contrast to versioning, I prefer this to be explicit. Some aspects of the interface, including operator overloading. Taking all that into account, here's a small example of a serialization function using my system:struct Rect { int16 x, y; uint16 w, h;};template void Serialize(Archive& ar, Rect& t){ ar.Serialize("X", t.x); ar.Serialize("Y", t.y); ar.Serialize("W", t.w); ar.Serialize("H", t.h);}As you can see it is templated to receive an archive and a serializable type. The function simply runs through the members and calls Serialize on the archive, providing a unique name (within this type) for each member. This function will do for loading and saving. The Serialize function on the archive is a template too and will detect the object type to serialize it correctly (and recursively). And the output, when I write to a JSON archive:{ "Rect": { "X": 32, "Y": 0, "W": 32, "H": 32 }}Now, that doesn't have to be a JSON archive. I'm using that for now because it's portable, easy to parse and human readable. But I could swich over to a binary format without changing the serialization function at all. Next time I'll go through some more details of the implementation.
  11. EWClay

    Mesh Format Confusion

    There are many formats written out by modelling packages, and these are well documented with loaders available. Typically, because artists may make use of several of these on a game, they will be loaded to an intermediate format for processing. Granny 3D is one popular choice for that step, but very much not free. After processing the mesh is written to another format, and there will be loaders for each platform the engine runs on, which may include Direct X. But that format will be very simple compared to the original, optimised for fast loading and designed to go straight to the GPU. Because every engine works differently, there is no common format for this final step.
  12. EWClay

    Is my frustum culling slow ?

    That code is very branchy. Culling an AABB is literally two dot products and a couple of comparisons per plane. See here: http://blog.makingartstudios.com/?p=155 Optimise later with SIMD if you need to, but more important is the data layout. Keep bounds data in a vector separate from everything else. Cull before rendering anything, writing out a list of objects to render. Once you get above 10000 or so, consider some sort or hierarchy.
  13. I deal with a similar case by creating temporary nodes at the start and end positions, linking to all the surrounding nodes, then path-finding as normal. The direction of the goal isn't necessarily the way you need to travel if an obstacle is in the way.
  14. EWClay

    Command queues

    Squad movement is looking ok now. Given solid pathfinding code and physics, it was already working reasonably well in that units could simply push other units out of the way to get where they wanted to go. It just didn't look that good. I chose a simple solution; as my squads are quite small I don't need anything too sophisticated. First I predict collisions using a circle intersection test. I only care about the next predicted collision. The unit's path is deflected either left or right depending on which requires the least deflection while still leaving room to pass. Once the collision is no longer predicted, the unit returns to the original path. It's nice because it's sort of self-correcting, and doesn't involve modifying the path or the path-following code. With that working I could move on to commands. I currently have three: move, follow and attack. Because I'm using a component system, each command is a component. Follow and attack both make use of move. It's possible to use these components directly, but I wanted to have a queuing system for setting up waypoints and general automation. How to handle that? Another component of course! The command queue sends messages to the other components to begin and end actions, they carry out the action and respond with success or failure messages. For the commands in the queue I use a Command base class with virtual functions. This makes it easy to plug in new commands. But most of the work is done in the components. This could get interesting if I start mixing it with AI. How much intelligence should an individual unit have? I want some of them to not even have the pathfinding component, so they could only follow or attack within line of sight. My attack command is very basic at the moment. It could take into account cover, different weapon types and the current health of the unit. Next, though, I think I really need to make a proper level. It's just been small test areas so far and they don't have enough scope to try out these ideas properly. So I shall put on another hat and become a level designer for a bit.
  15. EWClay

    Command queues

    No plans at the moment to add formations. I think I would use a shared path with an offset for each unit. It would mean adding clearance calculations to the pathfinder.
  16. EWClay

    xnamath byte alignment

    You can use local variables, or you can make sure that Cube is always 16 byte aligned by using a custom allocator.   If you can't guarantee the alignment, the safest way would be to use XMFLOAT4X4 in the class and explicitly load and store to XMMATRIX when needed.
  17. You already have it sorted by depth after the first loop. So why run through again in the original order, performing a linear search through the depth-sorted list, only to sort again by depth at the end? It should be possible to do it in a single pass plus a sort.
  18. Disregarding all the issues others have mentioned about the way you measure time, doesn't that result say that the inlined f2 executes faster then f1? The less times time difference is > 0, the faster the loop runs... That would be a good point if f2 were the inlined one.
  19. Actually you can't. Even __forceinline is just a strong suggestion. From the MSDN documentation on __forceinline: It's a very strong suggestion. It overrides the compiler's own analysis and it will do it unless it's impossible to inline the function, for example if it is recursive or virtual. Because it is such a strong suggestion __forceinline should be used with caution and after profiling. Unlike inline itself which means nothing but 'I put this function in a header'. For this test you may very well need __forceinline and possibly __declspec(noinline) too. Check in the debugger that it's doing what you expect. The method of timing looks unreliable. It's most likely random whether a tick occurs between those two points. Try timing the whole loop instead.
  20. If that's true, I must be nearly finished!
  21. I used to use scene graphs but I've had to abandon the whole concept as a bad idea. The gain in speed when you instead design data structures around the operations that need to happen is immense. It also leads to fewer dependencies and simpler code. If the physics engine is handling relative transformations, why have transform nodes at all? You're just duplicating information and making it twice as complex as it needs to be. You can represent rendering as a tree, but why? The process of rendering is the equivalent of flattening the tree. Why not flatten it in advance and save all that work? Scene graphs have their place in tools, but they shouldn't make it to the run-time.
  22. EWClay

    Collision resolution algorithm

    My favourite paper on this subject: http://www.cs.columbia.edu/cg/rosi/ It explains what is required from a solution, how various methods fall short, and how to put together a method that works.
  23. EWClay

    Another SDL Error

    When you free optimizedImage, you free the surface that img_surface points to, because they are the same at that point. After that, you are using an surface that has been freed, which is an error.
  24. I can't keep saying this or I'll bore everyone, but you haven't got the logic right. The only value you can return from inside the loop is OUTSIDE, when a plane rejects the box. If any plane intersected the box return INTERSECT after checking all the planes. Otherwise return INSIDE. You seem to expect all planes to return outside if the box is outside the frustum. That's not right. It only takes one to reject it.
  25. You just need to think through the logic of this. If the box is fully outside any plane, no further planes need to be tested and you can return OUTSIDE immediately. Any plane intersecting means the box is not fully inside, but you can't return INTERSECT until all the planes have been tested because one of them might still cull the box.
  • Advertisement
×

Important Information

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

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!