Friday, March 25, 2016

"The Cranky Teacher Routine"

"I don't feel like it."

[clasps hands behind back, walks slowly around desk]

"I don't feel like it, says the young man. Mm."

"Mm, well I quite sympathize. I certainly do."

"When you're young, the world lies before you sprawled out like a slovenly woman."

"All roads lead to greatness, all paths are strewn with garlands of glory, your feet never touch the earth, songbirds flutter about, tweeting of springtime lust, spurring your wretched meander through life to new depths of drunken merriment, is that about right?"

"When you're young, things that don't tickle your insipid fancy can be tossed aside, tossed out of your crib like brightly colored baubles that have finally bored you, because that's the way you treat everything and every one who has the misfortune to cross your careening, self-satisfied, nose-picking path, isn't that about right?"

[sighs, looks down glasses, taps foot]

"Mm but when you're old."

"When your skin is made of paper and the days fly by. When life is like a winter evening you spend huddled around the hearth as the sky presses blacker and blacker against the windowpanes."

"Maybe you think you'll never get there, or don't give a hang if you do. That's fine. Barring some freakish twist of circumstance, you'll be here long enough."

"You'll be here, with us, oh I'll still be here, and so -"

[gestures around room]

"So will a good three quarter of these, believe it or not. Quite long enough."

"Long enough to feel the cold of the world. And if perhaps, and I'm speaking to all of you, if perhaps you're one of those with enough spending coin to be immune to the more common depredations of the flesh, don't you worry. Don't worry one bit. The cold will find you too."

"You'll feel it in others, in your various disappointments, and most certainly in your interactions with the bloody gears of the machine, and by the machine I mean not just capitalism, or the West, but humankind. The whole lot of us, lurching toward something, or away from something else, a runaway machine where anyone with enough cunning and evil in them can grab the controls, at least long enough to yank a few levers and pound on a few buttons and make things a lot worse for everyone else. You'll see, now and again, how the machine is just as glad to take your life for fuel as anyone else's, and how it can run on nothing but lives devoured, and how it mows across the whole earth, for the benefit of so few. So very few."

[rubs eyes]

"All that, though, is like so many images in your picture books. Easily shrugged off, hardly an impediment at all to the truly vain. After all, you're the only real one, isn't that about right?

[interrupted by a sudden coughing fit]




[slowly regains composure]


"No, none of that will matter until the day your own body, the vehicle through which you travel the world, reveals its true nature as a brilliant flower, a firework, an exotic improbability that crests a tiny wavelet in the sea of time and is gone. A toy of nature, a thing as strange and brief as a rainbow."

"On that day, on that day you might think back."

[chuckles]

"Or you might think forward. Age is a terrible thing for a drifter, for someone who has not made their place in the world. The machines of the world care very little for the old, and the point of entry for old age gets younger every year. If you reach a certain age without acquiring certain skills, your outlook may begin to look very dark. Very dark indeed."

[begins walking back to front of room]

"Certain skills. What am I talking about, what skills? Well, pupils, that's entirely up to you. There is no magic skill that will ease your way through life. It will be a rough slog regardless, but you have it in your power to make everything immeasurably worse. It's a power I would beg you not to use."

"What I'm saying is, as you advance, in this school, in your own studies, as a person, you will find certain things that enchant you, and drive you, and fulfill you. Those things will become the lodestones of your life, even if they are not the things you set out in your schooling or your work to achieve. Those real goals will determine who you, in actuality, are."

[sits down at desk]

"Those true goals will be your mountains, your jungles and unexplored continents, those crafts and sciences and arts that call you to them and make you their servants, and that rush to fulfill that calling with hard work, let me tell you, is one of the greatest joys one can know in this little life."

[looks over the room slowly]

"Those goals, those paths, being true, will be hard. There will be times when you are sorely tested. There will be nights exposed on the face of a glacier. Days when the sun is like a hammer, and you crawl along like an ant under glass. Weeks and months of dull white clouds and black lanes and grey grass. You may not seek death, but you'll sorely consider the idea of never having shown up here in the first place."

[takes off and polishes glasses]

"When that day comes, when the ultimate failure of your vessel becomes real to you, you might wonder what's left. Unless you're one of the elect illuminated and have already made your peace with the divine, you might wonder what's expected of you. How to live out the remainder of your days. How to leave a mark, to provide for the future, to achieve immortality."

[puts glasses back on]

"On that fine day, class, God help you if you're one of the ones who said 'I don't feel like it'".

"Turn to page 53 in your books."

Sunday, March 8, 2015

Boxes Full Of Bones

Project_catalan is still a game that doesn’t really know what it wants to be when it grows up, but that hasn’t prevented me from using it as a platform on which to try out new things I’m learning to do. Once I had established the game board as a set of tiles of varying colors (representing terrain types like grass, sand, water, etc.) I became interested in the idea of generating a new version of the board each launch. This is less like sophisticated procedural generation and more like plucking hexagons from the Catan box, but… baby steps.


The method here takes place in three steps. First, a full grid of grass tiles is laid down. Then, starting from one edge, a line of river tiles is placed in a random meandering line, replacing grass as they go. Finally, the tiles on either side of each river tile are replaced with sandy banks. The whole thing is somewhat unwieldy but here’s the river function as an example of what’s being done:

