Path Movement in Speer

HaxeFlixel Speer Tutorial

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!

HaxeFlixel Tutorial: Single Separation Collisions

November 3, 2017
HaxeFlixel Speer Tutorial

Hanging on Balloons

October 25, 2017
HaxeFlixel Speer Tutorial
comments powered by Disqus