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.