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.

Friday, February 28, 2014

Tightly Controlled Physics Forces in Unity




This post pertains to an ongoing struggle of mine.

Unity has a character controller. This is very useful if you want to precisely control a character through movement deltas.  Unity also has a fully-fledged physics system, which is very useful if you want to control a character with physically-simulated forces and have it interact realistically with the world.

Unfortunately, this is an either-or situation. As useful as it would be, you can't apply a direct movement delta to a rigid body, or apply a knockback force to a character controller. This comes up more often than you might think. Any game with knock-back from explosions ("rocket jumping") uses a mix of physics simulation and direct-driven character movement, in general.

I'm working on a prototype (currently in the sort of limbo where it may never see any sort of public release) of a physics-heavy FPS where every weapon has different movement tricks attached to it. (Similar in concept to the traditional rocket jump, but obviously very different for each weapon.) I needed to be able to do this.

Unfortunately, I still kind of can't. I built three (well, two and a half) controllers, and while two of them work to some extend, neither of them work as well as I need them to, which is why I'm temporary shelving the prototype.

My first controller tried to precisely control a rigidbody using physics forces.

 if (rigidbody.velocity.HorizontalComponent().sqrMagnitude < speed * speed)  
           {  
                rigidbody.AddForce(movement - rigidbody.velocity.HorizontalComponent(), ForceMode.VelocityChange);  
           }  
           else  
           {  
                Debug.Log(influenceMove * Time.fixedDeltaTime);  
                rigidbody.velocity.Influence(influenceMove * Time.fixedDeltaTime);  
           }  
           if (Input.GetKeyDown(KeyCode.Space))  
                rigidbody.AddForce(Vector3.up * jumpPower, ForceMode.VelocityChange);  

This immediately ran into some problems. I still didn't have precise control, and although that could be managed, the controller was jumpy on some slopes, and the constant application of force let the player stick to near-vertical walls.  This is more or less the solution I had used in the past, it's acceptable for much simpler applications, but it simply didn't meet my needs here.

My second (attempted) controller tried to basically re-implement a primitive version of Unity's character controller using CapsuleCast and SweepTest to check collisions.

 RaycastHit[] hits;  
           hits = rigidbody.SweepTestAll(movement);  
           float hitDistance = Mathf.Infinity;  
           foreach (RaycastHit hit in hits)  
           {  
                Debug.Log(hit.collider.gameObject.name + " at " + hit.distance);  
                if (hit.distance < hitDistance)  
                     hitDistance = hit.distance;  
           }  
           //Debug.Log(hitDistance);  
           Debug.Log("Moving " + movement.magnitude * Time.deltaTime);  
           if (hits.Length == 0 || hitDistance > movement.magnitude * Time.deltaTime)  
           {  
                Debug.Log("FullMove");  
                transform.Translate(movement * Time.deltaTime);  
           }  
           else  
           {  
                Debug.Log("TruncatedMove");  
                transform.Translate(movement.normalized * hitDistance);  
           }  

 Unfortunately, these Unity functions don't work quite as advertised. Rather than checking if my capsule will collide with anything along the ray that represents the sweep path, SweepTest and CapsuleCast just cast a single ray from the center of the capsule to the destination point, and if the ray is interrupted, it adjusts the hit point so that the capsule will touch the object that was hit, rather than being stuck in it. This isn't a useless function, but it doesn't do what I need it to here, and as far as I could tell after several hours in the Unity documentation, there is no function that does what I need, which left this approach dead in the water.  It's certainly possible to do, but it isn't easy enough to do in two weeks.

My third controller was the closest to being successful. Instead of trying to build a character controller around rigidbody physics, I tried to build primitive rigidbody physics around a character controller.
 public class PhysicsAccumulator : MonoBehaviour {  
      CharacterController controller;  
      Dictionary<string, PhysicsForce> forces = new Dictionary<string,PhysicsForce>();  
      public PhysicsForce Force(string name)  
      {  
           if (ForceExists(name))  
                return forces[name];  
           else  
                return null;  
      }  
      public bool ForceExists(string name)  
      {  
           if(forces.ContainsKey(name))  
                return true;  
           else  
                return false;  
      }  
      public bool AddForce(string name, PhysicsForce force)  
      {  
           if (ForceExists(name))  
                return false;  
           forces[name] = force;  
           return true;  
      }  
      public bool RemoveForce(string name)  
      {  
           if (!ForceExists(name))  
                return false;  
           forces.Remove(name);  
           return true;  
      }  
      // Use this for initialization  
      void Start () {  
           controller = GetComponent<CharacterController>();  
           forces.Add("Controller", new ControllerForce(controller, 4));  
           forces.Add("BunnyHop", new JumpForce(controller, 7f, 5, 10));  
           forces["BunnyHop"].FlatDamping = 3;  
           forces.Add("Gravity", new VectorLockedForce());  
           forces["Gravity"].Acceleration = new Vector3(0, -15f, 0);  
           ((VectorLockedForce)forces["Gravity"]).LockDirection = Vector3.down;  
           forces.Add("Rocket", new PhysicsForce());  
           forces["Rocket"].LinearDamping = 1;  
           forces["Rocket"].Aerial = true;  
      }  
      // Update is called once per frame  
      void LateUpdate () {  
           foreach (KeyValuePair<string, PhysicsForce> f in forces)  
                f.Value.Update(Time.deltaTime);  
           Vector3 netForce = Vector3.zero;  
           foreach (KeyValuePair<string, PhysicsForce> f in forces)  
                netForce += f.Value.Displacement;  
           controller.Move(netForce);  
      }  
      void OnControllerColliderHit(ControllerColliderHit hit)  
      {  
           foreach (KeyValuePair<string, PhysicsForce> f in forces)  
           {  
                f.Value.HandleHit(hit);  
           }  
      }  
 }  

I built a simple physics force registry and set it up with an array of forces representing both my "control" forces for movement and jumping and various environmental forces for things like gravity and rocket knockback. This worked perfectly until I tried to implement spherical planet gravity for the player. Character controllers in Unity have to be axis-aligned, which makes using them on a sphere impossible. This wasn't technically something I felt I needed going in, but the point of this experiment was to find a way to avoid being constrained by the limitations of these two systems in the future, and this is at least one limitation I have yet to overcome.

I think my next attempt, whenever that might be, will probably involve attempting to solve a problem I encountered before any of the controllers mentioned here. It is possible to drive a rigidbody's velocity directly, but it effectively overrides any meaningful physics motion. (You can't, for example, drive a vehicle off of a jump believably if you reset it's velocity every physics frame.) There might be a better approach to this method, but that's a task for the future.