void MakeRivers()
{
 //this could get a little tricky
 //we want to make a river that cuts across the whole grid in a meandering fashion
 //we'll assume at the moment that we only ever want one river
 //first: is this a vertical or a horizontal river?

 int riverOrientation = Random.Range(0, 2);
 if (riverOrientation == 0)
 {
  Debug.Log ("Vertical river");
 }
 else
 {
  Debug.Log ("Horizontal river");
 }

 //now, where does the river start?
 //at a random point along either the top of the left side
 Vector2 riverStartTile;
 int startRow;
 int startColumn;
 if (riverOrientation == 0)
 {
  //start on final row, then randomize column
  startRow = rows - 1;
  startColumn = Random.Range(0, columns);
 }
 else
 {
  //start on final column, then randomize rows
  startColumn = 0;
  startRow = Random.Range(0, rows);
 }

 //now that we have the row/column index, we need to match that up with the index into 
 //tiles[] of the tile actually living in that spot
 //first let's get the world pos of where we think we actually are
 Debug.Log("starting our river at row " + startRow + " , column " + startColumn);
 penPoint = new Vector3(startColumn * width -16f, 0f, startRow * length -16f);
 Debug.Log ("penPoint is " + penPoint);

 //since we're naming the tiles with their row/column numbers, we can use that to find and destory them
 //let's try a straight river first I guess
 int currentRow = startRow;
 int currentColumn = startColumn;

 if(riverOrientation == 0)
 {
  for(int i = startRow; i>= 0 ; i--)
  {
   string tileName = "grassTile_"+i+"_"+currentColumn;
   GameObject tileObject = GameObject.Find(tileName);
   //Debug.Log ("Looking for " + tileName);
   if(!tileObject)
   {
    break;
   }
   Vector3 replacePosition = tileObject.transform.position;
   Quaternion replaceRotation = tileObject.transform.rotation;
   Destroy(tileObject);
   GameObject waterTile = Instantiate(tileTemplate_water, replacePosition, replaceRotation) as GameObject;
   waterTile.transform.parent = tileMapObject.transform;
   waterTile.name = "waterTile_"+i+"_"+currentColumn;

   //bend?
   int bend = Random.Range(0, 5);
   if (bend == 3)
   {
    //ok, we're bending, let's determine how much
    int bendAmt = Random.Range(1, maxRiverBend+1);
    //find bend direction
    int coinFlip = Random.Range(0, 2);

    for(int k = 0; k <= bendAmt; k++)
    {
     //here's where we hop to another column
     //east or west?
     if (coinFlip == 1) //east
     {
      currentColumn++;
     }
     else //west
     {
      currentColumn--;
     }

     //now replace the bend tile 
     GameObject bendGrassTile = GameObject.Find ("grassTile_"+i+"_"+currentColumn);
     if(!bendGrassTile)
     {
      break;
     } 
     Vector3 bendReplacePosition = bendGrassTile.transform.position;
     Quaternion bendReplaceRotation = bendGrassTile.transform.rotation;
     Destroy(bendGrassTile);
     GameObject bendWaterTile = Instantiate(tileTemplate_water, bendReplacePosition, bendReplaceRotation) as GameObject;
     bendWaterTile.transform.parent = tileMapObject.transform;
     bendWaterTile.name = "waterTile_"+i+"_"+currentColumn;
    }
   }
  }
 }
 if(riverOrientation == 1)
 {
  for(int i = startColumn; i < columns; i++)
  {
   string tileName = "grassTile_"+currentRow+"_"+i;
   GameObject tileObject = GameObject.Find(tileName);
   //Debug.Log ("Looking for " + tileName);
   if(!tileObject)
   {
    break;
   }
   Vector3 replacePosition = tileObject.transform.position;
   Quaternion replaceRotation = tileObject.transform.rotation;
   Destroy(tileObject);
   GameObject waterTile = Instantiate(tileTemplate_water, replacePosition, replaceRotation) as GameObject;
   waterTile.transform.parent = tileMapObject.transform;
   waterTile.name = "waterTile_"+currentRow+"_"+i;
 
   //bend?
   int bend = Random.Range(0, 5);
   if (bend == 3)
   {
   
    //ok, we're bending, let's determine how much
    int bendAmt = Random.Range(1, maxRiverBend+1);
    //find bend direction
    int coinFlip = Random.Range(0, 2);

    for(int k = 0; k <= bendAmt; k++)
    {
     //hop to another row
     //north or south?

     if (coinFlip == 1) //north
     {
      currentRow++;
     }
     else //south
     {
      currentRow--;
     }

     //now replace the bend tile 
     GameObject bendGrassTile = GameObject.Find ("grassTile_"+currentRow+"_"+i);
     if(!bendGrassTile)
     {
      break;
     } 
     Vector3 bendReplacePosition = bendGrassTile.transform.position;
     Quaternion bendReplaceRotation = bendGrassTile.transform.rotation;
     Destroy(bendGrassTile);
     GameObject bendWaterTile = Instantiate(tileTemplate_water, bendReplacePosition, bendReplaceRotation) as GameObject;
     bendWaterTile.transform.parent = tileMapObject.transform;
     bendWaterTile.name = "waterTile_"+currentRow+"_"+i;
    }
   }
  }
 }
}
This was the only work I did on this over the winter, as petrov was monopolizing most of my time (see previous post). I returned to it late last month, as it seemed like a perfect test bed for my latest preoccupation: 3D modeling and animation.

I had done some hands-on work with Maya and 3DS Max at various studios, mostly in the context of testing, and I managed to find my way around at a basic level, but these programs are a little too expensive for home use, so I approached Blender, and bounced off, hard. My first crack at it was probably about a year ago, and every so often I would return, a tutorial in hand, and try again, only to end up sliding face first down the learning curve like Wile E. Coyote. In a sense I had been spoiled, as Maya is quite friendly and sensible in terms of its user interface. Blender, by contrast, is full of obscure keyboard combinations, context-sensitive menus, and many different modes the main editor can be in, all of which respond differently to those commands and menus. Ultimately it’s like any other piece of complex software: if you bang your head against it repeatedly, you’ll eventually realize that there are one or two small techniques you've absorbed and become comfortable with. You can then use those as a beachhead and campsite from which to explore more daunting areas. Eventually you may earn enough hard-fought victories to ascend the mightiest peaks, breathe that rarefied air, and proudly don the mantle of “beginner”.

Jokes aside, 3D modeling (not to mention rigging, skinning, and the rest) is a discipline much like programming, in that it’s easy to fool yourself into thinking you know something about it, when you really don’t. A forum post I read somewhere described the gap between a bit of functional programming knowledge and the ability to contribute meaningfully in a programming role at a serious studio as being somewhat like the gap between being able to make yourself dinner and being able to work as a sous-chef at a busy high-end restaurant. I think this is spot on, it applies to many game studio roles, and it’s a good reminder not to get cocky. There is always much more to learn.

My goal in this instance was to add another prototyping tool to my kit. Many of the game ideas I jot down in notebooks require animated 3D models, that is to say, little people moving around. If I couldn't at least rough in this sort of thing, there was no hope. So, I affixed my pitons to the face of Mt. Blender and started climbing.


This is boxman, a Blender model composed of a bunch of cubes joined together. Boneheadedly simple, not much to look at, and in this form totally useless except as a mannequin. I could import him into Unity, but he would just stand there motionless.


This is boxman’s armature, made of a series of “bones” that control his movements. The model is made up of vertices, points in 3D space, that together define the shapes of the boxes. Rigging and skinning a model means that each vertex must be assigned to a bone, so that when the bone moves, its assigned set of vertices are moved along with the bone, in a process called mesh deformation. Since boxman only has a couple dozen vertices, this is about the simplest case you could hope for. Making a realistic-looking character model move realistically involves assigning thousands and thousands of vertices and carefully “weighting” them to control how much deformation the bones can do, to say nothing of constructing the actual animations, transitioning seamlessly between them, and making them responsive to player input. I have a tremendous amount of respect and admiration for the people who do this work for AAA games. Have a look at something like the combat moves in Shadow Of Mordor and it’s quite clear that that this is as painstaking and complex a combination of skill and art as anything a programmer might face.


Here’s boxman getting his walk on via Unity’s Mechanim animation system, which uses a simple keyframe animation interface like you’d find in Maya or even Flash. Of course, you can animate in Blender, then import those animations in Unity and hope you can make them work, but I was quite relieved to discover I didn’t have to. Unity lets you move the bones around in Animation mode using the same transform and rotate widgets you’d use on any other object, so it was a joy to be back in familiar territory. Boxman’s walk cycle is as rudimentary as the rest of him, but the goal here was just to get the basic parts in motion. Pretty can wait. Mechanim also contains a handy state machine interface with variable-based transitions that can be called from script. That is to say, I can give the model a walking state and an idle state, which will flip back and forth depending on if the boolean “isWalking” is true. Then I can make that bool true or false from the movement control script, based on whether the player is touching a key.

Keys bring up the topic of character control, a subject that most projects wrestle with. The original proto-prototype of catalan (known as benko at the time) had a control scheme where you simply clicked the mouse on the point where you wanted to travel, as I had been playing a bunch of Path of Exile at the time and basically wanted to rip off some of the feel of playing that game (no luck whatsoever on that front). I quickly ran into problems because the shooting mechanic involved clicking on monsters, so a slightly misplaced click would send you strolling blithely toward the enemy instead of engaging it, which got frustrating fast. The next iteration was straight-up WASD movement with mouse aim, which was great when the player was an orange cylinder because it didn't matter which way it was facing. To my horror, the box model totally broke this movement scheme.

