Jump to content

  • Log In with Google      Sign In   
  • Create Account

Banner advertising on our site currently available from just $5!


1. Learn about the promo. 2. Sign up for GDNet+. 3. Set up your advert!


Zipster

Member Since 11 Mar 2000
Offline Last Active Today, 05:32 PM

#5243638 Safety vs Efficiency

Posted by Zipster on 30 July 2015 - 02:11 PM

In the past, I've taken advantage of some special macros which are a combination of an if-check and an assert:

#ifdef _DEBUG
#define MUST_BE_TRUE(expression) if ((expression) || (Assert(#expression), false))
#define MUST_BE_FALSE(expression) if ((expression) && (Assert(#expression), true))
#else
#define MUST_BE_TRUE(expression) if (expression)
#define MUST_BE_FALSE(expression) if (expression)
#endif

In Debug, an assert will be raised in the unexpected case, but in Release you can still handle both cases.




#5243628 2D Platformer Camera

Posted by Zipster on 30 July 2015 - 01:21 PM

Tile culling logic for this type of game is so trivial that there's no reason why you shouldn't do it. Not only will you avoid sending throwaway data to the rendering engine, but knowing which tiles are on-screen can allow for certain gameplay benefits and optimizations.

 

Just transform the upper-left screen coordinate and lower-right screen coordinate to tile-space (you can also go directly from view-space coordinates to save a transformation). Truncate to integers, and that's your upper-left and lower-right visible tile bounds.




#5243415 default install folder for windows game

Posted by Zipster on 29 July 2015 - 12:20 PM

I've seen applications that install their static components into Program Files, such as updaters, patchers, etc., which then "install" everything else that can be modified or updated into AppData.




#5239034 Job Manager Lock-Free

Posted by Zipster on 08 July 2015 - 12:23 PM

In mine at the moment, if i fail to pop a job, I try to decrement a semaphore (which will block if it hasn't been incremented). Every time I push a job, I increment the semaphore.
When the queue becomes empty, yes, it does spin for a while until the semaphore reaches zero, then sleeps until the next push.

There's probably a better way to implement that using events/condition-variables...

 

That is exactly what I do as well. I've found that you can tune the semaphore's max count to a certain extent to mitigate the "wind down" period where the threads spin on an empty queue, but depending on how and when jobs are pushed to the queue, you can run into situations where threads aren't awakened when there's work for them to do. So for now, I just set the max count equal to the number of worker threads.

 

 

There is actually a much easier way to look at this than has been suggested so far, though it is along the lines of Hodgmans solution.  Basically just never let the queue fail to return something, instead when it is going to fail you return a default task which contains the 'wait' code.  In that task you can perform the same solution as Hodgman suggests or be a little more clever using a condition variable to prevent the excess spins.  Using this form has a number of benefits since you are effectively setting your system up somewhat like a VM.  For instance, I adjust the number of threads in my system dynamically so if a single core can keep everything happy, I have a couple of the threads stuck in wait tasks.  For mobile, this allows the cpu to power down cores not in use and extend battery life considerably.  Having them as simple tasks means that the complexity of control is removed from the tight execution loop and of course waking them back up is as simple as releasing a semaphore or condition variable.  I have found this approach to have a great number of benefits once you get your thinking around to it, mostly in that it remains lockless but with the ability to play nice with the OS as desired.

 

This is an interesting way to approach the problem, but since returning a default task when a queue is empty isn't typical queue behavior, you'd have to roll your own solution instead of being able to drop in a ready-made container like tbb::concurrent_queue. At that point it's a toss up between writing wait code in the thread loop ala Hodgman's solution, or writing default task handling code when popping the queue.




#5235373 Tiny Serialization Library

Posted by Zipster on 17 June 2015 - 07:51 PM

On the topic of versioning of variables, since it interests me personally...
 
Can someone give me a practical example of when you really need a versioned variable, that e.g. only shows up after version 4 like snake5 gave? I'm asking out of curiousity because I have found so far that having those two conditions:
 
- Variables declared and not found in the savegame are initialized to a default value and
- Variables not declared but found in the savegame are dismissed
 
to be more than sufficient even for my rather complex 2D RPG. It might be my limited experience with "professional" real world games, but I don't see any case where having those conditions would really break anything over a manual versioning system...

Those conditions only handle the addition of new data or the removal of obsolete data, but not the transformation of existing data.

Quick example, let's say that in version 0 you save a value representing a duration in seconds, but in version 1 that changes to miliseconds. The only way to properly interpret the value on load is explicit version handling, and it quickly gets tricky for non-trivial transformations.


#5234553 Engine design, global interfaces

Posted by Zipster on 12 June 2015 - 08:26 PM

Funkymunky, are you sure a couple of instances of your Singleton are enough?

 
Definitely, I made a SingletonManager class to make sure I kept track of them all properly.
You'll only ever want one of those for sure, so make sure it's also a Singleton.


#5234534 Library Question

Posted by Zipster on 12 June 2015 - 05:48 PM


In windows - if I create a static library which uses other shared libraries, will whoever is using my static library need to use the dlls from the shared libraries?

Yes, if a static library has a shared library dependency (DLL), then any code that links that static library will take on the shared library dependency as well.

 


Or will the code in those dlls be compiled in to the static lib?

The whole point of shared libraries is to not have to statically link against code, and instead load it at run-time, so no that would not happen.

 


The same question for linux - if I create an .a which uses .so's, will whover uses my .a file need the .so files used to compile it?

I don't do a lot of Unix development, but I imagine it's exactly the same.




#5234334 My first (static) library

Posted by Zipster on 11 June 2015 - 11:59 AM

A static library doesn't really provide much benefit during development, and is usually only appropriate once your code is more stable and you're not making changes as often. Otherwise every time you make a change, you need to incur the cost of re-building and re-linking against the entire library, as opposed to just a targeted handful of object files. It doesn't sound like much, but we've found that this cost adds up slowly and can eventually drive you insane :)

 

If your goal is to test how well you can build apps against your engine, then you should just attempt to do so while not modifying engine code (or doing so as little as possible). Proper version control software will make this easy to keep track of. Once you're confident in your engine's capabilities, you can then pay the setup cost of a static library.

 

On the other hand, we've found DLLs to be much more beneficial during development, since you only have to rebuild the libraries/engine you modify (no re-linking of dependents necessary), improving iteration time quite a bit. As a matter of fact, all of our projects are set up to use our internal libraries as DLLs for development configurations, and static libraries for Gold/RC configurations. You get the best of both worlds.




#5232452 RTS Server architecture

Posted by Zipster on 02 June 2015 - 02:12 PM

We released an RTS game earlier this year that is client/server, with the server-authoritative state being updated to clients. It's not the most common approach, but it works. The biggest advantage to client/server in this case is that the architecture is applicable to just about every type of game, even if it isn't typically considered the best fit for RTS, whereas a lock-step deterministic solution really only works well for the RTS problem space, and games with similar constraints and limitations. So what you sacrifice in specificity, you make up for in being able to use the same technology across multiple projects.

 

But that's the mindset of a larger, multi-project studio. As an indie with only a handful of developers and starting an engine from scratch, definitely use the best approach for your immediate project. If/when you start working on more games, you can write more tech... but I'd consider that a *good* problem to have.




#5232032 Sending and Recieving Game Map Files.

Posted by Zipster on 31 May 2015 - 05:23 PM

As recently as last week I implemented this exact feature in our game using libcurl and straightforward GET/PUT requests to a web server. There are a plethora of examples on how to use it, most of them only a few dozen lines. zlib is similarly great for handling the compression locally before upload. As mentioned above, no point re-inventing the wheel when all these solutions already exist, and are fairly easy to use.




#5213411 Best way of ensuring semantic type consistency?

Posted by Zipster on 27 February 2015 - 05:10 PM

As far as time is concerned, you're typically dealing with either an absolute point in time, or a difference between two times. So the same way you have points and vectors in a mathematics library, I'd have Time and TimeDelta types in my time library to clearly communicate the semantic differences between the two. For instance, you wouldn't be able to add two Time's together, subtracting two Time's would result in a TimeDelta, etc. It may seem a little heavyweight at first, but it doesn't take terribly long to implement and will prevent a lot of common bugs when dealing with "absolute" versus "relative" measurements.




#5212944 Handling of modifier keys

Posted by Zipster on 25 February 2015 - 01:05 PM

I recently redesigned our game's input system based on the robust input handling article, and it works perfectly. However something I've noticed -- not just with this design but with others I've used as well -- is that modifier key behavior can be tricky to handle correctly, and it's not something I've seen covered in detail by resources on the subject.

 

The issue with modifier keys (Ctrl, Alt, and Shift for the purpose of this post) is that they can either be bound individually to game actions ('Shift' -> 'Action_Jump'), or in combination with a non-modifier key ('Ctrl+A' -> 'Select_All'). As you can imagine, this results in some interesting behavior if you have a modifier key that is both bound to an action on its own and modifiers another key bound to another action. If you want to trigger the latter action, you'll necessarily trigger the former when you first press the modifier. I don't have a problem with this though, since I'd say it's expected behavior when the user binds their actions in such a way (where an 'action' is a single-shot event).

 

However when the modifier key is bound to a state, the expected behavior is not as clear, especially if that state is designed to modify other actions that are triggered when that state is active. To take a specific example, in our game the user can press the 'Q' key to queue a single unit for production at a factory. If they're holding down the 'Shift' key when they do this, the unit is queued in a special mode that tells it to autonomously re-queue itself upon completion. At least this is what's supposed to happen, however if the user is holding down the 'Shift' key when they press the 'Q' key, the input system doesn't see a 'Q' press, it sees a 'Shift+Q' press, and no action is triggered.

 

The solution that made the most sense to me was to add some special logic for modifier keys bound to states. When the user presses a key, it first attempts to map that key with currently held modifiers. If it doesn't find any results, it then checks to see if there are any active states mapped to modifier keys (under the assumption that this could be why the original mapping failed). If there are, it re-maps the key without those modifier states, and uses those results instead. My reasoning behind this approach is that the user wouldn't expect a modifier key bound to a state, to interfere with any actions meant to be triggered while that state is active. The only side-effect is that if the user actually had another action bound to the modified key, like 'Shift+Q', that action would trigger instead of the 'Q' action. However I can live with that.

 

I'm curious as to whether or not this approach makes sense to anyone else, or whether they've found another way to deal with modifier key ambiguity? My above solution fixes the cases I need it to fix, however that doesn't mean there aren't some other nasty side-effects I haven't thought of yet that could appear with the right combination of key bindings. If possible, I'd like to move the special case handling out of code and somehow make it work by adding more options to the key binding data, but I'm not sure what that would look like...




#5209844 Quick Question re:Good Practice for this Game UI I'm designing...

Posted by Zipster on 10 February 2015 - 12:50 PM


Hmmm... so is that to say that rather having a set of premade buttons or w/e, you would get each UI scene to call the same methods and simply pass in their own copies of the same arguments?
Does that mean the "same" buttons would then have to be "defined" separately at each point in the code where the buttons were used?

Yes, each UI scene would maintain state for a particular widget and just pass it into the UI library when it needs something done with it. The style and "feel" of any particular widget would be part of the UI system, and possibly customizable. QStyle comes to mind as an example of passing widget state into something that knows how to render widgets with a consistent motif.




#5209698 Quick Question re:Good Practice for this Game UI I'm designing...

Posted by Zipster on 09 February 2015 - 04:40 PM

You don't really need a manager. Take a look at my example. All UI state is owned and managed externally, by whatever needs UI, and only passed into the UI code when it immediately needs it to be rendered or interacted with. If you want common elements, you simply have a set of methods in the UI library like drawButton/handleInputButton, drawLabel, etc., that take a state object to customize their look (position, size, text, color, etc.), and by virtue of the fact that all your UI scenes call the same methods, all the buttons will look and behave the same. Even layout can be handled this way, with something like a calculateBounds method, if you allow the state objects to be parented to each other.




#5209104 Quick Question re:Good Practice for this Game UI I'm designing...

Posted by Zipster on 06 February 2015 - 12:17 PM

An immediate-mode approach is typically good for simple, engineer-driven UI. You don't need to wrestle with an entire framework if you don't need one, and it's very quick to get everything up and running in a usable state. Simple example (excusing typos):
class GameUI : public UI::MenuState
{
public:
   GameUI()
   {
      menuAButton.parent = this;
      menuAButton.bounds = Rect(100, 100, 100, 50);
      menuAButton.text = "Menu A";

      menuBButton.parent = this;
      menuBButton.bounds = Rect(100, 200, 100, 50);
      menuBButton.text = "Menu B";

      menuCButton.parent = this;
      menuCButton.bounds = Rect(100, 300, 100, 50);
      menuCButton.text = "Menu C";
   }

   void render()
   {
      UI::renderButton(menuAButton);
      UI::renderButton(menuBButton);
      UI::renderButton(menuCButton);
   }
      
   void handleInput()
   {
      UI::handleInput(menuAButton);
      UI::handleInput(menuBButton);
      UI::handleInput(menuCButton);

      if (menuAButton.clicked)
      {
         // Do Menu A action
      }
      else if (menuBButton.clicked)
      {
         // Do Menu B action
      }
      else if (menuCButton.clicked)
      {
         // Do Menu C action
      }
   }

private:
   UI::ButtonState menuAButton;
   UI::ButtonState menuBButton;
   UI::ButtonState menuCButton:
};

Then call the render() and handleInput() methods from your game mode.






PARTNERS