Pattern/Enemy Script, How To?

This forum is for discussing shmup development, tools, engines and techniques.
User avatar
Posts: 12
Joined: Tue Jul 31, 2012 7:46 pm

Pattern/Enemy Script, How To?

Postby XYX » Wed Oct 17, 2012 6:17 pm

I was wondering how all you guys create your patterns and shooting scripts for enemies?
Im thinking something along the lines of Danmakufu that uses scripts, so a script might contain something like:

ring(20,10,0,false,0) fire ring of 20 shots, 10 times, no angle mod, dont change num, no num mod
arc(10,10,p,0,true,1) fire arc of 10 shots, target player, 0 ang mod, do reduce num each round, reduce num by one

my main question is how do you go about having these in a text file and parsing them to your game? i need lots and lots, each to be chosen randomly(ish) do i store them all as some kind of object?

Thanks guys :)

User avatar
Posts: 34
Joined: Sun Jul 29, 2012 4:44 pm

Re: Pattern/Enemy Script, How To?

Postby kdmiller3 » Thu Oct 18, 2012 10:19 pm

To answer your initial question, I store everything in XML documents and parse them with TinyXML-2.

You might want to start by doing everything in code instead of trying to parse external data files. You'll need to create game logic and data structures (or classes) anyway so you might as well start there. :)

User avatar
Posts: 12
Joined: Tue Jul 31, 2012 7:46 pm

Re: Pattern/Enemy Script, How To?

Postby XYX » Sat Oct 20, 2012 2:00 pm

Thanks for the link, you use case statements or something when parsing the XML?
Like
Code: Select all
switch(type) {
case shot.ring:
case shot.spiral;
..etc
}


I have got the classes done in various prototypes, im copying them over and recoding them at the moment

Im looking for info on doing this so once ive redone my renderer and mesh generators i can implement some patterns since i have the triggers ready for the AI now

What sort of base types do you have for your patterns/scripts?

User avatar
Posts: 34
Joined: Sun Jul 29, 2012 4:44 pm

Re: Pattern/Enemy Script, How To?

Postby kdmiller3 » Sun Oct 21, 2012 7:00 am

You can't do it that way in C or C++, unfortunately.

While you can do a bunch of string compares, what I do is compute the hash of the type string and do a switch based on that. I use FNV-1a on my project because it's simple but you can use CRC32 or other hash functions. I made a little program that takes its input and spits out the hash value in hexadecimal with the original string as a comment. I end up with a bunch of stuff that looks like this:

Code: Select all
   // process child elements
   for (const tinyxml2::XMLElement *child = element->FirstChildElement(); child != NULL; child = child->NextSiblingElement())
   {
      switch (Hash(child->Value()))
      {
      case 0x14c8d3ca /* "offset" */:
         ...

      case 0xcab7a341 /* "scatter" */:
         ...

      case 0xca04efe0 /* "inherit" */:
         ...

      case 0x32741c32 /* "velocity" */:
         ...

      case 0x0dd0b0be /* "variance" */:
         ...

      case 0x3a224d98 /* "spawn" */:
         ...
      }
   }


I don't do particularly complex shot patterns or bullet movements, mostly just bursts and spreads with projectiles moving in straight lines. Weapons support action scripting with wait, repeat, recoil, flash, ordnance, cue, and loop commands. Ordnance doesn't support actions yet since I never had a need. Here's the entire XML document for a burst-firing player weapon (with "playerbombbullet" and "playerweaponflash" being separate documents):

Code: Select all
<?xml version="1.0" ?>
<template name="playershipweapon3">
   <weapon>
      <action>
         <ordnance name="playerbombbullet">
            <position x="6" y="0" angle="0"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="6" y="-1"/>
         </flash>
         <ordnance name="playerbombbullet">
            <position x="-6" y="0" angle="0"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="-6" y="-1"/>
         </flash>

         <ordnance name="playerbullet">
            <position x="2" y="2" angle="0"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="2" y="2" angle="0"/>
         </flash>
         <ordnance name="playerbullet">
            <position x="-2" y="2" angle="0"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="-2" y="2" angle="0"/>
         </flash>
         <cue name="fire"/>

         <wait value="0.05"/>
         
         <ordnance name="playerbombbullet">
            <position x="6" y="-1" angle="-5"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="6" y="-1"/>
         </flash>
         <ordnance name="playerbombbullet">
            <position x="-6" y="-1" angle="5"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="-6" y="-1"/>
         </flash>

         <ordnance name="playerbullet">
            <position x="2" y="2" angle="0"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="2" y="2" angle="0"/>
         </flash>
         <ordnance name="playerbullet">
            <position x="-2" y="2" angle="0"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="-2" y="2" angle="0"/>
         </flash>
         <cue name="fire"/>

         <wait value="0.05"/>

         <ordnance name="playerbombbullet">
            <position x="6" y="-1" angle="-10"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="6" y="-1"/>
         </flash>
         <ordnance name="playerbombbullet">
            <position x="-6" y="-1" angle="10"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="-6" y="-1"/>
         </flash>

         <ordnance name="playerbullet">
            <position x="2" y="2" angle="0"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="2" y="2" angle="0"/>
         </flash>
         <ordnance name="playerbullet">
            <position x="-2" y="2" angle="0"/>
            <velocity x="0" y="480"/>
         </ordnance>
         <flash name="playerweaponflash">
            <position x="-2" y="2" angle="0"/>
         </flash>
         <cue name="fire"/>
         
         <wait value="0.05"/>
         
      </action>
   </weapon>
   <sound volume="0.25">
      <file name="robotron-playershot.wav" />
   </sound>
   <soundcue>
      <cue name="fire" sound="playershipweapon3" />
   </soundcue>