It’s an interesting and fundamental point of game design that when you introduce anything from the real world, the player expects (quite reasonably) that it will behave like that thing does in the real world. Players will forgive a lack of verisimilitude when controlling cubes or spheres or even spaceships (sometimes), but gosh darn it we all know how people move. The problem with my controls came about when I implemented the player model rotating based on mouse aim. Having the rotation disconnected from movement was no problem for a cylinder, but for a person, it just felt weird that the right arrow was sometimes rightward movement, and sometimes forward or backward movement, depending on the model rotation. Now, this scheme is perfectly valid, and has a long and storied history in the “twin-stick shooter” genre, going all the way back to Robotron 2084, but in most successful cases the movement is sold to the player as realistic in various ways, the most ingenious being to disconnect the upper half of the body from the lower, and orient the legs to the movement vector and the arms to the aim vector. Elegant, attractive, and still a bit over my head technically. I could see upgrading catalan to that scheme sometime in the future, but for now I needed something simpler.

I had a really cute second idea to make the right and left arrows move the player laterally to the facing direction, and even learned just enough about cross product math to make it work, but it turned out to also be a really dumb idea. When the player was facing away from the camera, the arrows worked beautifully; when the player faced the camera their motions were flipped, which was an even queasier sensation than before.

In the end, I opted for a dirt simple approach: the forward and backward arrows (or W and S) work, the side arrows do nothing, the mouse controls aim and rotation. This still doesn't feel great, but it’s workable. I punched up the visuals with some handmade, randomly placed trees and some simple textures instead of solid colors. I animated the water and even replaced the “purple refrigerator” enemies with some kind of bear giant thing that toddles around amusingly.


The whole thing is still a far cry from being fun, there’s nothing much to do and it still lacks any kind of theme or character, but this is really the way I like building things. A lot of designers start with a high concept idea or story and then build the mechanics up underneath that, but I always start with a mental image of a small onscreen interaction. A video game is just a person in front of a screen with a manipulation device (at least until the VR revolution swallows us all whole). If the basic act of interacting with what’s on the screen isn't engaging second to second, then the best story in the world won’t be any use. Refine the gameplay loop, again and again, make that your primary focus, and don’t worry about whether the player is supposed to be a pirate, a cowpoke, an astronaut, or a QA contractor with dreams of glory. Make the fun, and the rest will come out in the wash.

Coming Up
  • Oh, hello Unreal 4. Hello Source 2. Is this an embarrassment of riches or what?
  • A radar for catalan
  • More prototypes, and maybe I'll finish those design docs I keep been talking about






Saturday, February 21, 2015

Always Crashing In The Same Car


It’s dangerous to go alone. The total freedom of solo game development can be its own worst enemy. With no one around to say “hey, maybe rough in feature X and move on”, you can end up caught between your best judgment and the stubborn, dogged perseverance that got you into this mess in the first place. The more of this I do, the more I find that the ability to scope from within a problem, to decide which solutions are good enough for now, to basically Produce oneself as mercilessly as a corporate publisher, that constantly updating self-reflection is the key difference between solo games that are in some way concluded and others that are simply abandoned.

I spent most evenings and weekends of the dark months on project_petrov, often spinning my wheels, or more accurately watching a small model car flip onto its side over and over again.


This whole racing project is a derail in itself. I’ve been working through this (really excellent) Unity textbook which mostly consists of little example game projects where they give you some starter bits and then you complete the game. Everything was going great until I got to the "generic racing game" project. I got into it, then started getting irritated by things like the terrain being inappropriately mountainous (making camera movement in scene view difficult and lighting a chore), the track being punishingly long for the size of the models (making iteration on things like win condition code a chore) and various other minor annoyances. I decided the only way to complete the project to my satisfaction would be to remake it from the ground up, including all the assets. I figured I could whip up some new scripts, hit Turbosquid for some free cars, and maybe put some twists on it so I could have another minor portfolio piece. Expected turnaround time: a few days? This was in November.

My first hack at the track was an interesting idea, I found some outline images of famous race tracks at this cool site, and attempted to use some as height map files on Unity terrain objects. The primary problem with this was the dismal sense of racing in a WWI trench, but there were other problems, for instance I had no easy way to tell whether the cars were on the track or not. Eventually I realized I would need to use some kind of road objects that could send messages about whether a car was touching them. So, the racing trench and its associated materials went into the bin.


Making modular track pieces with Blender was straightforward enough after watching a few relevant YouTubes. My UV mapping skills are still pretty basic, so the road curves look a little stretched and blurry, but I was able to make myself move on from that (which wasn’t hard, because another few nights in Blender was nothing to look forward to).


The muscle car model itself was also simple enough to work with, although the artist “posed” it with the front wheels turned insouciantly to the left, so I was constantly having to adjust them by around 9.6 units of rotation, a figure which grew to haunt me. It didn’t make much difference in the end as I wound up chopping the model into body, glass and wheel meshes and placing each individually as child nodes of either the car body or the car wheels, so I had an invisible cube and some invisible cylinders “wearing” the good meshes like a second skin. I painted each mesh with some basic colored materials, entertaining various dreams of a rainbow panoply of AI opponent cars, and a cel-shaded “Saturday morning cartoon” effect in post-processing, but both of these features turned out to be, thank Christ, out of scope.


Then it came time to make the car go. The book example propels the vehicle by using AddForce on the car’s RigidBody, which works well enough, but as a physical simulation is somewhat like towing the car along behind another car with a cable – the movement force doesn’t come from the wheels, so the simulation is not very car-like. I was determined to do things “the right way”, which meant controlling the car with four WheelCollider objects by applying MotorTorque and BrakeTorque. This was actually fairly intuitive and even fun to implement, and the simulated springs gave the car a delightfully realistic bounciness when dropped onto the terrain. Everything worked beautifully until I attempted to turn the vehicle, at which point it flipped over. Repeatedly. Every single time, in fact.

Anyone who has done time in the murky woods of Unity vehicle physics is probably familiar with self-described Hardcore Developer Edy, a towering genius of video game automobiles who has published some fascinating posts on the Unity forums, describing, among other things, how to keep a car from flipping over. The answer he proposes is simulating stabilizer bars. The idea is, you read data from the WheelCollider to determine how far the spring is extended. When the car takes a turn, the springs on the inner wheels will compress, while those on the outer wheels expand. So, as a spring expands, you impose a downward force on the car body at the wheel location, and the more the wheel threatens to leave the ground, the harder you push down on it. Elegant in theory, but in practice there are more than a few things that can go wrong, and in my case all of them did.

I could not get the damned car to stop flipping over. Some reasons my stabilizer bars might have failed:
  • The car may have been out of scale to the Unity physics system
  • The car’s rigidbody mass and center of gravity settings may not be compatible with the stabilizer code
  • The car model exported from Blender may have some qualities that don’t play well with the physics system and/or the stabilizer code
  • My WheelCollider settings may be incompatible with how the stabilizers work
  • I may not be applying the downward force effectively, with the right amount and at the right location
  • Despite extensive visual debugging to make sure X really is X, I may be misunderstanding what the stabilizer code is actually doing
  • My math deficiencies may have gotten the best of me (the stabilizer code uses Transform.InverseTransformPoint, which I can’t honestly claim to completely understand)
  • The WheelColliders themselves may be in some way suspect. I have nowhere near the knowledge to takes sides on Edy’s post about a purported scandal in wheel town, but nobody from the Unity dev community has come along to tell him he’s wrong, so who can really say?

