• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.

Servant of the Lord

Members
  • Content count

    7229
  • Joined

  • Last visited

Community Reputation

33705 Excellent

About Servant of the Lord

  • Rank
    2nd Place - The Week of Awesome 2014

Personal Information

  1. That's certainly one way (and indeed the way I'm implementing my current game).   There are other ways that are "better" in certain circumstances, but slightly more complicated - like ECS ('Entity Component Systems'). However, I'd stick with a base-class 'Abstract Entity' until you complete at least one decent-sized game. I'd not use ECS on smaller games, anyway.   Also, don't force everything into the base class - some things would just be better separated as something else. Walls, for example, may be better off separated in their own class. Depends on the game and the rest of the project's architecture, though. Just pay attention to how much you have to force-fit one class like a 'wall' into the virtual interface of something like 'Thing', and also watch to make sure Thing's virtual interface isn't getting bloated - recognizable if it gains too many virtual functions, especially if most derived classes don't implement most of those virtual functions.
  2.   where is it written that components must be data?    what about an AI component?     I write that components should be data.  :wink:   If Components are data (generally), and Systems execute code, it's far easier to parallelize, as well as generally makes the code cleaner because it's clear what part of the architecture is responsible for what.   The key is to realize that logic can be parameterized and transformed by data, and that code itself can be data (for example, callbacks and scripts (which are just runtime callbacks)). But by thinking of Systems as Logic and Components as Data (i.e. input and parameters to the logic), I think it helps you keep your architecture organized and lets you know what is responsible for what, helping you recognize what parts of your code are actually sharable (and optimizable). It's obvious that code transforms data, but don't forget the subtler reality that data can also transform code.   Obviously this depends on what style of ECS you are using, but I think it's a good guideline; "Hold to it until you absolutely need otherwise (and until you triple-check your need)" kind of thing.
  3.   In one ECS design that I really like from research, but haven't had a chance to personally explore in code, Components are *only* data, Systems operate on the data, and Entities are just an ID.   Each System make use of containers of components, and map the EntityID to components when needed.   When starting off, here's a few things that people widely do that I personally think are bad design:  - Don't make Components inherit from a base component (even if it's CRTP).  - Ditto for Systems. Don't make Systems inherit from a base system (even CRTP).   My rational for this is stated in another thread - others disagree. I'd start off NOT making them inherit at all, because I think it messes up the entire way your interfaces will be designed; but if you later find you *need* it, then later you can rewrite the interfaces easily enough (easier than going the other way).   Additionally:  - Don't limit Systems to one component type. A System might want more than one type of component. Don't assume a one-to-one mapping of system and component types.  - Many Systems might need access to the same components. Don't assume only one System can ever use the same container of components.  - Some component containers are public (used by multiple systems), others are private (a single System uses them internally for bookkeeping).  - Don't assume every type of component needs to be stored in the same way. Some types of component are better in arrays, others in maps.  - If a component contains a single variable, something's likely wrong and you might be breaking your components up into too fine a granularity. However, if your game genuinely needs it (again, I think it's a code-smell), then you may want to make a Property system for your ECS.     For Components in arrays, this is done for speed in many cases - in those cases, you should double-indirect to map your EntityID to the component: //Conceptually: std::unordered_map<EntityID, IndexOfComponentInArray> componentIndexMap; std::vector<Component> components; myEntityID = 357; auto iterator = componentIndexMap.find(myEntityID); if(iterator == componentIndexMap.end()) {     //...entity doesn't have this kind of component!     //This indicates a problem in your run-time *data* (your scripts, or whatever), and should be hidden gracefully (and reported to the log). } else {     IndexOfComponentInArray index = *iterator;     if(index >= components.size())     {          //Error! Out of bounds index. This indicates a flaw in your *code*.          //If performance is genuinely required and proven (for example, if this is running on a server), this check can be disabled in release mode (but by default, shouldn't be).     }          Component &myComponent = components[index]; } (I'd wrap this type of container in it's own class)   This only needs to be done when looking up a component using an EntityID, which is much rarer than just iterating over entities. (and again, this is only needed for some types of components where performance is measured to be necessary). Two benefits of this is that you save space for the many entities that *don't* use the components (you don't have empty unused elements in the container), and also that the System can re-arrange the components for optimization without invalidating the EntityIDs trying to access the components.   I'd also suggest one other thing: I personally believe making general-purpose engines are a waste of time :P, unless you are selling the engine itself commercially. I prefer engines that are designed for a specific genre of games and with a specific game in mind, that can be easily modified to accommodate additional games of the same genre, or heavily modified to accommodate games of different genres when actually needed. Over-genericness can waste more time than it saves. So while making your code, I strongly suggest you do so focusing on a specific game you are making, so you don't over-engineer yourself into not releasing any games and only having an engine that works for no games.
  4. This is why the const keyword exists - pass it to the renderer by const reference, and the compiler will spit out an error if code that accesses the member tries to modify it.   void Renderer::consume(const std::vector<RenderToken>& tokens) { // this will cause a compiler error! tokens.resize(0); }   I want to make sure this quote is seen by the OP, because it's probably the most important thing mentioned in this thread. 'const' is a key concept that must be grasped, being so fundamental to almost everything, that any C++ discussion takes comprehension of it for granted. i.e. you can't realistically talk about project-level software design without first grasping function-level and class-level design.   If the OP already replied to it, my apologies, I didn't see it.   [Edit:] Ah, the OP already saw it, since he up-voted that post. I missed that; my mistake!
  5. Banjo and Khazooie stars an anthro bear, and an arguably non-anthro bird. Ditto for Yooka-Laylee (anthro chameleon, non-anthro bat) made by the same core devs.   If you want purely non-anthro, you'll run out really quickly, and have to reach toward indie games like Shelter (or Half-life 1 mod, Cat-Life :lol:).   Though, for more mainstream, Legend of Zelda: Twilight Princess has you play roughly half the game as a non-anthro wolf (Link changes back and forth, but is often stuck in wolf form).   But this is an issue that is common to most forms of media. I can name on one hand how many books I'm familiar with with non-anthro animals; Jungle Book and Watership Down*, for example - Oh, and Raptor Red, and Redwall Abbey, though I haven't read those two. Now compare that to the tens of thousands of books starring human protagonists; even Jungle Book's protagonist is a human.   * Read if you haven't. And if you have, read it again - it's worth it.  :P  
  6.   Ah, so 'this->ImportantPointer' is uninitialized, and the correct code should've been e.g: else { this->ImportantPointer = nullptr; }.
  7. Congrats on your first game!
  8.   That's absolutely a good point. You can't learn everything at once, but have to take things in bite-sized chunks.
  9.   The choices shouldn't be: Avoid it almost everywhere or Use it almost everywhere   The smart thing is to learn when to use it, and use it in those situations, but not in other situations where using could problems.   Almost everything in C++ has "don't use it" situations and "use it here" situations. But less-experienced programmers often simplify that to, "Never use X!" or "Use X everywhere!" rather than, you know, actually learning.  :wink:
  10. by 'if(sizeOfData)', did you mean 'if(rawData)'? i.e. you were checking that the pointer wasn't null, but not that it was of non-zero size?
  11. Note also that 'auto' in that case is an 'int' copying by value (which in the case of an int, is preferred). But if you had something like, say, std::string, you'd probably rather have a reference - and usually you'd want it const, unless you are modifying it. std::vector<std::string> myStrings = {"red", "blue", "green"}; for(const auto &str : myStrings} { std::cout << str << std::endl; } Btw, while C++11 was a major addition, C++14 was a minor addition. C++17 will be another major addition, and C++20 (or whatever) will presumably be a small addition - this is the general 'tick-tock' upgrade plan they are going for.   The biggest new dealy with C++11 is move semantics (and the related 'rvalues'), which seem confusing (because people explain them poorly) but are actually really simple.   However, my favorite general syntax-related improvement is that you can now initialize class members directly in the class declaration: class MyClass { public: int myInt = 357; std::string myString = "Yay!"; }; Lambdas are also great! Functions (actually 'functors', because they are really classes in many cases) that you create inside other functions (among other things): #include <iostream> int main() { int counter = 0; //----------------------------------------------- //vvv Lambda vvv auto doSomething = [&counter](const std::string &str) { ++counter; std::cout << "Called 'doSomething' " << counter << " times. " << "Calling with with '" << str << "' as a parameter." << std::endl; }; //'doSomething' is a variable that *holds* an instance of the lambda to call later, like a function pointer. //----------------------------------------------- doSomething("Penguin"); doSomething("Aardvark"); for(const std::string &element : {"fire", "water", "earth", "wind"}) { doSomething(element); } return 0; } [Test the code right in your browser]   (Note that the lambda using what's called a "capture" has a reference to another variable ('counter')).   The general syntax looks like this: int myFunction(int arg) { } //Regular function [](int arg) { } //Lambda auto myFunction(int arg) -> int { } //Another way of writing regular functions (specifying the return type after the arguments) [](int arg) -> int { } //Demonstrating the same thing with lambdas Lambdas in other languages are typically called 'closures' or 'anonymous functions'.
  12. Depends on what IDE you are using. With g++ you pass both the directories where libraries can be found (-L (large L)), the directories where header files can be found (-I (large 'i')), and what libraries you want to link against (-l (small 'L' - no, I'm not kidding)).   Most IDEs handle that for you, though - or makefiles.   If you are going to use libraries, make sure they are static not dynamic, and you'll save yourself ten weeks of headaches.   I organize my code like this: Projects/       //(doesn't need to be language specific!) Projects/StrangerFlames/ Projects/StrangerFlames/Common/           //My "Common" library (a static library). Really needs to be broken up into sub-libraries when I have time. Projects/StrangerFlames/CommonTests/      //Test cases for Common. Ostensibly. Projects/StrangerFlames/Engine/           //The game logic itself. Projects/StrangerFlames/Platforms/           Projects/StrangerFlames/Platforms/Desktop/                  //Builds the .exe for Win/Mac/Linux PCs. Projects/StrangerFlames/Platforms/DesktopWithEditor/        //Builds the .exe for Win/Mac/Linux PCs, but includes the built-in Editor. Ofcourse, Projects/StrangerFlames/ is in a repo (I use Mercurial (aka 'hg') with the free BitBucket service, but git (and the free GitHub service) is more popular).   The common library is under the StrangerFlames repo, but at present I don't mind. If I work on another project, I don't want to be afraid of making changes that'll break StrangerFlames, so I want a full copy of the depedencies as part of the StrangerFlames repo. In the future I'll probably make it be its own repo, though.
  13. Sounds interesting - like a online Bioshock world.   I see several key focuses and major challenges.   Focuses and challenges: A) Atmosphere. The underwater environment creates an excellent setting to really layer atmosphere on thick and heavy. You get it almost for free, so you'd basically have to try hard to mess it up. But even with getting it for free, you want to go above and beyond, and do an incredible job, because it'd be one of your main selling points. I'd say key here would be to actually scale back the thick-and-heavy free ambiance, and really go for a more finessed atmosphere by keeping the atmosphere subtle and laying five or six subtle layers of complementary atmosphere to in-game areas to create a richer meal that'll be more enjoyable to players long-term. Bioshock took the thick-and-heavy approach, which works excellently for it single-player game only lasting 15 hours, but not for an MMO that players play 10-20 hours a week for months and years.   B) Level design.  To an especially creative person, underwater habitats provide a welcome challenge and artistic constraint to create interestingly laid out gameplay areas, and provide some fantastic components to do so. Unlike with atmosphere, however, it'll take conscious effort and skill to both create those areas well on the normal scale - each area needing to be properly designed, not merely built on the fly - and also to make every area fit together well in the macro-scale big picture; it can't feel like each area is the same, nor can it feel like each area is entirely different. The world itself can't feel like hundreds of areas built in isolation and merely connected together - the game needs diversity, but it also needs cohesion.   C) Area diversity. By setting the game almost exclusively underwater and on the ocean surface, you'd have to work extra hard to not make everything look the same, and to have plenty of varied environments for players to explore. It's certainly doable by someone skilled in world design, but while setting the game underwater gives you a free boost to atmosphere, it also penalizes you on area diversity, and you'll have to work extra hard to overcome that, or your game will suffer with long-term player retention.   D) Exploration initiative You'll also have to work extra hard to make players want to explore. If the world feels just like a bunch of mostly-identical pockets of areas pointlessly spread apart by giant empty space, people get bored fast. If you ever played Legend of Zelda: Windwaker, it suffered from this problem - and you need a hundred times more content, making it an even bigger challenge.   In addition to that, you'll need solid world lore (read: backstory, culture, etc...), and solid gameplay.   You haven't mentioned any gameplay (a bad sign), and there's loads of problems with your story, which I'll just give some examples of and explain why it's important.     This strikes me as entirely unrealistic and hand-wavy, which breaks immersion. It seems you already know the result you want (an underwater game) are writing the plot backwards to get the result you want, but that can easily lead you to having a crummy plot.   A) "when the first atomic bomb hit south korea, 2 more hit north korea." In response to a nuclear bomb hitting South Korea, ZERO nukes would be launched at North Korea. It's unrealistic to real-life politics. It's not in anybody's interest (China, USA, or even South Korea's) to launch a nuke at North Korea.   And even if there was a nuke launch, there is no need to launch more than one! North Korea only has one viable target (Pyongyang), and the only point of a nuke would be symbolism, and even there it'd be a dumb political move with almost zero benefit and loads of detriment.   B) "over 20 nuclear weapons were used during the 40 year long war." It's unrealistic for the war involving actually-launched nuclear weapons to last 40 years. If it did last 40 years, and nukes were launched, it's unrealistic that only 20 would be fired. It's unrealistic that China would launch any nukes at anyone, unless they were involved in direct war against a major nuclear-armed state like Russia or the USA. In a war against South Korea, the political cost of China launching a nuke outweighs the man-power and economic costs of just kicking their butt using traditional means.   C) "Both nations suffered dramatic losses" No, that's just too hand-wavy. China would curbstomp Korea, unless something really substantial occurred - like an Admiral Yi. But that's an abnormality, so unless you explain or hint at the abnormality, then your half-hearted "yea they both lost pretty equally, despite one being two hundred times better equipped and two hundred times more man-power." breaks immersion because it contradicts the reality of your own fictional world. Because anything you don't explicitly add into your world, is automatically adopted from reality. Unless you say gravity is different, then gravity is the same. Unless you explain why China would suffer any kind of even minor losses from Korea, then China automatically wins.   For example, South Korea doesn't even have nuclear bombs! The USA has several nuclear-bomb equipped bomber jets stationed there, but South Korea can't use them, and in a conflict with China, China can take them out with traditional weapons before people even know war has began.   D) "the in house assassination of china's leader marked the end of the war" Nope, the assassination of China's leader won't mark the end of the war. (You do know China is ran by a council of 9 members, right? The current President Xi is the head of the party, having replaced the former president and party-leader because the former president was too old according to party laws) This just seems a hand-wavy excuse to end a war that you hand-wavy started, just to get the result you want.   E) "and sparked a 100 year peace treaty with all nations." You only sign peace treaties with nations you are at war with, or at risk of war with. Not "all nations" ('all nations' = hand wave). Not 100 years (another hand wave, being overly neat and overly dramatic, with zero explanation or reason).   Obviously this isn't the whole story, and just snippets to give us the general idea, but what I'm saying is even if you took this story concept and expanded using a brilliant writer and a full novel, it'd still break immersion, because the key details are unrealistic at the core. I only mention this because unrealistic hand-waving breaks immersion. You're violating the contract you signed with me, as a consumer. The contract states that I agree to suspend disbelief as much as possible, while you agree to minimize breaks of immersion as much as possible. You are breaking immersion frequently, because your world isn't self-consistent with the perception of it you have formed in my mind when you invited me into your magic circle.   [Edit:] My apologies for the sloppy grammar - I was in a hurry. Hopefully this is enough to get you started re-analyzing your idea, and maybe get you to look at it from a few additional design angles.
  14. I made a mistake in my first post, where I said: "I'd move "auto newMessages" to outside the loop, so you can help hint to the compiler to re-use it" I had forgotten that for RVO to take effect, you have to construct the variable from the result of the function (not simply assign), so the variable can't be brought outside the loop (oops) if you also want RVO - in that case, I'd still bring it outside the loop (saving the repeated constructions/destructions).       I think I remember watching a video where Scott Meyers or the MS compiler guy said something about RVO, where if the return size is larger than 4k~ then RVO could be worse then rvalue since the caching/paging mechanisms of the stack would not be large enough for RVO to function without the CPU fetching more pages to the cache. Afaik the retern value has to be at the top of the stack after the function returns, and if the pre-fetcher don't know how much memory the RVO will use on the stack then it would have to reallocate for every page it has to grab. Do you think that applies to returning a std::vector since internals is pointing to heap?     Seeing that the compiler can do whatever it wants, as long as it gets the same result, we can't say for sure what it's doing in your case without seeing the assembly. (and in my case, I haven't yet taken time to learn assembly.  :ph34r:)   That said, we can theoretically walk through both cases of RVO and move-semantics. Let's say your vector contains 1 MB of data: struct MyElement { uint32_t x,y,z,w; //16 bytes. }; template<typename Type = MyElement> class MyVector //Our pretend implementation of std::vector { size_t elementCount = {67 million and change}; Type *data = {1 MB of data}; public: MyVector(MyVector &&other) //Move constructor. { std::swap(elementCount, other.elementCount); std::swap(data, other.data); //Simply swaps the *pointer*, does *not* move around any data! Doesn't even deference the pointer (so that particular cache line isn't even called up). } }; //Likely 16 bytes on a 64 bit OS (a 64 bit integer and a 64 bit pointer). (Some implementations of std::vector use two pointers (begin and end) rather than 'begin' and 'count', because this is more optimized CPU-wise, but that doesn't affect this discussion, and the class size would end up being pretty much the same (two 64bit pointers vs a 64 bit pointer and a 64 bit integer)).   So now let's examine two theoretical straight-forward implementations of returning r-values vs RVO.   Returning r-value: //------------------------ //Example situation: //------------------------ MyVector&& MyFunc() { MyVector localVector; std::swap(localVector, this->memberVector); return std::move(localVector); } MyVector finalVector = MyFunc(); //------------------------ //Example implementation: //------------------------ Allocate finalVector //16 bytes Call MyVector() default-constructor on finalVector Allocate MyFunc_localVector //16 bytes Call MyVector() default-constructor on newly allocated MyFunc_localVector Swap MyFunc_localVector and MyFunc_memberVector .....(three moves* and an MyVector temporary allocation) *[by 'move' I mean a call to move-assignment or move-constructor) ..........(each MyVector move is two swaps) ................(each swap is three moves and a temporary allocation) Call Function std::move() on MyFunc_localVector .....(std::move() is just a cast from l-value reference to r-value reference) .....[remember: the function takes a universal reference, not a r-value reference - that can look confusing] .....[this std::move() will very likely get optimized out entirely, unless I'm confused] Return the result and assign to finalVector .....Move [unnamed MyVector r-value] into finalVector ..........(each MyVector move is two swaps) ................(each swap is three moves and a temporary allocation) Destruct [unnamed MyVector r-value] //Later: Destruct finalVector If RVO takes affect, then it (possibly) looks like this:  Allocate finalVector //16 bytes //Skipped: Call MyVector() default-constructor on finalVector //Skipped: Allocate MyFunc_localVector //16 bytes Call MyVector() default-constructor on [[pre-existing]] [[finalVector]] Swap [[finalVector]] and MyFunc_memberVector .....(three moves* and an MyVector temporary allocation) *[by 'move' I mean a call to move-assignment or move-constructor) ..........(each MyVector move is two swaps) ................(each swap is three moves and a temporary allocation) //Skipped: (in the other case, it'd be likely optimized out anyway) // Call Function std::move() // .....(std::move() is just a cast from l-value reference to r-value reference) // .....[remember: the function takes a universal reference, not a r-value reference - that can look confusing] // .....[this std::move() will very likely get optimized out entirely, unless I'm confused] //Skipped: // Return the result and assign to finalVector // .....Move [unnamed MyVector r-value] into finalVector // ..........(each MyVector move is two swaps) // ................(each swap is three moves and a temporary allocation) //Skipped: Destruct [unnamed MyVector r-value] The only problem in this case is that RVO might not get triggered.   But here's good news: If RVO isn't triggered, code like this: std::vector<std::tuple<HWND, UINT, WPARAM, LPARAM>> Window::moveMessages() { //...blah... std::vector<std::tuple<HWND, UINT, WPARAM, LPARAM>> windowMessages; std::swap(windowMessages, m_windowMessages); return windowMessages; } ...that's used like this: auto newMessages = MyWindow.moveMessages();...has newMessages' move-constructor getting called anyway, because the result of that function is still an rvalue!   MyType x = (y + z); //This is returning an rvalue and giving it to 'x'. As you know, '(y + z)' is really a function call: 'operator+(y, z)'. Yet the result is an rvalue, because once the function exits, there's no existing variables connected to the value. In the same way, MyWindow.moveMessages()'s result is an rvalue. It's an unnamed temporary variable that the compiler knows isn't being used by anything else. Returning by value will either RVO (when possible), or rvalue (if move-constructor/move-assignment operators are available for that type), or deep copy if your compiler doesn't support move-semantics (and if your compiler doesn't support move semantics, calling std::move() ain't gonna make a difference! :wink:).   But anyways, regardless of whether the compiler uses RVO is used or move-semantics, the 1MB data in the vector isn't touched. So this shouldn't be the location of your slowdown, unless I'm missing something (which is very possible! :P). Have you really confirmed through profiling that your slowdown is in how you return from that function?
  15.     :huh: