Jump to content
Site Stability Read more... ×
  • Advertisement


  • Content Count

  • Joined

  • Last visited

Community Reputation

2890 Excellent


About irreversible

  • Rank

Personal Information

  • Role
  • Interests

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Most, if not all of what you're saying should be handled by the logic thread. In fact, the render thread is the least relevant one here (for the most part). For UI actions what you're concerned about are the input and logic threads. Let's say there's some mouse input (a click, mouse move event or whatnot). The input thread pushes this in a queue. During the next tick the logic thread processes the input queue and determines what the result of the input is... ... If the mouse is over a "Build Wind Trap" button, and the input is a click it triggers the Build Building mode with the Wind Trap as the argument (see below). ... If the game is already in Build Building state, it sets a sate for the render thread ("draw this building here, please"). If the mouse has moved, it first performs a raycast to get the position on the ground*. Finally, it performs any suitability checks for the building placement. If the placement is good, it sets another part of the state for the render thread - "draw it in green please" (or "red, please" otherwise). ... If the user is hovering over a valid location and performs a click, it adds the building to the game world during that tick. * Now, hit testing can be just a smidge trickier since you can perform this on the logic/physics thread or during the render pass as feedback from the GPU. In the latter case you use a similar mechanism to propagate the current world (or view) space coordinate back to the logic thread, which then ties the two things (input and render thread) together. >> How best for the render system to retrieve this information? Create a simple struct that relays this information. Make sure only one thread can write to it and you'll be free of most multithreading issues, though proper synchronization and paying attention to who owns the data at any given time goes a long way. Consider really simple communication between the input and logic threads: struct mouseinput_t { int button; int state; }; struct inputqueue_t { std::mutex _mtx; std::vector<mouseinput_t> mouseInput; }; Write to the queue (input thread): mouseinput_t newMouseEvent; newMouseEvent.button = MOUSE_LEFT; newMouseEvent.state = MOUSE_DOWN; queue._mtx.lock(); queue.push_back(newMouseEvent); queue._mtx.unlock(); Read from queue (logic thread): queue._mtx.lock() for(auto event : queue.mouseInput) HandleMouseEvent(e); queue.clear(); // you'll want to do this here queue._mtx.unlock(); If your input processing is very heavy, then you'll want to make a local copy of the queue, as shown below. Communication between the logic and render thread is similar: struct preview_t { std::mutex _mtx; const builting_t* building; color_t color; vec3_t position; }; Logic thread: preview._mtx.lock(); preview.color = COLOR_GREEN; preview.position = hitTestResult; // you got this from the raycast preview.building = buildingType_WindTrap; // some object that describes the Wind Trap building preview._mtx.unlock(); Render thread: preview._mtx.lock(); auto localPreview = preview; // make a copy of the state since the render thread likely runs a fair bit faster than the logic thread and can stall the logic thread unnecessarily preview._mtx.unlock(); if(localPreview.building) DrawBuildingPreview(*localPreview.building, localBuilding.position, localBuilding.color);
  2. 1. What does fire_at() do? Does it access the target object extensively? If yes, can you reduce the accessed data to something small? I would imagine firing at an actor can be reduced to firing at a scene component style object whose size can be reduced to just a handful of bytes. 2. What is the "target"? If the target is a remote actor and there's only a handful of targets per firing object, then can you split off an above-mentioned scene component style chunk and copy it locally to the actor that is firing? (note that this is kinda moot without answering point 3) 3. Are you dealing with actual scale or trying to optimize the wrong thing? If yes to either, can you time-slice your logic? Point in case: Starcraft (or any RTS game for that matter, though Starcraft is a really nice example because it is extremely competitive and therefore needs to aim its requirements at machines that run on potato batteries with presumably weak hardware) supports armies of up to 200 units (I'm rounding here) per team. In a minimalist 1-on-1 game you can therefore have up to 200 zerglings or whatnot per team that need to find their paths relatively intelligently on a map that may be fairly large. Things get much worse when you have 3, 4 or 8 teams. Now, pathfinding is an inherently cache-unfriendly problem, which is why you cannot brute-force your way through this. You cannot take 200 zerglings and find paths for all of them in one timeslice (tick). Therefore, any modern RTS game likely employs any number of tricks from path caching to multi-tier pathfinding to simplify this problem. But there is a simpler way: spread out your work load. You can do this it two ways: laterally (on different threads) or temporally (timeslicing) Does every object in your game fire at one or more enemy every tick? Probably (hopefully) not. Here's where you could cheat! Unless you can substantially improve cache coherence (as suggested in points 1 and 2) or come up with a clever set up your data in a completely different way, just ignore the cache issue and solve the problem architecturally. Option A: create a task system and assume there's at least one other thread that can take 30-50% of the load off the main thread. This is actually fairly easy if you synchronize your data at regular intervals and work on thread-local data sets. Or, better yet, option B: place your firing squad in a priority queue. Assume that every tick you have up not N milliseconds to spend on logic and work through that queue as you do right now. When you run out of time, simply finish the tick and keep processing the queue during the next tick. Unless you fill up a gigantic queue (we're talking millions of objects firing at any number of things) that brings a modern CPU core to its knees for more than a full second, all you'll see are very slight potential delays before a shot is fired here or there. Chances are that in a real world scenario, parts of your unit logic "lag" a bit but that remains unnoticeable to the player since, hopefully, they're busy, you know, playing
  3. My motivation was to write a test framework that I could run on functions to check whether they properly handle bad input. The below code sort of assumes the tested function does not throw, because that would defeat the purpose. Instead it should return gracefully and/or output a pretty error string. If you've done a bad job at coding, you'd, of course, expect it to crash. The tester takes a set of reference arguments, which come in the form of hopefully valid input arguments that you provide. Code that runs all tests might then look something like this:void my_test_nocrash(int32 i32, char* str, testing::eTestType argtype, void* ptr, math::vec3 vec) { lout
  • 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!