0
\$\begingroup\$

Issue:

I want to be able to set up my game loop to work concurrently with my pathfinder request, that would be a queue which different units request for in order in the pathfinder thread.

My current attempts have so far failed.

   -

while (ConqEngine->IsRunning())
{
    bool running = ConqEngine->IsRunning();
    // Draw the scene
    ConqEngine->DrawScene();
    updateTime = ConqEngine->Timer(); // Frame Timer
    s1.pathfinderthread(ref(unitsneedpathfinding)) // this here, beginning of 
    if (menuloop)
    {
        initial_menu_loop(loaded_men_interface, menuloop, loadloop, infoloop ,textControl);
    }
    else if (loadloop)
    {
        preconqload(loadloop, gameloop, startingx, pipnumber);
    }
    else if (infoloop)
    {
        if (ConqEngine->AnyKeyHit())
        {
            ConqEngine->RemoveSprite(MainAB.LoadingSprite);
            infoloop = false;
            menuloop = true;

        }
    }
else if (gameloop)
{
    if (game_n_setup)
    {
        SetupMusic("sound/RA2.ogg");
        sMusic.play();
        game_n_setup = false;
    }

    ConqCamera->LookAt(0, 0, 0);
    CamLastFrame = CameraVec;
    gameloop_interface(cspeed, CameraVec, displaycubes, dcubestime, CamLastFrame,
    draycasttime, CitySprite, cityspriteno, wealthtick, myFont, myFont2, textControl);
    gameloop_player(wealthtick, gameloop, menuloop);
    gameloop_units();
    //gameloop_interface()
    if (ConqEngine->KeyHit(Key_Escape))
    {
        gameloop = false;
        loadloop = false;
        menuloop = false;
        ConqEngine->Stop();
    }






    s1.join();
}

}

Currently what this attempts to do is that i have a bool for gameloop as a global, and a global[currently] lists of units needing pathfinding, and i am trying to execute the thread each frame seperately from the rest of the thing running, and then do a join at the end to force the program to wait for all to be completed but this causes a crash.

What is a good structure for multithreading my game with a pathing requester ? I'm honestly not sure how to tackle this the best way, I don't mind my Unit having to wait a few seconds, as that would look somewhat realistic anyway, a thinking animation is really no problem for me.

Is there a standard practice for seperating out the game loop with something else?

\$\endgroup\$
  • \$\begingroup\$ Sounds like you need to debug why it's crashing before deciding that the structure needs changing. \$\endgroup\$ – MichaelHouse Aug 24 '17 at 22:57
  • \$\begingroup\$ I'm not entirely sure why its crashing, it seems similar to the crash where a thread is out of bounds, or its not finished the execution when it goes out of scope. \$\endgroup\$ – RNewell122 Aug 24 '17 at 23:07
1
\$\begingroup\$

I'm not sure exactly what your requirements are for pathing or how the game should behave so this is going to be a general approach. You'll have to tune what data needs to be shared.

Separate the threads
I think the first step may be to not consider the threads working in step with each other. That meaning, don't pause your game loop to wait for pathing results. You want your gameloop to run at whatever minimum frequency you require to keep smooth results.

Communicate work needed and solutions
I'll admit my understanding on threads in C++ is limited, but in a higher level language like C# or Java, I'd suggest a couple of concurrent data structures. One for queuing pathing requests to the pathing thread, and another for sending solutions back to the gameloop.

Then you can leave the pathing thread to have its own loop of, grab a pathing request, solve it and place the results.

And your game loop simply decides what pathing it needs, and then processes pathing solutions as they become available.

The goal is to make the data flow in one direction, one thread can write to a structure and the other reads from it.

Decoupling this way could also mean using more threads for pathing if it becomes necessary.

Responsiveness
This does also mean that there could be a number of iterations on the gameloop between when pathing is requested and when a solution is available. If your game is real time, you have a couple of options for what an object in the game can do during those loops (which may not even be noticable to a player).

  • Begin moving towards the destination, then adjust when the pathing solution is available
  • Have the object animate in some way that makes it look like it's trying to figure out where to go
  • Setup your solution data structure to be updatable. Your pathing algorithm could quickly include some first steps and update as you go. (This might be necessary if the pathing is meant to represent incomplete terrain data.)
\$\endgroup\$
0
\$\begingroup\$

I managed to fix my problem and make a nice concurrent system, the communication is handled with an abstract interface.