All interesting lines of inquiry, for someone with half a dozen spare lifetimes to spend faffing about with wheel physics, but I had already squandered too much time on the problem. Finally, I found a brutal hack and wielded it like a hatchet. Behold my shame:

downforce = -veloMag * rigidbody.mass * 3f; //sSHHH it's a MAGIC NUMBER son
downvector = new Vector3(0.0f, downforce, 0.0f);
rigidbody.AddForceAtPosition(downvector, transform.position);

The faster the car goes (veloMag is the rigidbody’s velocity.magnitude), the harder I press down on it at the center. It’s like racing with the veritable Thumb of God on the car roof the whole time. Now, any future development work would surely start here: I’m confident that I could lower that value and tweak various other parameters until I reached a compromise where the car would resist flipping, but retain some of the car-like bounciness the WheelColliders provide. As it is, I’ve more or less come all the way back around in terms of realistic simulation. This solution is hardly more “car-like” than just applying force directly to the car, all the interesting wheel spring behavior is getting squeezed out of existence, like the dynamic range on a Red Hot Chili Peppers album.

Regardless, this allows me to glue the car to the ground, finish the scenery and UI, put some basic game logic in place, and close the loop on the project as a portfolio tech demo. It’s a racer, just not a very compelling or well-realized one, and it’s not exactly meant to be. Although I have some interesting ideas on where it could go, and I may spend more time on it later, I’m not taking this thing to Early Access.

Here's where I confess to not particularly loving racing games. Oh, don't get me wrong. Pole Position is just as iconic to me as Galaga or Moon Patrol, and the arrivals of the Stun Runner and Hard Drivin' cabinets at my local arcade were earth-shaking events at the time. I just mean to say that I never planted a stake in any modern racing franchises, never had a Forza career or bought a faux steering wheel. So why make one? Primarily due to being at the racing chapter in the textbook I was working from, but just as importantly out of a self-imposed need for portfolio diversity. For some dumb reason I feel like a proper game designer should be able to whip up anything from a shooter to a stealther to platformer, in 2 or 3D, in enough detail and with enough quality so that it could convey whatever the cool new twist is without stumbling over the set dressing. It's a fine notion, but it turns out that maybe racing is one of the less trivial genres, from a gameplay design point of view.

And, without getting too grim, this work is hard enough to justify as it is. When I wasn’t day jobbing on the fringes of the industry, I spent the dark months flitting from studio to studio whenever a designer role came open on the job boards, shaking hands and telling my life story over and over, trying to convince someone, anyone, that yes, I am actually competent as a game content designer, and not being able to convey that impression in the slightest. I’m hoping that all this portfolio work will pay off, but it’s hard to believe that anyone will care. The studios want, and in most cases can afford to wait for, a designer with years of commercial experience on the exact subgenre they’re putting out. I’ve been rejected on the basis that “we’re looking for someone who lives and breathes subgenre X”, which, I suppose I’d want that too in their position, but it points to a pretty dark future for someone whose resume looks like it was written with a shotgun. I’m slowly trying to find a way to accept that I may never be employed as a creator again, that the rest of my career may be an endless string of exploitative and unrewarding QA contracts. I have to make a conscious decision every day to not believe this, because the feeling of believing it is like the feeling of being on fire. Nobody likes being on fire.

Coming Up
  • more prototypes
  • project_catalan upgrades
  • some non-Unity experiements
  • some game design docs for larger projects 


Monday, October 13, 2014

void EverythingThatRisesMust(Collision collision)



In its resting state, the current build of project_catalan may not look much different from the previous one (a bit more red maybe), but a lot has changed under the hood. The changes, in my mind, are akin to standing in the doorway of the empty shell of a house and thinking "all right, we're going to need to shape out the living room, so we'll need walls here, and a load-bearing column there, through which we'll route water and electricity..." We haven't even gotten to the studs and sheetrock, much less the arguments about which of your friends' paintings are going to hang near where people might be, like, eating. As in any self-impelled project, you're always glancing that far ahead from time to time, and it can be useful to attempt to anticipate what problems you may need to solve in the future, to the extent that it helps you choose the solutions to your current problems that make future you's life easier, but it would be a classic game development mistake to fall afoul of something so seductive and misleading as a plan.

This last sprint was about damage, death and communication. How does one character damage another, and how is that information communicated to everyone who needs it, including the player. I found Unity's new UI system (introduced in 4.3?) to be an absolute miracle, in the sense that it allowed me to do sensible things. My previous experiences with Unity UI involved making instances of things like GUI.Box in the OnGUI function of something attached to the camera, which seemed to mostly work for what I needed but had the usability flavor of writing a Windows GUI application in C# without the benefit of XAML, or doing web layout by changing numerical values in raw CSS files and then refreshing the page. It wasn't very IDE-integrated, is what I'm saying, and the new version totally is. When I was contemplating how to implement MMO health bars for the enemies, I was at first thinking about scripted GUI.Texture instances translating and scaling based on a mathematical relationship between the enemy and the camera. You'd have to be a masochist to write such a system, so I imagine the preferred solution was just some Doom-style billboarding on quad meshes attached as children of whoever gets a healthbar, all on some custom collision layer that ignores the rest of the game world. Unity recognized that not using the UI system was better than using it, so they fixed it. Now we can have world space Canvases as children of moving gameObjects, and a an enemy's health bar can be built with basically no code:



Allowing for player damage was somewhat trickier. I wanted a standard player HUD, and using the new Canvas tools in combination with the main gameplay camera produced undesirable results. I ended up adapting a technique I had seen the Snuggletruck guys explain in a seminar: I created an orthograhic camera "box" far away from the action, and gave it a positive Depth so that it would render over the gameplay camera. This freed me to compose a UI in Scene view without feeling crowded by actual gameplay objects:


The problem with this approach (Screen Space - Camera) is that it's not very forgiving to resizing / rescaling. Right now I'm viewing all of my current projects as portfolio pieces aimed at a standard web browser, but not building in UI scaling functionality as you go is a recipe for "platform prison syndrome". The new anchor system is quite elegant and only a little over my head, so this feels like a very solvable problem once it needs to get solved.

