Yukkuri Games

Python BulletML

BulletML is the Bullet Markup Language. BulletML can describe the barrage of bullets in shooting games. (For example Progear, Psyvariar, Gigawing2, G DARIUS, XEVIOUS, ...) This module parses and executes BulletML scripts in Python. All data structures in it are renderer-agnostic. A sample renderer for Pygame is included. The full API documentation is contained in its Python docstrings.

More information about BulletML is available at the BulletML homepage.

In addition to the standard BulletML XML format, this module supports an equivalent YAML format. For convenience, two simple collision routines are provided, bulletml.overlaps for stationary circles and bulletml.collides for moving circles.

API Example

from bulletml import Bullet, BulletML

doc = BulletML.FromDocument(open("test.xml", "rU"))
player = ...  # On your own here, but it needs x and y fields.
rank = 0.5    # Player difficulty, 0 to 1

bullet = Bullet.FromDocument(doc, x, y, target=player, rank=rank)
bullets = [bullet]
...
for bullet in bullets:
    bullets.extend(bullet.step()) # step() returns new Bullets
...

For drawing, you're on your own, but Bullet instances have a number of attributes that can be used to influence it.

BulletYAML

BulletYAML is a translation of BulletML into YAML. The structure is mostly the same as the XML version, except BulletRef/FireRef/ActionRef elements are only used if they contain parameters, as YAML has its own intra-document references. Parameterless references are turned into direct YAML references.

If PyYAML is installed, importing this module automatically registers BulletYAML tags with the default loader and dumper.

YAML Example

!BulletML
  type: vertical
  actions:
    - !ActionDef
      actions:
      - !FireDef
    bullet: !BulletDef {}

FAQ

Can I use bulletml with pyglet / PyOGRE / Panda3d / etc?
Yes. BulletML works on abstract data structures that have x/y/speed/direction fields. Depending on your renderer, you can either use these directly to render, or feed the simulation data output by BulletML into a physics controller or scenegraph.
What Python version is required?
Python 2.6 or later is required; this includes 3.x (although the sample runner will not work without a functional Pygame for Python 3).
How stable is the API?
As of version 2, I expect the API to be stable enough to use for your game, including adding new kinds of actions or subclassing Bullet. However, I still recommend including the entire module with your game if possible.
How fast is is the module?
This depends on what features you are using. There used to be a table here explaining it, but starting in version 2 it varies even more depending on the bullet definition used. If you're stuck with pure-Python collision detection and no Pysco, you might get as low as 100 per frame; if you've got _collision.so and Psyco available, you can get over 2000.
My BulletML file doesn't load / play correctly in this module!
Send us an email and attach your script.
My BulletML plays fine in this module, but nowhere else!
This module has several major extensions to the BulletML format, as well as a much more lax parser. If you're using the extensions you're probably SOL, but you may just need to clean up your XML elements a bit.
Can I output BulletML?
No. Some data about the XML structure is lost in the loading process. You can output BulletYAML however, or anything else supporting the standard Python serialization interface.

Spec Deviations

XML Validation
This module does absolutely no XML validation. Element order doesn't matter, unknown elements are completely ignored, and when duplicate elements are found the most-recently-parsed one is used.
Direction Frame Counts
The BulletML reference implementation contains code which makes a changeDirection element with a term of 1 do nothing. In this implementation, it causes a change of direction during the next frame. This change had no effect on the reference samples. Kenta Cho has confirmed this is a bug in the reference implementation.
Firing Speed

The reference implementation, when creating a bullet with <speed type="relative">, uses the previous fire speed, and not the source bullet's speed. This module instead uses the speed of the source bullet for relative, and the previous fire speed for sequence. This change had no effect on the reference samples. Kenta Cho has confirmed this is a bug in the reference implementation.

Extensions

This module contains some extensions to the BulletML element set. These use a separate XML namespace, http://code.google.com/p/python-bulletml/. Sample documents that use them begin as follows:

<bulletml xmlns="http://www.asahi-net.or.jp/~cs8k-cyu/bulletml"
          xmlns:py="http://code.google.com/p/python-bulletml/">

These extensions do make your document invalid BulletML, and they may not parse correctly in other parsers. (For example, the reference implementation applet will not read them.)

0 frame times

The reference applet does not correctly handle changeDirection, changeSpeed, or accel elements if they have terms of 0 (it ends up dividing by zero). This module instead applies the change immediately if the type is relative, absolute, or aim, and does nothing if the type is sequence. A 0 in the reference applet and in this implementation behave the same - it stops evaluation of the action immediately, but advances it again next frame (as opposed to 1, which stops it immediately, waits the next, then advances the next).

<offset> - within <fire>
Attribute
label - STRING
Contents
x?, y?
Example
<offset type="absolute">
  <x>$rand*3</x>
  <y>-5</y>
</offset>

The <offset> element can be used to give a positional offset to a newly-fired bullet. Normally, bullets appear at the same location as their parent bullet. Using offset, these can either be moved around in absolute space, or relative to the parent's direction.

When using a relative direction, positive x is to the "right" of the source's facing, and positive y is "in front of" the source.

<tag> and <untag> - within <action>
Contents
STRING
Example
<action>
  <tag>new</tag>
  <wait>20</wait>
  <untag>new</untag>
</action>

<tag> sets a simple string tag on the bullet running this action. <untag> removes a tag. Setting the same tag twice or trying to remove a tag that is not set has no effect.

Bullet tags are implemented as Python set object in the tags attribute of the Bullet class.

<appearance> within <action>
Contents
STRING
Example
<appearance>pewpew</appearance>

<appearance> sets the appearance attribute on the Bullet instance. The effect of this is game-dependent, but the intended use is to set a texture, color, or animation sequence for the bullet.

<tag> and <appearance> within <bullet> and <fire>
Like <action>, <bullet> and <fire> can contain <tag> and <appearance> elements. This causes the bullet to be created with those tags and an appearance already set.
<if>, <cond>, <then>, <else>
Contents
<cond>STRING</cond>
<then>action | actionRef</then>
[ <else>action | actionRef</else> ]

Conditional actions are supported. These have one of two forms:

<!-- Fire a bullet 70% of the time. -->
<if><cond>$rand > 0.3</cond>
  <then><fire><bullet/></fire></then>
</if>

<!-- Randomly fire red or blue bullets. -->
<if><cond>$rand > 0.5</cond>
  <then><fire>
     <bullet/><appearance>red</appearance>
  </fire></then>
  <else><fire>
    <bullet/><appearance>blue</appearance>
  </fire></else>
</if>

If the conditional has no else branch, execution proceeds immediately to the next instruction of the action containing the conditional. Otherwise, as <action>, <repeat>, etc., the first step of the subaction is executed.

License

All example BulletML files in the examples folder are released into the public domain. The BulletML specification is the work of Kenta Cho.