Monday, May 27, 2013

Lerps of Faith


I've been stuck in a test level for weeks - weeks! I finally got something together that I'm willing to live with, but I still don't like it much. The simple-sounding problem was that I needed, in various places, to move an object from point A to point B, in some cases speeding up as it leaves A, in other cases slowing down as it approaches B. Thank Christ I didn't need both at the same time or I'd still be working on it.

Several game development environments support this sort of thing natively with easing functions, but Unity is apparently not among them. Seems like an odd thing to leave out, but the Unity philosophy seems to be that if people want something badly enough, some user will eventually code it up and offer it to everyone else, possibly for a profit, and for this use case one need look no further than iTween, or if one's pockets are empty one could conceivably go to the community and base a solution on something like MathFx.

Granted, there's also the option of hooking my objects up to Unity's animation system, but that felt like using a bazooka on a mosquito, and I'm not sure the kind of easing I want is easy to get to. I had a hunch that I could probably get close enough to what I needed without anything so complex. Turns out I was sort of right, the Lerp function took care of me on the slowing down side, but the speeding up side took a little more head-scratching. Below is what I came up with, you'd attach it to a cube or whatever and then flip the bool to switch between modes.

This was one of those weird little bottleneck problems that is pretty unimportant in the grand scheme of things, but somehow had the power to bring my project to a grinding halt because it felt like it ought to be easy, and not being able to solve it had cascading negative effects on my confidence and motivation. This solution still looks a little weird, but no weirder than anything else in my project, so I'm calling it good.

using UnityEngine;
using System.Collections;

public class test_smoothlerp : MonoBehaviour {

GameObject cube;
Vector3 beginPoint;
Vector3 endPoint;
float startTime;
float tripDist;
float acc;
bool speedUp = true;

void Start () {
cube = GameObject.Find("Cube");
print ("cube = " + cube);


tripDist = 150.0f;

beginPoint = cube.transform.position;
print ("beginPoint = " + beginPoint);
endPoint = new Vector3(cube.transform.position.x + tripDist, cube.transform.position.y,
cube.transform.position.z);
print ("endPoint = " + endPoint);

acc = 0.01f;
}


void Update () {
if(speedUp)
{
//speed up
if (acc < 1.0f)
{
acc += 0.01f;
}
Vector3 nextPos = new Vector3(cube.transform.position.x + (tripDist*acc), 0.0f, 0.0f);
print (nextPos);
if (nextPos.x < endPoint.x)
{
cube.transform.position = Vector3.Lerp(beginPoint, nextPos, Time.time);
}
else
{
print ("done");
}
}
else
{
//slow down
cube.transform.position = Vector3.Lerp(cube.transform.position, endPoint, Time.time);
}
}
}

Sunday, May 5, 2013

Float Downstream



Fear - fear gripped my heart in its clammy fist. After making a new WebPlayer build to replace the somewhat outdated one on my webpage, I fired it up and found that it didn't work. I realized I hadn't tried playing the game in an external build for quite some time. The game worked when played in the Unity Editor's play mode, and that should just be exactly the same as a built version, right? RIGHT??

The game's main menu and auxiliary screens seemed to work fine, but when starting a new game, the player would be stuck at the character input screen, as no amount of clicking would actually select a character and launch the first level. This worked perfectly fine in the editor. The setup is, a UI script that lives on the Main Camera in this scene Instantiates all three of the player prefabs at positions based on the screen size, and hangs a scipt called UI_dummyPlayer on each of them. That script just has an OnMouseDown event that fires whenever someone clicks on the GameObject that the script lives on, loading the next level with the appropriate character as the player.

A platform inconsistency is by nature a painful problem. I started looking for diagnostic tools. The first was the fact that you can right-click on your game in the WebPlayer and bring up a crude development console, pictured above, which in my case read

MethodAccessException: Attempt to access a private/protected method failed.

and helpfully pointed me to the very function causing the problem. The two potential culprits in there are the DontDestroyOnLoad call and my XML Serialization stuff... I started to get a sinking feeling. I quickly narrowed the problem down to these two lines:

seattle = City.Load(Path.Combine(Application.dataPath, "City.xml"));
seattle.Save(Path.Combine(Application.dataPath, "seattle.xml"));


and with some searching online I started to piece things together. Unity's WebPlayer has some built in security features that stop you from reaching into code that is stored in certain ways. I did a quick test on Path.Combine alone, as I hadn't used that before either, but the WebPlayer was fine with it. It was balking at my City.Load function, which I talked about in a previous post. It's another case of using some code I don't understand well, but I understand well enough that the WebPlayer is blocking me from access to the game's underlying file structure, which would most likely disallow things like

using (var stream = new FileStream(path, FileMode.Open))

and there are plenty of threads online about this and they all die out pretty quickly when a senior user steps in to say No, you can't do this in WebPlayer, it would in fact be a glaringly dangerous security flaw if you could.

One post suggested a potential solution: if I stored my xml files in the same directory as my game on the website, I could use Unity's WWW functions to read that xml into a Unity object without touching the internal filesystem. The effect would be the same. Unfortunately, the example code for this employed XmlReader, where I was using XmlSerializer. Also, the process outlined involved junking all of my Xml code, and one of the other posts I read seemed pretty confident that I could still use the XmlSerializer class from within WebPlayer. If the FileStream was indeed the problem I might be able to keep most of my code.

