Saturday, April 19, 2014

Interpretted Scripting in Unity

I spent a little while earlier this year trying to devise a good way to handle attack scripting in Phantom Mansion in an extensible, designer-friendly, mod-friendly way.

I decided to look into interpreting scripts at run-time. This would put a little bit more faith in the people who will use this system than I generally like to, but I think it compares favorably with the other options, where I either have to anticipate everything this tool might be used for or be on-call 24/7 for the lifetime of the project implementing tiny changes because a designer wanted, say, a slightly different targeting behavior on an attack.

I made some progress on this recently. There's a wonderful library called Jurassic that allows a C# program to compile and execute Javascript code at run-time. You can expose C# functions and objects to Javascript with a little boilerplate code, which isn't too much work, and this keeps it properly encapsulated.

Initialization looks like this:

 ScriptEngine engine; 

 engine = new ScriptEngine(); 

 engine.EnableExposedClrTypes = true; 

To register a function, the code looks like this:

 engine.SetGlobalFunction("SetState", new System.Action<string>(jsSetState)); 

 #region JS Functions 
      public void jsSetState(string newState) 
      { 
           state = newState; 
      } 
 #endregion  

The jsSetState function is a C# function that sets a C#-side member variable string called State. The engine.SetGlobalFunction call hooks that function to a JS-side function called SetState. We can call that function with Engine.Execute(); which executes an arbitrary string as Javascript code. This code will set the state variable to "This is the new state string."

 engine.Execute("SetState("This is the new state string.");"); 

There's still a lot of work to do on this system. Right now the extent of the supporting architecture for it is a very simple state machine setup, which is what I intend to base the attack system on, but it's very rudimentary right now. I need to write a lot more gameplay hooks, and probably do about seven polish passes to make this really usable. Unfortunately, all of those gameplay hooks are in a really grizzly part of the Phantom Mansion code that I'm never going to touch again unless I'm rewriting it, and rewriting the entire attack system of Phantom Mansion was a little beyond scope. This was, after all, primarily a research/experimentation project.

I think I've found something that can, one day, be the foundation of a really good attack scripting system for Phantom Mansion, but we've got a long road ahead of us, and I am very, very tired.

Building a Fight Stick

I just built a (prototype of) a fight stick.








It was a process.


For a while, everything went off without a hitch. The controller is built around an Arduino Leonardo, which has a couple of very important features. First, the new AVR microcontroller embedded in it supports USB communication directly, without the need for a second chip. This means that the Leonardo can act as a USB host and emulate Human Interface Devices natively, given the right software support. (You can do this with older Arduinos, but you need to reprogram the USB communication chip that is usually used for programming the Arduino. So every time you need to change your code, you need to flash the original software to the communications chip, use that to program the Arduino with your new code, and then re-flash the communications chip with your modified, HID-emulation bootloader, which is a really awkward hack.)



The controller uses Sanwa pushbuttons, which are pretty standard, and started off with a rare, complex joystick called the Ultrastik 360. The Ultrastik is a magnetic analog stick in an arcade joystick form factor.

The most interesting part of this project was the physical act of building the stick. Stripping wires, crimping connectors, cutting holes in cardboard boxes with a dull X-Acto Knife... There's something about a new wiring harness that makes me really happy, I can't quite explain why.


The actual circuitry is pretty simple. Every button or directional input has a pin on the Arduino. These pins are connected to power through a small resistor ("pulled high") and connected to a button, which is connected to ground. This means that, when the button is pressed, the input pin is connected to ground with less resistance than to power, the pin reads low, and we know our button is pressed.


The software problems are significant, but fortunately I didn't have to solve any of them. Writing a new HID Report Descriptor, and the drivers attached to that, is a tremendous amount of very low-level, technical work. Fortunately, the kind people over at the UnoJoy! Project have done it all for me, and it's all open-source, well-documented, and performant. Give them all your pageviews.

I'm still fortunate in that I didn't have to make any modifications to the HID Report Descriptor they gave me. 13 buttons, a D-Pad, and six analog axes as a hard limit could cause trouble for some people, but it was plenty for me.

My first hitch came when it was time to wire up the Ultrastik. It's a very sophisticated piece of hardware, capable of appearing as a Joystick in windows on it's own. It has a nine-pin communication header, which can either be used to read eight buttons (and communicate their state to Windows), or to output four digital values (as an eight-way stick) and two analog values (as an analog stick) to an external source. The ultrastik autodetects which mode to enter when it powers on, based on whether the digital output lines are pulled high or not. Unfortunately, this didn't work, for reasons I still haven't determined. Current theories are:

1. My Arduino isn't pushing quite enough power to the Ultrastik, and I should probably invest in a decent multimeter.

2. I have a bad Ultrastik. I don't think this is the case, but it's a possibility I haven't been able to rule out entirely.



In any case, I couldn't get the UltraStik into output mode. I spent a while talking to Andy at Ultrastik, he was very responsive and helpful, a real pleasure to work with. (As far as I can tell, Andy is UltraStik; they're modified Sanwa joysticks, and it's a very niche market, so I believe he makes them more-or-less by hand.) I sent him some questions, and he gave me more detailed information about the automatic mode selection. When I still had problems, he sent me modified Ultrastik firmware to force the stick into Output mode. Unfortunately, after I installed it, it bricked my stick, and I'm sending it back to Ultrastik for repairs. It's an unfortunate situation, and I feel a little bad writing it here. If someone finds this while researching Ultrastik: I actually recommend it. The hardware is great, it worked well out of the box for most applications (mine is very fringe, most people will use Input Mode) and for people like me with atypical applications, Andy's very helpful. This problem isn't yet resolved, and has run into complications, but I firmly believe that given more time we will, or at least could, make it work.

Unfortunately, I ran out of time. It's the end of the year, and I have a stick that doesn't work. Fortunately, one of my friends, who was assisting with this project, found an old Atari controller in a box in his roommate's closet. These controllers aren't very nice to handle, but they're great to work with. I still mourn the demise of parallel interfaces; they're so intuitively hackable. In any case, wiring up an Atari controller is super simple, and provided some semblance of functionality. You can play flash games with Joy2Key. You can play Super Meat Boy, but it's hard. I can't condone Skullgirls, the Atari stick is just too sticky.


 Here's an awkward video that didn't embed quite right. It's very late, I'm sorry.