</template>

User avatar
Posts: 34
Joined: Sun Jul 29, 2012 4:44 pm

Re: Pattern/Enemy Script, How To?

Postby kdmiller3 » Mon Oct 22, 2012 12:25 am

If you want to go the hash route, here's the functions I use. The string one is case-insensitive though you don't have to do it that way.

Code: Select all
// fnv-1a hash
inline unsigned int Hash(const unsigned char byte, unsigned int hash = 2166136261u)
{
   hash ^= byte;
   hash *= 16777619u;
   return hash;
}
inline unsigned int Hash(const void *data, size_t len, unsigned int hash = 2166136261u)
{
   const unsigned char * d = static_cast<const unsigned char *>(data);
   const unsigned char * const e = d + len;
   while (d < e)
      hash = Hash(*d++, hash);
   return hash;
}
inline unsigned int Hash(const char *string, unsigned int hash = 2166136261u)
{
   if (string == 0)
      return 0;
   for (const char *s = string; *s != 0; ++s)
      hash = Hash(unsigned char(tolower(*s)), hash);
   return hash;
}


FNV-1a has the nice property that you can use the hash of a string as the seed for another hash and get the hash of the concatenated strings; Hash(".bar", Hash("foo")) produces the same result as Hash("foo.bar"). That can occasionally be useful.

(It also doesn't need any lookup tables the way most implementations of CRC32 do; it works a lot like a linear congruential random number generator so it's the kind of thing you could write down on a cocktail napkin.)

User avatar
Posts: 12
Joined: Tue Jul 31, 2012 7:46 pm

Re: Pattern/Enemy Script, How To?

Postby XYX » Thu Oct 25, 2012 9:46 pm

thanks for this VERY useful info, i tried looking at converting a string to hex values, i cant seem to get it formatted like yours so i decided to maybe use the string hashcodes instead? (java seems to have a DataType.getHashcode() method that returns an int)

unfortunately im still getting my renderer to work when generating ships, ive just finished the bullet managers classes. This xml stuff is going to link in with that and the 'behaviors' which are up next :)

User avatar
Posts: 34
Joined: Sun Jul 29, 2012 4:44 pm

Re: Pattern/Enemy Script, How To?

Postby kdmiller3 » Fri Oct 26, 2012 3:58 pm

I wrote a program to do it since I use those hexcodes everywhere. I set it as an external tool in the IDE that takes the selected text as input, runs the program, dumps result to the Output pane. I can then copy and paste it from there.

Code: Select all
int main(int argc, const char* argv[])
{
        for (int i = 1; i < argc; i++)
        {
                printf("%s0x%08x /* \"%s\" */", i > 1 ? "\n" : "", Hash(argv[i]), argv[i]);
        }
        return 0;
}

User avatar
Posts: 12
Joined: Tue Jul 31, 2012 7:46 pm

Re: Pattern/Enemy Script, How To?

Postby XYX » Sat Oct 27, 2012 1:42 pm

we dont have that magic Hash() function in java, ive tried suggestions from here http://stackoverflow.com/questions/4159 ... sh-in-java but they return values like fade44533c5ef08184c96d779853d545 for "enemyshot"

i dont want to add LOTS of code for this, since ideally itd be a nice little class like yours, any suggestions?

edit//
completely missed your hash() function this time round, trying again and ill report back ;)

edit2//
any chance you can post the rough java equivalent to your Hash() methods? no idea what half of it means :(


edit3//
using the following:
Code: Select all
String ss = "offset";
      int h = ss.hashCode();
      System.out.println(ss + " - " + Integer.toHexString(h));


your examples
case 0x14c8d3ca /* "offset" */:
case 0xcab7a341 /* "scatter" */:
case 0xca04efe0 /* "inherit" */:
case 0x32741c32 /* "velocity" */:
case 0x0dd0b0be /* "variance" */:
case 0x3a224d98 /* "spawn" */:

become:
offset - c3376493
scatter - 71e9c6be
inherit - 740c90fb
velocity - 7f363cdd
variance - b584fe71
spawn - 688f37b

might this do for 3 lines of code??

User avatar
Posts: 6
Joined: Fri Jul 27, 2012 12:30 am
Location: Blasting off and striking the evil Bydo empire!

Re: Pattern/Enemy Script, How To?

Postby berilium » Sat Oct 27, 2012 5:03 pm

Hi

XYX wrote:edit2//
any chance you can post the rough java equivalent to your Hash() methods? no idea what half of it means :(


Wikipedia has an article about fnv-1 hash with some pseudocode. It might help?
http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash


I think Java's hashCode() is enough. You want unique hashes and the function fast enough for your requirements. It doesn't matter if the hashes are different from kdmiller3's ones.

User avatar
Posts: 34
Joined: Sun Jul 29, 2012 4:44 pm

Re: Pattern/Enemy Script, How To?

Postby kdmiller3 » Sun Oct 28, 2012 3:51 am

I haven't worked with Java enough to provide a working Java implementation, but I can at least explain what it's doing.

Start with a seed hash value (unsigned integer 2166136261 by default)
For each byte of the input:
  • exclusive-or the hash value with the input byte
  • multiply the hash value by unsigned integer 16777619

Java doesn't have unsigned types so FNV-1a is tricky to implement; you'd need to use 64-bit integers (long) and mask off the low 32 bits (& 0xFFFFFFFF) each time. When it comes down to it, the concept is what matters, not the actual hash function you use. Java's string type hashCode function should be perfectly adequate.

Return to Development

Who is online

Users browsing this forum: No registered users and 1 guest