The breakthrough finally came as a result of this postwhere the suggestion was to use XmlSerializer in connection with Resources.Load and something called a TextAsset to bypass use of the prohibited FileStream class. Other exotic tools in play include MemoryStream and Encoding.UTF8 ... we're somehow simulating a streaming file operation within the game's runtime memory, which I find baffling and kind of magical. We have replaced this:

var serializer = new XmlSerializer(typeof(City));
using (var stream = new FileStream(path, FileMode.Open))
{
  return serializer.Deserialize(stream) as City;
  stream.Close();
}

with this:

City loadedCity = null;
TextAsset myAsset = (TextAsset)Resources.Load(fileName, typeof(TextAsset));
byte[] bytes = Encoding.UTF8.GetBytes(myAsset.text);
using (MemoryStream stream = new MemoryStream(bytes))
{
  loadedCity = (City)(new XmlSerializer(typeof(City))).Deserialize(stream);
}
return loadedCity;


and lo and behold, the version built for the webplayer runs without error, characters are selectable, xml is deserializable, the sun is shining, and all is right with the world. I'm taking the rest of the day off.

Thursday, May 2, 2013

Orange You Happy Now



Everything was going fine until I hit the color orange.

A little background here. In order to complete a level, the player needs to pick up five bus passes. Each bus pass has associated with it two particle effects, one a rising stream of particles for the at-rest state, as pictured above, and one a sort of blooming effect on the player when the thing is picked up. Both of these are the same color as the bus pass.

The particle systems are attached to GameObjects which are then stored as prefabs. The bus pass itself is placed in the scene view with a pickup script on it, and I toggle which color this particular pass will be via the Inspector, as the list of bus pass colors is an enum within that script. When the game starts and the bus pass script runs, it looks at that variable to find out what color it ought to be, then switches to the appropriate frame of its' RagePixel sprite. Each bus pass uses the same sprite, which just has five frames of the same image in different colors.

I made the ambient particles first, and I just set the color of each directly in the particle system before making the prefab, so I ended up with five different ones like "prefab_particle_buspass_orange" and "prefab_particle_buspass_green". Each prefab was assigned to its corresponding bus pass through an inspector variable, and each pass Instantiated that prefab during its Start function. Doing it that way once was, I felt, an acceptable level of laziness, but when it came time to do the bloom effect, I decided I had better just make one, "prefab_particle_buspass_pickup_any", and then programatically change the color. After all, each bus pass knew its own color, so it should be easy enough to use a blank white material and just throw RGB colors at it at runtime.

My first "huh" moment was getting the message "UnityEngine.Color does not contain a definition for 'purple'." I must be spoiled by Unity's easy access to so many MSDN libraries, but I figured a robust suite of crayola colors in the API would be more or less de rigueur. Not the case this time, but no big deal. Since I'm using C# and Color is a Struct like Vector3, I have to remember to replace

Color purple(128.0f, 0.0f, 128.0f, 1.0f);

with

Color purple = new Color(128.0f, 0.0f, 128.0f, 1.0f);

but again, all is well, and my purple looks purple in-game! Well, a little pink but whatever. Let's try a classic Cadmium Orange:

Color orange = new Color(255.0f, 97.0f, 3.0f, 1.0f);

This comes out yellow.
Undeniably, unsquint-at-ably, absolutely damned yellow.
I thought for a while, and I spent maybe a bit too much time at Wikipedia's unusually fascinating page for the color orangeand I thought some more, and eventually I did the right thing, which is to resort to googling ever more plaintive rephrasings of one's problem and clicking on Unity Answers links until something gives.

...No, I'm kidding, the right thing to do is check the documentation, which in my case would have easily shown this, for example:

Yellow. RGBA is (1, 0.92, 0.016, 1), but the color is nice to look at!

Unity doesn't use RGBA values of 0-255, it uses normalized values of 0-1. Maybe that's a Scandanavian thing? I do like the consistency of it, Alpha is usually going to be 0-1 anyway... I tried to learn a bit more about this but I soon wandered into a forbidding land of shadows and had to turn back. Not tryng to be incurious here, but your basic safety orange should not require post-graduate math.

Unfortunately my "what if this works" first stab of just dividing each of the values by 255.0f gives us roughly

Color orange = new Color(1.0f, 0.3803f, 0.5019f, 1.0f);

Which renders onscreen as a sort of ill salmon color. Then suddenly, in the next tab, a light shone upon me, for the good people of Unity had, by their own divine providence, thought to include a built-in function to do this exact thing.

Moments later I had

Color orange  = new Color32(255, 97, 3, 255);

Which pops out blazing orange. I believe it was Ben Franklin who said, "I only try to understand something complicated until I figure out something simpler that will allow me to stop trying to understand the more complicated thing." As for the mystery of why my totally wrong purple was somehow sort of purple, I guess if you overflow the bounds of whatever kind of structure Color is, it just holds the max value, like a cup of coffee being filled by someone who has fallen asleep. (128, 0, 128, 1) is to Unity the same as (1, 0, 1, 1), which is Magenta, which looks sort of like purple, in a certain light.