Friday, November 29, 2013

In The Trees, In The Weeds


I've entered some impossible place where every task takes twice as long as the previous task. The goal was and is still three playable levels, with a few neat unlocks and a couple more enemies. Level 2 is supposed to represent Seattle's Queen Anne neighborhood, with its imposing hill spreading up the right side of the screen. There's not much platformer action to be had on a single steep hill, so what to make platforms out of?

Pioneer Square had the easy visual metaphor of that big iron lattice pergola (yeah I had to look up what the hell that thing's called), but nothing so obvious exists in QA. Trees were an obvious solution, I remember planning to have a few in one area or another, and they are about as classic a platform-disguise trope as you can get. One problem that arose quickly is that I can't draw a convincing tree.

The method I ended up with is far from ideal but somehow the appearance was able to barely pass my internal editor. After all, I had to come up with something. I drew out the trunk and each branch, as well as four squiggly bare "sub-branches". I flipped each of the sub-branches for a total of 8, then took a green 32x32 "leaf" and used it as a stamp across all of those, as shown here:



Then each individual branch of the tree gets stamped with a selection of these sub-branches and attached to the trunk. I made each branch its own gameObject because I had this idea of scripting them all to sway gently in the breeze, which I may still do but whenever I picture it now it looks like it would be unsettling, which is maybe a point for and not against. I've thought also about doing a lighter green semi-transparent globe of leaves behind each tree to give them a more traditional tree-like appearance, but I'm not sure I could really get it right.

The tree branches also presented an opportunity for varied routes through the level, something more interesting than flat planes, so I rigged up the hinged box colliders shown at the top. Now, to do this kind of platforming right, you need for the player to be able to pass through these colliders when jumping from beneath. You can't make them go to the end of each branch and then try to reach the one above, it wouldn't work. There are many ways to do this.

One important point is that each face of a collider only works in one direction. So theoretically the easiest solution would be to use, instead of box colliders, mesh colliders with a single quad sprite mesh, facing up. Player passes through from the bottom, collides on the way down.

I didn't go with this, because some early experiences and research led me to understand that single plane collision with a 3d object in a 3d environment is inherently just a bad idea. You're subject to "tunneling", where the surface area that records a hit is so narrow that your colliding object can pass through it between the high-speed snapshots your physics simulation is taking of the game state. This Unity Amswers user sums it up. 

Another solution, likely quite easy for many, would be to take the standard Unity box collider, import into Blender or what have you, remove the bottom, and export back to Unity. Custom collision mesh, very nice. Problem is I don't have any skill points in 3D modeling.

"It's free, there are tutorials, you could learn to do that much in an afternoon!" Yes but I have a series of goals here and I can't be endlessly sidetracked. Maybe my next game will involve custom 3D models but this one doesn't and I need to finish it with the tools at hand. That's the theory anyway: when everything is taking twice as long as you thought, don't go looking for hours of work to put between you and the next thing, just find a better way to solve the immediate problem. You spec the talent tree you're working with the XP you have and you play your class, you don't try to be All Things At Once.

So it turns out Unity has a feature called Layer-Based Collision Detection that's actually more or less perfect for this. The tree branches go on one layer, the player goes on another, then we ask a question, is the player moving up?
 
void CheckForPassPlatform()
{
 if (!controller.isGrounded)
 {
  jumpThisPos = transform.position;
 
  if (jumpThisPos.y > jumpLastPos.y)
  {
   Debug.Log("ignoring branches");
   Physics.IgnoreLayerCollision(10, 13, true);
  }
  else
  {
   Debug.Log("hitting branches");
   Physics.IgnoreLayerCollision(10, 13, false);
  }  
  jumpLastPos = jumpThisPos;
 }
}

I probably don't need that isGrounded really. Like I said it's more or less perfect, but the less happens becuase of my hinged collider platforms. There are occasionally cases when the player is coming down from the apex of a jump that took them halfway through the platform above. The player won't hit the thing they are already in the middle of, but if the player is also moving sideways they will bump into the next piece of collider at the hinge. The player is heading down so the collision layers are reading each other, which we would want if the player were above the collider, but since the player is to the side, we don't want this.

Well, I know there's an answer here, and I know it involves raycasting, so I've been investigating that, but I find raycasting difficult to use. The ray is invisible, so you have to draw it using Debug.DrawRay, but Debug.DrawRay takes a different set of arguments than Phsyics.Raycast, so how do you know your debug line is really the same as your raycast line?

I guess when the player is coming down from a jump, we raycast from the player's center to a point just beneath the player's feet. If there is a collision there, we allow the layers to collide, otherwise we don't. This should allow the player to pass through side collisions on the way down.

I really hope I don't get any cool ideas like this for level 3.

No comments:

Post a Comment