path following

edited November 2014 in Help request
What I would like to do is to define paths for the players to follow, when selected, and then in-game, have the player follow the selected path. Is there some function/class for doing this? If so, how do you define the path? Another basic question, sorry. I'm sure there's something for this...

Comments

  • edited November 2014
    Hmmm... this is something I will needing to do soon myself.

    I had planned to make alien waves based on mathematical curves, but I think if I was to do it based on a predefined path, I would keep a list of vectors in the config, a vector point at intervals along the path.

    You might at least like to start reading about orxVECTORs: http://www.orx-project.org/orx/doc/html/group__orx_vector.html

    And search through both the wiki and forum for use on orxVECTORs.

    Iarwain will probably have a better way of doing this.
  • edited November 2014
    Well, actually, there's no AI-related framework in orx, which is a kind of a shame because that's what I do professionally.

    There used to be a grid-based A* pathfinder but I teared it apart years ago with the plans to make it more modular and never got around to it. If I do it in the future, it'll probably be either a node-based A* or a grid based JPS+.

    However for now, you'll either need to roll your system (depending on your needs that can be a simple waypoint-based patrolling system) or use one of the AI framework out there, sorry.
  • edited November 2014
    What I was thinking was something like a catmull-rom curve function that would smooth out the curve between multiple points. I was hoping you had something like that, but, no problem. I'll code up something and give it back when I get it working. That way I can just describe fixed x/y points for the path and then at run time interpolate it into a curve. Thanks for the input!
  • edited November 2014
    Ah, if you only need Catmull-Rom or Bezier curves, we do have a couple of functions for you (the Catmull-Rom one appears in the doxygen link sausage posted).
    The Bezier one (orxVector_Bezier) was added after the release of 1.5, so you'll find it mentioned in your own local doxygen doc (/orx/doc directory).
  • edited November 2014
    There's also the FX system that allows to apply curves to object properties, including their position and/or speed.
    Those curves are combinations of simple primitive curves such as sine, sawtooth, triangle, square, smoothstep, ...
    I haven't added catmull-rom or bezier to the list yet, and I also plan on adding free-form curves: https://bitbucket.org/orx/orx/issue/14/add-freeform-curves
  • edited November 2014
    Great!! That's perfect. So, I'll just describe my players path with x/y coordinates. This is like the playbook for a football play, for one player. Then, at runtime, I call your function, whichever is shortest, to set up my smooth path for the player. That's pretty cool! Thanks! Not a wasted topic at all. Sorry sausage, that I didn't follow your link, I didn't think it relevant when I read it...
  • edited November 2014
    I just realized what you were saying. Well, you're right, I plan to have a predefined path of x/y coordinates to generate the spline curve. I'm not sure how I'm going to input that at this point. Maybe I'll have a gui that generates a config file from a spline curve that's drawn in the gui. Not sure yet. Heck, it could be a text document with x's at spots along the curve for that matter. I'll let you know.
  • edited November 2014
    Nah that's cool. I thought it might have helped because your objects perhaps would be using physics movement and be given a directional vector.

    The movement vector could have continually been updated to adjust to the next waypoint in your list of orxVECTORs.

    And as Iarwain said, there's a lot of math calc functions in that API for distances, directions etc.
  • edited November 2014
    Thanks, yeah. I like what you're saying. I'm not that far along yet. Haven't even thought about directional physics. Heck, I don't even know the engine yet... This is my first cut at something that works and I appreciate any input I can get.
  • edited November 2014
    Might be a very good candidate for a wiki article if you work something simple out.
  • edited November 2014
    I'm not sure that's worth much for you, but you might be able to get some inspiration out of it.
    I'm currently using Bezier paths for one of my projects (purely for animated visuals). The way we do it is that the artist creates them in SVG format and I wrote a very simple converter that will output the path in orx's config format.
    I'll then simply iterate over the points in the path and render them using a mesh with orxDisplay_DrawMesh.

    I actually added the conversion utility to the game itself, as a command line parameter (just because it was very easy to plug it in than write a separate tool and I'm lazy that way).

    Here's the code, it relies on Mikko Mononen's NanoSVG (a single C file SVG decoder):
    #include "Scroll.h"
    
    #define NANOSVG_ALL_COLOR_KEYWORDS  // Include full list of color keywords.
    #define NANOSVG_IMPLEMENTATION      // Expands implementation
    #include "nanosvg.h"
    
    static orxBOOL orxFASTCALL SaveFilter(const orxSTRING _zSectionName, const orxSTRING _zKeyName, const orxSTRING _zFileName, orxBOOL _bUseEncryption)
    {
      return (orxString_Compare(_zSectionName, "SVG") == 0) ? orxTRUE : orxFALSE;
    }
    
    static orxSTATUS orxFASTCALL Convert(orxU32 _u32ParamCount, const orxSTRING _azParams[])
    {
      struct NSVGimage *pstImage;
      const orxSTRING   zOutput = "svg.ini";
      orxSTATUS         eResult = orxSTATUS_FAILURE;
    
      // Has a valid parameter?
      if(_u32ParamCount > 1)
      {
        // More than one parameter?
        if(_u32ParamCount > 2)
        {
          // Updates destination
          zOutput = _azParams[2];
        }
    
        // Opens the source with NanoSVG
        pstImage = nsvgParseFromFile(_azParams[1], "px", 96);
    
        // Success?
        if(pstImage)
        {
          orxU32 u32PathIndex = 1;
    
          // Pushes config section
          orxConfig_PushSection("SVG");
    
          // For all shapes
          for(NSVGshape *pstShape = pstImage->shapes; pstShape != NULL; pstShape = pstShape->next)
          {
            // For all paths
            for(NSVGpath *pstPath = pstShape->paths; pstPath != NULL; pstPath = pstPath->next, u32PathIndex++)
            {
              orxCHAR acBuffer[32768] = {};
              orxCHAR acKey[64] = {};
              orxU32  u32Offset = 0;
    
              // For all vectors
              for(int i = 0; i < pstPath->npts; i += 1)
              {
                orxVECTOR v = {pstPath->pts[i * 2], pstPath->pts[i * 2 + 1], orxFLOAT_0};
    
                // Prints it
                u32Offset += orxString_NPrint(acBuffer + u32Offset, sizeof(acBuffer) - 1 - u32Offset, "%s(%g, %g, %g)", (i != 0) ? " # " : orxSTRING_EMPTY, v.fX, v.fY, v.fZ);
              }
    
              // Stores paths
              orxString_NPrint(acKey, sizeof(acKey) - 1, "Path%d", u32PathIndex);
              orxConfig_SetString(acKey, acBuffer);
    
              // Stores width
              orxString_NPrint(acKey, sizeof(acKey) - 1, "Width%d", u32PathIndex);
              orxConfig_SetFloat(acKey, orx2F(pstShape->strokeWidth));
            }
          }
    
          // Pops config section
          orxConfig_PopSection();
    
          // Saves to disk
          orxConfig_Save(zOutput, orxFALSE, &SaveFilter);
    
          // Delete
          nsvgDelete(pstImage);
    
          // Updates result
          eResult = orxSTATUS_SUCCESS;
        }
      }
      else
      {
        // Logs message
        orxLOG("No file provided, aborting");
    
        // Updates result
        eResult = orxSTATUS_SUCCESS;
      }
    
      // Done!
      return eResult;
    }
    
    void Init()
    {
      orxPARAM stParam = {orxPARAM_KU32_FLAG_NONE, "s", "svg", "convert SVG file", "Convert SVG file to orx config format", &Convert};
    
      // Registers param
      orxParam_Register(&stParam);
    }
    

    Then using the command line switch -s, I can convert a .svg file.
  • edited November 2014
    That is amazingly helpful. This is great stuff. Thanks for sharing. It seems like a team effort, which I haven't had in a while...
  • edited November 2014
    Which reminds me that the IRC channel would probably be more appropriate for this kind of discussion.
    I stopped hanging there because it was rather empty (and my current employer's firewall doesn't allow me to do so anymore), but I'll try to get there more often. As a reminder, it's on Freenode, #orx-project, there's a link on the site's left navigation panel.
    I won't be able to come tonight as it's getting late and I have some IGF judging to do. :)
  • edited November 2014
    Never heard of IGF. Looks cool. I'll check out irc.
  • edited November 2014
    Thinking more about this issue today.

    Ended up reading into how Catmull Rom splines work (thankfully pretty straight forward http://www.gamedev.net/topic/575181-catmull-rom-splines and http://schepers.cc/svg/path/dotty.svg)

    Then started reading in the box2d forum to see how it will apply to physics and came to realise I'm comparing two incompatible methods.

    If I want an alien wave, I either wait until free-form curves are available in the FX module.. or I have a clock that constantly updates object positions manually at interpolated x and y positions along the curve.

    Am I right on this?
  • edited November 2014
    This is what I have come up with initially:

    init
    ----
    p1 = orxVECTOR_0;
    	p2 = orxVECTOR_0;
    	p3 = orxVECTOR_0;
    	p4 = orxVECTOR_0;
    	
    	p2.fX = 200;
    	p2.fY = 600;
    	
    	p3.fX = 400;
    	p3.fY = 0;
    	
    	p4.fX = 600;
    	p4.fY = 600;
    
    	fighter = orxObject_CreateFromConfig("Fighter");
    
    	pstClock = orxClock_Create(orx2F(0.01f), orxCLOCK_TYPE_USER);
    	orxClock_Register(pstClock, Update, orxNULL, orxMODULE_ID_MAIN, orxCLOCK_PRIORITY_NORMAL);
    

    And the clock:
    void orxFASTCALL Update(const orxCLOCK_INFO *_pstClockInfo, void *_pstContext){
    	orxVector_CatmullRom(&result, &p1, &p2, &p3, &p4, i);
     
    	orxObject_SetPosition(fighter, &result);
    	
    	i += 0.01;
    	if (i >= 1){
    		i = 0;
    	}
    }
    

    Works ok, smoothly sends the fighter object along points 2 and 3 (points 1 and 4 are just anchors to determine the shape of the path).

    orxVector_CatmullRom outputs the x and y position, but does it also output the rotation? I suppose I will need to read one step ahead and calculate the orxVECTOR direction.
  • edited November 2014
    Ha! You beat me to it. That was my plan too. I had already studied those 2 functions, catmull and bezier. The cat game got me side tracked, so I haven't gotten to it yet. Glad to hear it works good. Thanks!
  • edited November 2014
    You can also compute the actual speed instead of just position, that will help keep the physics engine happy.

    When I implemented this, lydesik suggested to also add a version that would compute the tangent along the position. If you find it useful, I can add that at some point, just open an issue for it.
Sign In or Register to comment.