The idea of implementing one last feature for this build, pushing a character away from a damage impact, turned out be much like Columbo un-mouthing his cigar, turning his head slightly, and asking about "one more thing". The ur-idea here is of course from the NES Legend Of Zelda  (yes, catalan is a Zelda clone. I will also stay up all night with you argiung that the Diablo games are Zelda clones, provided you're buying). A character being melee'd by an enemy should be slammed back, not far enough or long enough to disrupt gameplay, just enough to provide that sensation of "yes, dumbass, you touched a hot stove. How bout you try to avoid doing that?"

Teaching in video games is not done thorough tutorials, which are not widely read or played or viewed or whatever. Teaching is done by presenting the player with a variety of hot stoves and inviting them to make like Buddy Rich on some bongos. See what you like! They'll figure it out. If not, they'll leave you a nasty review claiming the game is too hard. Look, all I can do here is make stoves and heat them to various degrees. It's up to you to decide what to touch, that's what our relationship is about.

Trying to sort out combat, I worked through all the permutations of Unity colliders, triggers, rigidbodies... you can't just point at any of the onscreen characters and ask what collisions they've experienced recently, it's all very dependent on what components the attacker and defender were sporting, the complexity got silly, I even used some joints (er, Fixed Joints, that is). One morning at about 3:40AM I realized part of one of my "Push" function involved assigning one variable's value to another variable that just had a different name. I was writing in circles. I called it off.

The "Zelda Push" ended up not making use of colliders at all. It doesn't even require rigidbodies! It's dirty secret is performing a while loop inside a CoRoutine, and using "yield return null" at the end of each loop iteration. so the while loop actually updates at the same speed as the main game loop. Here's how the whole thing ended up looking (for context, this method lives in healthStates.cs, which is attached to any object whose health we care about:
IEnumerator DoPush(GameObject pusher)
 {
  Vector3 startPoint = transform.position;
  Ray pushRay = new Ray();
  if(pusher.GetComponent())
  {
   //Debug.Log ("melee push");
   pushRay = new Ray(startPoint, transform.position - pusher.transform.position);
  }
  else if(pusher.GetComponent())
  {
   //in this case using the bullet for the ray origin gets wonky
   //so we get the gun location and start the ray there
   //Debug.Log ("bullet push");
   bullet_base bulletScript = pusher.GetComponent();
   Transform firearm = bulletScript.weapon;
   pushRay = new Ray(startPoint, transform.position - firearm.transform.position);                
  }
  else
  {
   Debug.Log ("invalid pusher");
  }
  Vector3 flatPushDir = new Vector3(pushRay.direction.x, 0.0f, pushRay.direction.z);
  pushRay.direction = flatPushDir;
  Debug.DrawRay(pushRay.origin, pushRay.direction*1000, Color.red, 10.0f);

  float pushLength = 5.0f;
  Vector3 pushPoint; 
  pushPoint = pushRay.GetPoint(pushLength);
  //pushBall = Instantiate(pushBallTemplate, flatEndPoint, Quaternion.identity) as GameObject;

  float pushStartTime = Time.time;
  float distanceCovered = 0.0f;
  float fractionComplete = 0.0f;
  float pushSpeed = 50.0f; //this actually matters in relation to the pushLength
  while(fractionComplete < 0.9f)
  {
   distanceCovered = (Time.time - pushStartTime) * pushSpeed;
   fractionComplete = distanceCovered / pushLength;
   transform.position = Vector3.Lerp(startPoint, pushPoint, fractionComplete);
   //Debug.Log("fractionComplete = " + fractionComplete);
   yield return null;
  }
 }


Once I built the new version, befoe deploying it to the web I tested it locally and encounteerd this error:
Built with beta version of Unity. Will only work on your computer!
which is pretty delightful. I guess as of 4.6.0, the games we're able to build at home are somehow ahead of the WebPlayer to the point where they could become dysfunctional? As a matter of fact when I ran the game under chrome, the healthbars were offset and didn't scale properly, as I'm sure you'll see if you check the game out. I don't know how FF or IE deal, I didn't have the heart to find out. I mean, yes, it's my own fault for trying to create software to present to the public with beta tools, but I much would have preferred messages more specific to my situation:

  • Other computers may choke on your ridiculous, leaky algorithms!
  • Other computers may just not have the CPU/GPU muscle to simultaneously lift and lower all the vertices of your tremendously detailed and natural game world which is not optimized in any significant way 
  • Your game world has, in defining its origins and boundaries, mishandled the spatial and memory assumptions of the machine you're working on so as to create some sort of novel geometric figure, somewhat like a turd the size of the universe spinning at (0,0,0).

Just some decent error messages. That's all anyone should feel comfortable asking for.

Coming Up:

  • How bout them models
  • Some kind of hooky concept seems about ripe... this can only stay generic for so long
  • Models should perform some kind of death sequence
  • What's it all about, killing purple dudes? Do they drop money which allows you to buy better guns? From whom? The more solid your gameplay systems become, the more there is this need to commit to some kind of content interpretation in order to build more sophisticated sub-systems that enhance that interpretation. Generic gameplay engines are great, but you have to plant an conceptual / content-centric flag in order to make that into a game, and that's a process of walking through one of many doors and feeling a ton of other doors slam closed. You better hope you walked through a door you can live with!  





Sunday, September 21, 2014

Out On The Tiles


Once a certain mega-game shipped, I found myself with a little time in which to return to some Unity prototypes. Something about The Benko isometric project kept drawing me back, but I was frustrated by the control scheme. I wanted to control the main character more directly, and I wanted to be able to use a gamepad to do it. However, I also wanted the game to be playable in-browser by anyone with a keyboard and mouse, which meant a back-up set of controls. Surely, I thought, this would be a snap.

Unity did meet me more than halfway here, because rather than having to plow through a bunch of GetKeyDown type functions, I could use Input.GetAxis, which was automatically mapped to WASD for movement, and could quickly be taught to recognize the trusty 360 gamepad. Getting parity in movement was easy. Getting parity in facing was not quite so easy.

The gamepad’s right thumbstick gave me a pair of clean, normalized Vector3s right out of the box, so I was golden there. The interesting part was getting that same result out of a mouse position. I knew that a ScreenToWorld translation would be involved, but there was more to it. I fooled around for many a Designated Leisure Period trying to covert Quaternions to Vector3s using EulerAngles, exploring all the LookAt, LookTowards, and TurnToLookAtThatGuy functions, and generally achieving jack all. I would find solutions that had the right direction but the wrong magnitude, or solutions that worked great until you moved the player away from the origin point, at which point they broke down. Eventually I bumped my head or something and realized that what I needed was a Ray. I guess I had unconsciously filed Rays away in the part of my knowledge that dealt with collisions, and since I wasn't working explicitly on the physics I didn't make the connection that this was an available tool. But yes, when you need any information on where one point lies in relation to another, and how one object might face another, at an arbitrary distance from the origin, your best bet is to break out the Rays:

void HandleLookInput()
{
    //this func is how we set the lookDirection var every tick
 
    //MOUSE VERSION (first so gamepad can override if plugged in)
    mouseTarget = mouseScript.mouseTarget;
    //subtracting player pos from target gives us correct magnitude, which is then normalized to match gamepad
    Ray lookRay = new Ray(transform.position, (mouseTarget-transform.position).normalized);    
    Debug.DrawRay(transform.position, (mouseTarget-transform.position).normalized, Color.magenta);
    //force y to zero to avoid unwanted bullet fall
    lookDirection = new Vector3(lookRay.direction.x, 0.0f, lookRay.direction.z);

    //GAMEPAD VERSION
    if (isGamepad)
    {
        H_LookInput = Input.GetAxis("LookH");
        V_LookInput = Input.GetAxis("LookV");
        if (((H_LookInput >= 1) || (H_LookInput <= -1)
        || 
        (V_LookInput >= 1) || (V_LookInput <= -1))) 
        {
            lookDirection = new Vector3(H_LookInput, 0, V_LookInput);
        }
    }
    else
    {
        //don't change look dir if player released stick
        lookDirection = lastLookDirection;
    }
//Debug.Log("LOOKDIR : " + lookDirection);
}

The “bullet fall” thing is interesting, and leads into the next part: I had a player and a big purple refrigerator (conceptually a bear) and of course my next prototype impulse was to fire some sort of arrow or bullet at the bear. I think the direction this is moving now is into a kind of “isometric twin stick style action panic swarmer”, where the player fends of waves of attackers, but we’ll see where it ends up.

As usual, I wildly underestimated the challenge of deriving a projectile’s trajectory from a Vector3 representing a direction between two objects. Seems like the same thing… but not on a flat plane where the bullet has to come out of the muzzle of a weapon that could be rotated in any arbitrary direction. This was the place for LookRotation, plus the aforementioned Euler Angles.  I won’t pretend to understand any of that linked page, but I was able to find the examples I needed to rough in a working implementation (this lives in the ranged_weapon script attached to the player’s “bow”):

if (canShoot)
{
    //Debug.Log("yes");
    //make bullet
    //Debug.Log("BANG! " + i);
    //Debug.Log("Creating a bullet at " + transform.position);
    Vector3 targetVec = lookscript.lookDirection;
    Quaternion targetRot = Quaternion.LookRotation(targetVec);
    Quaternion flatTargetRot = Quaternion.Euler(new Vector3(90.0f, targetRot.eulerAngles.y, 0.0f));
    bulletClones[i] = Instantiate(bulletTemplate, transform.position, flatTargetRot) as GameObject;
    currentBulletsAlive++;
    //plant a reference to this weapon in the bullet when it is created
    bullet_base bulletscript = bulletClones[i].GetComponent();
    bulletscript.weapon = transform;
    //throw bullet
    bulletPath = lookscript.lookDirection;
    //actually it makes more sense for the bullet to control its own movevemt
    //but will leave this variable in to ref from the bullet script
    //activating cooldown
    canShoot = false;
}

The canShoot bool is to prevent bullet spam, and is made true on GetButtonUp. The bullet itself carries a script which throws it in the right direction (requiring that neat trick just above where we inject a value into a variable owned by something we just created, like inoculating an infant). The “bullet fall” mentioned above happens because for whatever reason the path to the target can sometimes have a y value of -0.1, which will cause it to fall through the ground plane somewhere in its flight time. The hack above led me to really understand the difference between a solution and a hack in a way that I never had before.

 A bug happens when some assumption you have about some data you have is wrong. If you trace the path by which you received the data backwards to where it diverged from your expectations, then re-derive the data from its sources so as to produce the desired result, you have solved the problem. If instead you take the data in your hand and say “well, regardless of what you think you are, you’ll conform to my expectations now so that I can proceed”, you have a applied a hack. It’s obvious why this is dangerous, and it’s equally obvious that there are any number of reasons why you’d do it anyway: maybe the source of the bug is deep within someone else’s code, or the exigencies of time and budget simply don’t allow for a proper solution. Hacking is about picking your battles, knowing when to do surgery and when to just use a bandage and cross your fingers.

Some collider-based debug statements showed the bullet striking the bear, but the game didn't alert the player. I decided to flash the bear red by changing the material color, to my horror this was permanent after I hit the play button to stop the session. Turns out Materials can be instanced too, and a simple co-routinue times the flash effect:

matInstance = Instantiate(baseMat) as Material;
baseColor = baseMat.color;
renderer.material = matInstance; 

[…]

void OnCollisionEnter(Collision collision)
{
    if (collision.collider.tag == "playerBullet")
    {
        Debug.Log ("enemy hit by player bullet");
        StartCoroutine(DamageFlash());
    }
}

IEnumerator DamageFlash()
{
    matInstance.color = Color.red;
    yield return new WaitForSeconds(0.25f);
    matInstance.color = baseColor;
}

So the bear is hit, and everyone knows it. Coming up next:
  • Health, damage, and death
  • GUI enemy health bars
  • A GUI for the player
  • Some proper character models??
  • A proper “world” of tiles? (Maybe procedurally generated???)
The current build of Project Catalan can be played here.

Monday, June 23, 2014

LETTER FROM THE FRONT: WRITING TIPS

I've been writing. I stepped away from Unity and this blog to do a writing project, which is now over.

A writing project is a menacing jungle full of traps and enemies. The enemies may be projected out from behind your eyeballs, but the traps are very real. I've have spent years learning about all of them, and I have the closet full of horrific half-manuscripts to prove it. Lately I've had to pass through some dark places I had hitherto managed to avoid, and now I'm back with some simple tips that may help light your journey through that darkness. Some of these are surely either dead obvious, or so particular to my own process as to be useless, but maybe there's something here you can take with you.

#1. MAP YOUR WAY OUT

No, I don't mean actual drawn maps. I mean that when you dread sitting down to your story, because Mr. Jones has to get to the barber or whatever, and you don't know how the fuck he does it, and your last paragraph is about a bluebird, and jesus christ will this thing ever end, are you going MAD???
Calm down.
Set your actual current draft aside and open a new file called "what_has_to_happen_for_this_chapter_to_be_over.txt" A totally blank file. Now, picturing that awesome ending you've already got basically DONE in your head, step yourself through how you get there, as though you were making a list ahead of going down to the grocery, drug and hardware stores the night before a hurricane. "OK Fred has to get his passport photo taken, Rachel has to go to court and plead no contest.." as you start to really articulate all the details that are holding you back in the story, you realize that some are either readily solvable or non-issues. Some are still problems, of course, but now you have a much clearer idea of where you want to go and how you want to get there, and what you need to spend energy giving to the audience versus what the audience is willing to take on faith.

#2. AS YOU NEAR THE END > START A PROJECT NOTES FILE (IF YOU HAVEN'T ALREADY)

One of the things that will keep you from the finish line is that you'll have a cool idea, but think, "oh, this means I have to go back and restructure some things to make this work, but I like this so much I want to make it work, so let's..." and you pick up the shovel and your enthusiasm falls out of your butt.
Don't stop your forward momentum. If your new idea requires rewrites of earlier material, take a few sentences of notes in the alternate document of "project_revision_ideas.txt", just to get the idea out of your head. Then, continue as you were, with the assumption that the work has already been done, the past has already been corrected. If you know where you are and where you are going, you can trust the past to explain itself later; do not go back to it and kick cans around while you still have work to do in the moment of the story. A great advantage of this technique is that when the time does come for you to gird your loins for the next draft, why look, you already have what's basically a roadmap to the weak spots. As an optional aside to this:

#3. USE COLORS 

You might be amazed at the power of this technique: when something is obviously not quite right, but difficult or impossible to make right at this very moment... write FIX THIS next to it and make it bright pink and bold. Then move on to the next paragraph and keep going. If you move far enough ahead, the next time you open the manuscript and see that, you'll be like, oh, right, and do a sentence or two that writes out your original half-assed flailing at an idea and replaces it with an actual idea that you worked out below in the meantime. This is preferable to spending hours banging your head on this one part because it had to be just right just then. Colors, shapes, pictures or links to pictures: if you can't the words you want on paper, it's OK to just put down a signpost or a reference, as long its clear enough that you'll know what it means later. This is a technique stolen from programming, where early code is studded with TODO and HACK FIX THIS. Don't let contemplation of tomorrow's work keep you from doing today's.

#4. AS YOU NEAR THE END > ANTICTIPATE AND EMBRACE "POST-PRODUCTION"

Making a complete first draft is like passing a god damned kidney stone, you definitely don't want to go back into that bathroom for at least a little while. However, whatever indexing, appendixing, formatting, and bow-wrapping details your particular project requires still stand between you and rung zero, which is the first person who is going to read it and tell you it sucks. Don't be passing around raw unformatted files of rambling text. You don't buttonhole people on the street and yell at them unceasingly, do you? Have some manners. Respect the potential person whom you are asking to read your work by presenting them with something that is not visually worse than what they already have to read during the course of the day. Going back over your work in various ways just to make it more visually presentable is a necessary part of finishing, and if you've already adopted a grim death march attitude by three quarters through your first draft, you are just not going to have the stomach for it. As you near the end, anticipate what you are going to need to do so it's not a surprise, and recognize that writing THE END doesn't mean you can run out into the street with it. BUT, heed the corollary:

#5. DON'T DO FORMATTING WORK IN THE MIDDLE OF A DRAFT

More generally: don't do anything during a draft that could potentially result in multiple conflicting versions of a draft. If you catch yourself copying draft text from one program to another, slap yourself. How many problems are you trying to cause for yourself here? Seriously? You want to spend hours reconciling multiple versions of a second or third draft?
You can do pretty stuff between drafts. If you started in a spiral notebook, finish the draft in the damn notebook. If you started on a PC, finish on a PC and do the next draft on your new Mac or whatever. Never split a draft.
The reason this happens is because writing requires you to switch between states of intense concentration and kind of mind-wandering states, and it's gratifying in a masturbatory way to idly reformat your draft. You are walking down the devil's path if you do this. Find something else to do while daydreaming. I hear some people enjoy Sudoku.

#6. GET TO THE PART YOU WANT TO WRITE TODAY (OR TONIGHT) AND WRITE THAT

An example: in your story, Farmer Bob purchases and wrecks a Maserati. In order to do this, he has to fleece some bankers and relatives, and perform various identity sleight-of-hand tricks. You feel compelled to make that part real because this is a realistic story, but the story started in your mind with this dream of Bob hauling ass on a twisty Iowa two-lane, and that's all you can think about as you write him in the banker's office, submitting his loan application and twisting his cap in his hands... The point is, any of your precious god-given time you manage to carve out to sit down to do the work, write the part you want to write. It's the least you can do for yourself. If you feel a need to sketch out some justification for getting to that part, give it a sentence or two. That stuff you write when you are seized by an idea and write what you want is always going to be loved and admired and actually read the most of anything you do, because it has your fire and your heart. If instead you are always having to set up a tedious house of cards to push your characters to where you want them to be, like a desperate Axis general shuffling units around a mock battlefield with one of those little pushbrooms... well, you're fucked, you've lost the thread. If you pursue the part you want to write, one of several things will happen: 1. Once your mind is free of the obsession of the part you wanted to write, it will naturally turn to other thoughts and any necessary exposition will fill that space. Then you write it and you're good. Or 2: if the part you wrote just isn't workable, you gained a deeper and necessary understanding of the story you really wanted to tell. Maybe the Maserati episode happens in a dream Bob has when he falls asleep driving the tractor. Maybe that dream never even makes it to the final draft, but your understanding of it informs Bob's character and makes your work better. Never fear that you are wasting time because you are writing the part you want to write, that's never the case.

#7.YOUR AUDIENCE NEEDS LESS EXPOSITION THAN YOU THINK THEY DO

A scene of two characters speaking about events in the present, if written skillfully and with the intent to do so, can cast strong light and shadow on any character's past, while never insulting the audience with a blunt disclosure that doesn't suit the conversations or the characters. An example: Billy has been in prison for five years. When his mother and his fiancee meet for the first time, for lunch, don't feel compelled to have either of them trumpet this fact just because it's the most important fact in their relationship. For this very reason, have them act like real people would, and tiptoe around the particulars of the subject, until the audience is wondering "what happened to this guy, did he get sick, did he go visit a guru in Tibet?" This segues nicely into:

#8. YOUR AUDIENCE WANTS TO DO SOME (EASY) WORK (THAT MAKES THEM FEEL CLEVER)

Your audience did not come to your entertainment to have every question they might consider answered immediately. By the same token they probably did not come to sit through some inscrutable outpouring of free assosciations. They probably came to participate in a give-and-take with you, that is enetrtaining, intellectually stimulating, and emotionally rewarding. They want to leave your work feeling like they know the world a little better, and they can't do this if you don't give them anything to think about. Conceal and reveal. Exposition and (ugh) "world-building" are not things you do at the beginning of a story, like the yellow Star Wars text crawling up the screen, to ground your audience in the self-imposed rules of your fiction, unless you are the worst kind of hack. If your fictional world (be it Castle Azgoth or 1973 Jersey City) has boundaries, and common knowledge, and things that mean certain death, let the reader learn about those things alongside the characters that they have become invested with (if you reach page 2, you are probably invested with a character). Don't profess: let characters experience the world, and bring the reader along. The same goes for realism: don't tell us the husband suspects infiedlity, show us late nights alone while the wife is at the office, and callers that hang up when they hear a man's voice. Trust the reader a little bit and the reader will be more interested. Treat the reader like a moron and they will hang up on your story.

#9 EMBRACE GENRE, REJECT GENRE, COMMENT ON GENRE 

Cliches get a bad rap. cliches are the Jungian archetypes of colloquial speech. Don't be afraid of an idea because it seems too familiar. There is nothing new under the sun... except you and your perspective on the stories we've all heard a thousand times already. If you have a new way to tell it, the whole world might listen.

Wednesday, January 15, 2014

The Terrible Twos


I think level two is looking pretty good, and I'm only about a month and a half behind schedule and have just a few major features to implement, and I have plenty of time and mental energy after my psychically non-demanding job to pour into all my portfolio projects...

Well I do think it looks alright, compared to the opening level. I'm learning. When designing level one, I had just discovered some tutorials on making grids of tiles in Unity, and was enraptured by this idea of a "tile engine" and all the games I was going to make with it, and somehow got it into my head that the various buildings all had to be made of tiles, and somehow all the tiles had to draw from a single 256x256 sheet, so all those brick buildings in the first level are made up of instances of like BRICK_BLDG_LOWER_LEFT_WINDOW tiles and it's horrible. The great joke is that I thought this was somehow saving me memory as I blithely constructed fifty different materials all drawing from the same tile sheet. In the end I don't think it matters much but I made a lot of extra work for myself to produce something that could have looked better.

In a way, though, I did make a bit of a tile engine, because when it was time for level two, I duplicated level one and just took everything out, save for the backgrounds and a single tile for the player to land on when I pressed start. I wanted to sculpt out a shape for the walkable areas of the level, and I started duplicating that tile and moving it around. Soon, I found I wanted images of the mid-ground scenery, I think of these in my mind as "flats", the is from theater, where it means a large frame built from plywood, with canvas stretched over it, on which is painted something like a castle battlement, or a storefront, or a brick wall.

A flat here is just a gameObject with a Mesh Filter and Mesh Renderer components. The Mesh is a Quad, and the renderer's material is made to point at a Texture of X by X (powers of two, please) and you have onscreen whatever it is you drew in your favorite "draw stuff" program. Just place these further away on the z-axis than your player and the ground he or she must collide with, and you have 2d scenery.

Glossing quickly over the quality of the art itself (yyyyeah), we come to the matter of the ghost, who presented a particular problem. Enemies in WAFHGame execute a function called "Patrol", and enemies are a sub-species of character, that is to say the script WafhEnenmy.cs is a class which inherits from WafhChar.cs. The problem is that WafhChar only executes the Patrol function during its update if the Char in question is Grounded. 

Being Grounded in Unity is a particular state, like being flat-footed in D&D. (if you get that reference, I'm sorry). The Character Controller is Unity's answer to one of the common beginner game dev problems: "I need some object that represents the player than can listen for all sorts of input, and respond naturally to all sorts of things, and inteact with both the environment and anyone else around, in all possible types of games." A lot of people seem wary of the Character Controller, but for a lot of us it's a godsend and we wouldn't be here without it, honest to Bob. Code low-level game controller input systems? Are you out of your mind?

So here we had a ghost who would not Patrol, because since I set his character's gravity to 0, to keep him from falling, he never acquired the property of being Grounded, so he would never Patrol. First thought was to allow Patrol to fire when not Grounded. Now, as I'm writing this, I wonder "why didn't I just remove the Patrol check from the conditional of being Grounded? Let falling skeletons Patrol in the air for a few fractions of a second when they fall off a platform, they're not hurting anyone." A noble thought, and yet my baser natures won out. My memory grows dim. I can say for sure I ended up extending WAFHGhost from WAFHChar: 


public class wafhGhost : WafhEnemy {
 //ghostly vars:
 float waverYVariance = 16.0f; //how spooky do we get
 float maxY;      //calcd from current position
 float minY;      //calcd from current position
 Vector3 currentPos;
 float InitialY;     //source for calcs
 float currentY;     //source for calcs
 float targetY;     //where we are going
 float targetDistanceDelta = 1.0f; //dunno
 CapsuleCollider capsule;
 Vector3 vertTarget;
 Vector3 myCenter;
 Vector3 ghostVelocity;
 float ghostYSpeed;
 
 //public GameObject debugCubePrefab;
 //GameObject debugCubeClone;
 
 public override void Start () {
  
  base.Start();
  gravity = 0.0f;
  InitialY = transform.position.y;
  maxY = InitialY + waverYVariance;
  minY = InitialY - waverYVariance;
  capsule = transform.GetComponent();
  //debugCubeClone = Instantiate (debugCubePrefab, transform.position, transform.rotation) as GameObject;
  ghostYSpeed = 50.0f;
  deathOffset = new Vector2(0, 0);
 }
 

 public override void Update () {
  base.Update();
  myCenter = new Vector3(transform.position.x + (controller.center.x), transform.position.y + (controller.center.y), transform.position.z + (controller.center.z));
  GhostlyShenanigans();
  ghostPatrol(patrolLimit);
  //Debug.DrawRay(myCenter, debugCubeClone.transform.position, Color.green, 0.1f);
  
 }
 
 void GhostlyShenanigans()
 {
  currentY = transform.position.y;
  AdjustGhostVelocity();
  GhostFloat(transform.position.y);
 }
 
 public override void Patrol(float patrolLimit)
 {
  base.Patrol(patrolLimit);
 }
 void AdjustGhostVelocity()
 {
  if (transform.position.y > maxY)
  {
   //Debug.Log("broke max, moving down");
   ghostYSpeed = -ghostYSpeed;
   
  }
  if (transform.position.y < minY)
  {
   //Debug.Log("broke min, moving up");
   ghostYSpeed = -ghostYSpeed;
  }
  ghostVelocity = new Vector3(0.0f, ghostYSpeed, 0.0f);
 }
  
 void GhostFloat(float currentY)
 {
  //transform.position = Vector3.MoveTowards(transform.position, vertTarget, targetDistanceDelta);
  controller.Move( ghostVelocity * Time.deltaTime );
 }
 


 void ghostPatrol(float patrolLimit)
 {
  state = CharacterState.Walking;
  patrolTime += Time.deltaTime;
  //print ("patrol time " + patrolTime);
  if (patrolTime < patrolLimit)
  {
   //the ifs in here are to handle enemies with flipped textures cause I suck at art asset generation
   if (facing == 1)
   {
    velocity.x = walkSpeed; 
    if (gameObject.tag == "enemy")
    {
     ragePixel.SetHorizontalFlip(true);       //texture flip
    }
    else
    {
     ragePixel.SetHorizontalFlip(false);
    }
    ragePixel.PlayNamedAnimation("WALK", false);    //texture animation
   }
   if (facing == -1)
    {
     velocity.x = -walkSpeed;
     if (gameObject.tag == "enemy")
    {
     ragePixel.SetHorizontalFlip(false);       //texture flip
    }
    else
    {
     ragePixel.SetHorizontalFlip(true);
    }
     ragePixel.PlayNamedAnimation("WALK", false);    //texture animation
   }
  }
  else
  {
   //print ("FLIP FUCKER");
   patrolTime = 0.0f;
   facing = -facing;
  }
 }
}



We are Patrolling every Update, but this is really the same RagePixel code we use in the main Patrol function, it's a copy/paste. This, as far as I have gathered, is one of the sadder and more shameful sins you can commit writing code: having to write something out in more than one place. It is an invariable sign that you have failed to optimize, misaligned your architecture, fucked up in general.

None of this is what I was meaning to write a post about. I meant to write about a particular revelation during the last phase, the placing of the bus transfers, the deciding which branches should have enemies walking (or floating) back and forth on them: I sat down to the business of level design and realized I had already made most of the important decisions already.

"Level Designer" is the cap I like to picture myself wearing, and it's a role I've made a decent living holding before, and I've often thought of the level design "parts" of making WAFHGame as satanically delicious treats to gorge on when I had finished the Volga Boatman gruntwork of asset preparation. Well, as it turns out, when I decided the size of the house and store flats, that was level design, because that, combined with the speed of the player (also level design) determines how fast the scenery moves by, which affects how your eye registers it and both how much attention you pay to it and at least part of your emotional response. Drawing the trees, and deciding how far apart branches should be, that was level design, so is the scroll speed on the parallax skyscrapers. Anything that happens to you in the level is by definition a result of level design, at some level.

Of course for tiny projects, level and gameplay design are inseparable, but I think it's an easy thing to lose sight of as projects get bigger. The size of the tile and the length of the jump are bigger level design factors than anything else in WAFHGane, and it's impossibl to make any level design decisions that don't depend omn those. I keep trying to adjust  the jump as I go, and it often feels unsalvageable and floaty and tedious, and I want to start over, but of course that's insanity. It just has to get done, but every decision, even the most frivolous-seeming art decision (sorry artists) has direct effects on gameplay and are a core component of level design. As someone like Timothy Leary might have said: "The more I get into it, the more it's all one thing".

Three levels, with different art. Each level has a pair of enemy types. Some enemy types repeat, but pairs don't. There are a few unlocks you can get by beating levels in particular ways. The experience flows smoothly, from menu to gameplay, with appropriate UI popups, without crashing.

That's it, not even a leaderboard, no points even! How hard could it be. And yet here I sit, blogging. Not working! Not drawing skeletons! Shame on me for reals. I'm 2/3 done.