void pathingrequester()
{
    if (AWorld.RequestingPath.empty() != true)
    {
        AWorld.RequestingPath.front();
        astarpathfinder(AWorld.HiFiWaypoints, AWorld.RequestingPath.front()->pf_Source->ret_posX(), AWorld.RequestingPath.front()->pf_Source->ret_posY(),
        AWorld.RequestingPath.front()->UltimateDestination->ret_posX(), AWorld.RequestingPath.front()->UltimateDestination->ret_posY(), AWorld.RequestingPath.front(), AWorld.lines, AWorld.pointsInLine, MainAB.Path);
        AWorld.RequestingPath.pop_front();
    }
}


void gameloop(IFont *myFont, IFont* myFont2, abstractTextHandler &textControl)
{
    if (MainAB.game_n_setup)
    {
        SetupMusic("sound/RA2.ogg");
        sMusic.play();
        MainAB.game_n_setup = false;
    }

    ConqCamera->LookAt(0, 0, 0);
    MainAB.CamLastFrame = MainAB.CameraVec;
    gameloop_interface(MainAB.cspeed, MainAB.CameraVec, MainAB.displaycubes, MainAB.dcubestime, MainAB.CamLastFrame,
        MainAB.draycasttime, MainAB.CitySprite, MainAB.cityspriteno, MainAB.wealthtick, myFont, myFont2, textControl);
    gameloop_player(MainAB.wealthtick, MainAB.bgameloop, MainAB.bmenuloop);
    gameloop_units();
    //gameloop_interface()
    if (ConqEngine->KeyHit(Key_Escape))
    {
        MainAB.bgameloop = false;
        MainAB.bloadloop = false;
        MainAB.bmenuloop = false;
        ConqEngine->Stop();
    }

    stringstream mui;
    stringstream mui2;
    stringstream mui3;



    if (ConqEngine->KeyHit(Key_O))
    {

        EntityUnit* a;
        a = dynamic_cast<EntityUnit*>(AWorld.EntitiesVector[45]);
        a->set_source_waypoint(AWorld.HiFiWaypoints[104][53]);
        a->UltimateDestination = AWorld.HiFiWaypoints[DebugUP->currLine][DebugUP->currDot];

        AWorld.RequestingPath.push_front(a);
        MainAB.x = DebugUP->currLine;
        MainAB.y = DebugUP->currDot;


    }
    if (ConqEngine->KeyHit(Key_Z))
    {
        EntityUnit* a;
        a = dynamic_cast<EntityUnit*>(AWorld.EntitiesVector[45]);
        for (auto i = a->pf_waypoints.begin(); i < a->pf_waypoints.end(); ++i)
        {
            cout << (*i)->ret_posX() << ":" << (*i)->ret_posY() << endl;
        }
    }
    //s1.join();
    MainAB.SkyboM->RotateLocalY(0.001);

}

 /**** MAIN GAINLOOP ****///--------------------------------------------------
        while (ConqEngine->IsRunning())
    {
        bool running = ConqEngine->IsRunning();
        // Draw the scene
        ConqEngine->DrawScene();
        updateTime = ConqEngine->Timer(); // Frame Timer

        if (MainAB.bmenuloop)
        {
            initial_menu_loop(MainAB.loaded_men_interface, MainAB.bmenuloop, MainAB.bloadloop, MainAB.binfoloop ,textControl);
        }
        else if (MainAB.bloadloop)
        {
            preconqload(MainAB.bloadloop, MainAB.bgameloop, MainAB.startingx, MainAB.pipnumber);
        }
        else if (MainAB.binfoloop)
        {
            if (ConqEngine->AnyKeyHit())
            {
                ConqEngine->RemoveSprite(MainAB.LoadingSprite);
                MainAB.binfoloop = false;
                MainAB.bmenuloop = true;

            }
        }
        else if (MainAB.bgameloop)
        {
            thread gl(gameloop,ref(myFont), ref(myFont2), ref(textControl));
            thread pl(pathingrequester);
            gl.join();
            pl.join();
        }


        /**** Update your scene each frame here ****/



    }
// Delete the 3D engine now we are finished with it

ConqEngine->Delete();

}

This means my Normal A* pathfinding only takes 0.9 seconds over medium distances, and will mean when I finish my implentation of HPA* is complete, it will be near negligible performance loss.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.