Path Movement in Speer

Posted October 17, 2017

I’ve recently been asked about how I did the movement of some Sparkballs in [Speer], more specifically these ones:

jail.gif
While the simple answer would be “Path movement, baby!” I thought I’d use the opportunity to go a bit more in depth and show you how I did it. Since [Speer] is powered by HaxeFlixel this is going to focus on that particular framework, although the general approach could easily be implemented in other engines and languages as well.

Step 1: Following Paths

Objects in HaxeFlixel (more specifically, instances of FlxObject) have a path property that will automatically make the object follow that path if you pass it an array of coordinates. Once you’ve done that, you just have to tell the object to start moving. For this you use the start function whose full signature is as follows:

start(?Nodes:Array<FlxPoint>, Speed:Float = 100, Mode:Int = FlxPath.FORWARD, AutoRotate:Bool = false, NodesAsReference:Bool = false):FlxPath

And that’s pretty much all you need. If you’re working in a different engine you might have to implement this behavior yourself but thankfully HaxeFlixel has already taken care of it!

However, it would be quite annoying having to add arrays of coordinates to every object, especially from within code. This brings us to the next step.

Step 2: Drawing Paths

It would obviously be a lot easier if we could draw the paths somehow, so that’s exactly what we’re gonna do. For [Speer] I’m using the excellent Tiled Map Editor which allows one to draw Polylines, which will serve as our paths in this instance. It’s as easy as creating a new entity layer, picking the Polyline tool (preferably by pressing L so you can feel like a shortcut wizard), then clicking to place one node after another. Right-clicking ends and confirms your line.

drawing.png

Well, it sure is pretty…but how exactly do we tell our object that this path exists and that it’s supposed to follow it? This is where it gets interesting!

Step 3: Connecting it All

If you’re using Tiled with HaxeFlixel, you are most likely using the HaxeFlixel Tiled addon package. If not, you should be. Additionally, I’d recommend taking a look at the source of this HaxeFlixel demo, specifically the file TiledLevel.hx. This loads all kinds of data from a Tiled map file, creates the tilemaps, instantiates the entities…all the good stuff. I’m not going to fully explain the whole file here (maybe I’ll do so some other time) but it’s not too complicated; you’ll especially want to take a look at the function loadObject.

But while the file does already do a lot, it doesn’t handle paths by default, so I’ve had to add another function to it. Here it is:

public function getPathData(Obj:TiledObject):FlxPath{
  var name = Obj.name;

  for (o in entityLayer.objects){
    if (o.objectType == TiledObject.POLYLINE && o.name == name){
      var points = o.points;
      for (point in points){
        //Factor in starting position
        point.x += o.x;
        point.y += o.y;
      }

      return new FlxPath(points);
    }
  }
  return null;
}

This function is dependent on some other custom code I’m using but the general idea behind it is rather simple. You pass it the TiledObject you want to attach a path to. The function then loops through all objects in an entity layer (which I have saved in a variable called entityLayer for convenience) until it finds one of the type TiledObject.POLYLINE. If the name of that Polyline matches the object name, it gets the points of that line, then uses them to create and return a new FlxPath you can assign to your object’s path property. For all of this to work, both the object and the Polyline need to have the same name inside the Tiled editor though (more on that in a second).

As you can see, HaxeFlixel again handles a lot of the heavy lifting for us. The classes in flixel.addons.editors.tiled contain everything we need to create our function: It can identify Tiled objects by type and parse the node coordinates of a Polyline, so all we need to do is assign those coordinates to our object. Let’s look at an example!

An Example

So here we have a simple level with three objects: The player, the exit, and an enemy. The enemy is the only entity that will interest us right now. As you can see, the enemy is of the type sparkball and has the name Steve.

editor.png

Now we grab our Polyline tool and draw a path on the same entity layer that Steve is on…

drawpath.png

…and finally name the path Steve as well. Our level now looks like this:

namesdone.png

And that’s it from the editor side! Now let’s jump into TiledLevel.hx and put it all together. In loadObject (or your equivalent) do something like this:

switch(o.type.toLowerCase()){
  case "sparkball":
    var p:FlxPath = getPathData(o);
    if (p == null) throw "No path found for " + o.name;
    var b:Sparkball = new Sparkball(x, y, p);
    //Now add the Sparkball to the stage, etc.
}

This uses our getPathData function we defined above to get a FlxPath, then pass it into the constructor of our Sparkball. Of course the constructor will have to take a FlxPath as an argument, so your entity’s constructor might look a little something like this:


public function new(?X:Float=0, ?Y:Float=0, Path:FlxPath) {
  super(X, Y);
  path = Path;
  path.start(null, 100, FlxPath.LOOP_FORWARD);
}

And that is pretty much all you need; if everything went well, you should end up with this once you start the level in the game:

tutresult.gif

I hope this post was somewhat helpful. If you have any comments or questions, feel free to post them in the comments below!

Join my Discord Server!

Hang out, get news, be excellent!

Come hang out!

Want To Buy Me a Coffee?

Coffee rules, and it keeps me going! I'll take beer too, though.

Related Posts

From Quackshot to Speer

POST | | #Ramblings #Retro #Mega Drive #Speer

Back when I got my Mega Drive in 1991 I got two games along with it: Sonic the Hedgehog and Castle of Illusion. Not only did these excellent titles provide hours of fun for me and my dad, they also left a huge impression on me. Through their sheer quality, these games became my taste in games. I loved the platforming, the colors, the charm, the easy to grasp mechanics and how they were simply fun.

Setting Up SGDK in Visual Studio Code (With Auto-Complete)

POST | | #SGDK #Tutorial #VSCode

As I mentioned in part 2 of Adventures in Mega Drive Coding I set up Code::Blocks as my IDE for Mega Drive development. And Code::Blocks is great, but I still had two issues with it. First of all, auto-complete didn’t really work. Considering I’m pretty much poking around the SGDK API blindly, that wasn’t helpful. And secondly…I use VSCode for pretty much anything else coding-related, so it would be nice to also use it for SGDK stuff.

Using Shoebox With MonoGame

POST | | #Tutorial #MonoGame

If you’ve worked with spritesheets at any point you might have worked with tools like TexturePacker or Shoebox, which can make creating and managing spritesheets a lot easier. I’ve actually not used them until now, but since I’m experimenting with a new framework (MonoGame) I thought I might as well give them a try! However, while TexturePacker has built-in MonoGame support you can only access it in the paid version. Shoebox is completely free, but doesn’t offer support for MonoGame out of the box…but luckily you can modify the output parameters to enable that support!

By using the Disqus service you confirm that you have read and agreed to the privacy policy.

comments powered by Disqus