Game Development Community

BulletML for Torque X 2D

by Christopher Perkins · in Torque X 2D · 08/19/2010 (8:19 am) · 3 replies

The past two nights I've been toiling to make BulletML work for Torque X. It's been eating into my sleep, but my Shmup needs it, so I do. Anywho, I'm releasing my modified implementation.

A few notes. BulletML was originally developped by Kenta Cho from Aba Games. The original website is here.

I did not do most of the port to C#, that was Keiichi Kashihara who did so. You can find his implementation here(site is only in japanese).

I took Kashihara's implementation and changed it to better suit Torque. Instead of it being a function of frames, I made it a function of time. Instead of it directly controlling the bullet's position, I made it control it's velocity and direction, so that our physics engine would do the legwork. My modifications aren't fully done as I feel I can optimize his work some more, so I plan on updating this sometime in the future.

I also implemented a Content Importer for it. BulletML scripts take a notoriously long time to parse(they are simple, but the data tree they produce is far from simple), so I made it parse and compile it at compile time to save on loading times.

So, here is my implementation

Things you have to do to work it:
Add a reference to your specific library(BulletML.dll or BulletML(Xbox).dll depending on what type of project it is). Add a reference to your Content Importer to BMLProcessorLib.dll. BMLProcessorLib.dll has to be in the same folder as BulletML.dll as it references it.

After that, you need to set your namespace reference in the code you are going to use BulletML with to:
using FriedGinger.ShmupFramework.BulletML;

Finally, you need to call BulletML.Init(IBulletMLManager imanager,ContentManager content). So you pass it your Game.Instance.Content and a class that inherits IBulletMLManager. The interface is as follows:
public interface IBulletMLManager
    {
        /// <summary>
        /// Gets a random number
        /// </summary>
        /// <returns>A random float from 0->1</returns>
        float GetRandom();

        /// <summary>
        /// Gets the Difficulty level
        /// </summary>
        /// <returns>Level from 0-?</returns>
        float GetRank();

        /// <summary>
        /// Gets a players X position.
        /// </summary>
        /// <param name="ship">Ship to check(will most of the time be 0 unless Target is changed)</param>
        /// <returns>Ships X position</returns>
        float GetShipPosX(int ship);

        /// <summary>
        /// Gets a players Y position.
        /// </summary>
        /// <param name="ship">Ship to check(will most of the time be 0 unless Target is changed)</param>
        /// <returns>Ships Y position</returns>
        float GetShipPosY(int ship);
    }

Anywho, once you've done that, you can now load and play bullet effects. So, let's learn how to do that.

Every bullet and every emitter must inherit IBulletMLBulletInterface. The interface is as follows:
public interface IBulletMLBulletInterface
        {
            Vector2 Position { get;}
            Vector2 Velocity { get; set; }
            float Dir { get; set; }
            int Target { get; set; }
            void Vanish();
            BulletMLBullet GetNewBullet();
        }
Emitters dont have to do anything for the sets(just override em with nothing). Vanish is what to do when a bullet has to vanish(emitter can just return). GetNewBullet is the code to spawn a new bullet.

Every bullet and every emitter must also have their own public BulletMLBullet field. It's how the code is run. On bullet's you pass their BulletMLBullet field when you create them with the GetNewBullet.

On an emitter you must pass the topTree of a bml file. To do so, you must load it and feed it to the BulletMLBullet. Code is something like this:
BulletMLBullet myEmitterBMB = new BulletMLBullet();
myEmitterBMB.InitTop(BulletML.LoadTree(filename));

As you see, you load a script by using BulletML.LoadTree(filename), where filename is the relative path and file name, without the extension. You also must note that you must call InitTop each time you want to refire off the script.

Finally, you must run the script. To run a script, you simple need to call BulletMLBullet.Run(float dt). Dt is the time since you last called the script. You should call it all the time for the life a bullet and whenever you want for an emitter. The method will return true if it's finished the script. So if we were to run with the last example it would be like so:
myEmitterBMB.Run(dt);

I also included the source code and a sample in the rar file. It pretty much shows a couple example components and how to use it in the builder. Everyone is free to use the source however they please. I only ask that you don't steal my art in the sample for your games.

You can get sample scripts at the original BulletML website from his applet. You can save them to an .bml file and add them to your project. They must be edited though. Any speed tags need to be modified from pixels/frame to units/second and any wait or term tags need to be edited from frames to seconds. Anywho, that's really all I think I need to explain, so unless ya'll have any questions, I'm going to bed.

#1
08/19/2010 (10:53 pm)
So, I figure I should explain what BulletML is.

It's a markup language used to script out complex bullet patterns used in danmaku(bullet hell) shooters. It's not for your everyday shooter as the bullets are rather complex, being able to spawn more bullets, being able to change direction, and being able to change their speed at will. You can craft some horrifyingly beautiful patterns with this.

I've fixed the timing issue so now it doesn't have to have a fix time step. I've edited the first post with the new link. The run for each object does not have to be called every frame now. However depending on waits and terms of the script, higher time between frames= the less it's going to be like your script(it'll mostly be there, but just less tight). You'll want to work on an equilibrium of your own(aka it's preferable to call it every update, but it's ok if you don't).
#2
08/20/2010 (7:43 am)
Looks exciting! I played around with the demo on the BulletML website tonight and will try to set this up tomorrow.
#3
08/21/2010 (6:21 pm)
Oh, man! That is beautiful! Works great too. Now I just need to learn a bit more of BulletML.