Tuesday, January 17, 2012

If You Can Name It, It's An Object

This is a continuation of a previous post on creating better design.

What happens when your class becomes a giant ball of garbage and you have no idea what anything does anymore. I throw it away and start again. Let's consider a Physics World object. What does it do? What is the role of this object? Let's start from the simplest example to the more difficult. I am going to ignore the double dispatch problem for now and completely ignore the issues regarding strong types in C++, but I will be taking advantage of that behavior.

Let's go back to the old example of my simple graphics app.

void main()
{
   System system(640, 480);
   Sprite ball("Ball.PNG");
   ball.velocity(1.0f, 1.0f);
   ball.position(10.0f, 20.0f);
   PhysicsWorld world();
   Scene scene();
   world.Add(ball);
   scene.Add(ball);
   Boundary bounds(0,0,640,480);
   world.Add(bounds);

   Timer timer;
   Keyboard keys;
   Mouse mouse;

   while(!keys.AnyKey() && !mouse.RightButton())
   {
      world.update(timer);
      scene.update(timer);
      Renderer renderer;
      renderer.Render(scene);
   }
}

Let's take out everything except what relates to the Physics World object.
void main()
{
   Thing ball();
   ball.velocity(1.0f, 1.0f);
   ball.position(10.0f, 20.0f);
   PhysicsWorld world();
   world.Add(ball);
   Boundary bounds(0,0,640,480);
   world.Add(bounds);

   Timer timer;
   Keyboard keys;
   Mouse mouse;

   while(!keys.AnyKey() && !mouse.RightButton())
   {
      world.update(timer);
   }
}

Since this is not a post about how physics engines work, I'm going to ignore a lot of details. The details I'll be omitting is not relevant to this discussion, but is Critical to a proper Physics Engine!

OK. First I have changed the object from Sprite to Thing. I'll be writing about this object family in another post, but it's pretty complex and involves a lot of template meta programming. For our purposes what matters is that it has a physical body. It has physical properties like velocity and position. It has something that can be collided with, and since it moves, it can collide. That's an important concept when we consider efficiency.

Next we create a world. Really not much to say about that. Then we add the ball to the world. We're using overloaded functions so we only have the function Add(). This makes it easier to use the object because there is less to memorize. If you think you can add it to the physics world, you should be able to just Add() it. You don't have to memorize what kind of add to use. In the case of my real code the Add() is more complicated because the objects being used are collections of policy classes and the user may not know what the actual type is with regards to the Physics World object. This brings up a couple of important points.

Make your objects easy to use right and hard to use wrong.


Creating multiple Add functions is not as easy as having everything in physics derive from a common base class, but this adds an unnecessary burden to the user of your code by requiring a tight binding that may be undesirable or impossible, and makes the add function hopelessly cluttered with conditionals.

An objects type is as valid a piece of data as anything else.


The fact that I have a Dynamic Body, information that is hidden in this example, means that I know extra processing will have to be done that is unnecessary if the object is a Static Body. While I could have an if statement or a switch statement add objects to the correct container, why not let the compiler do it? This creates two clean, simple functions rather than one god awful mess. This will become more obvious as we continue.

When we add bounds, we are doing the same thing except this time bounds is a Static Body. When we get to the update function we don't have to update the position of bounds.

OK, but what if we want to have gravity?

Too often Physics worlds include gravity as part of the world object. This makes physics worlds very complex. It's not just gravity they include, but also ground planes and up vectors, and all kinds of other crap. So how do we get the same complex behavior in a simplified system?

Let's create a Gravity Object.

void main()
{
   Thing ball();
   ball.velocity(1.0f, 1.0f);
   ball.position(10.0f, 20.0f);
   COR cor(0.7f);
   ball.Add(cor);

   PhysicsWorld world();
   world.Add(ball);
   Boundary bounds(0,0,640,480);
   world.Add(bounds);

   // Default constructor assumes gravity is 1.0f in the -Y direction. 
   Gravity gravity;
   world.Add(gravity);


   Timer timer;
   Keyboard keys;
   Mouse mouse;

   while(!keys.AnyKey() && !mouse.RightButton())
   {
      world.update(timer);
   }
}

OK. We created Gravity and added it to the world. That's pretty simple. We could have changed properties if we wanted different behavior. What the heck is that COR? It's the Coefficient Of Restitution. It makes the ball not perfectly bouncy. I actually do this a different way in my code, but without getting into how Generic Programming works it's easier to think of it this way. And if I didn't have other tools available it's how I would handle complex behaviors of physics bodies.

Let's change the code completely and have the ball floating in space with a giant planet.

void main()
{
   Thing ball();
   ball.velocity(1.0f, 1.0f);
   ball.position(10.0f, 20.0f);
   COR cor(0.7f);
   ball.Add(cor);

   PhysicsWorld world();
   world.Add(ball);
   Boundary bounds(0,0,640,480);
   world.Add(bounds);

   // Default constructor assumes gravity is 1.0f in the -Y direction. 
   PointGravity gravity(150.0f, 150.0f);
   world.Add(gravity);


   Timer timer;
   Keyboard keys;
   Mouse mouse;

   while(!keys.AnyKey() && !mouse.RightButton())
   {
      world.update(timer);
   }
}

That's all there is to it. How about we make an atmosphere to slow us down?

void main()
{
   Thing ball();
   ball.velocity(1.0f, 1.0f);
   ball.position(10.0f, 20.0f);
   COR cor(0.7f);
   ball.Add(cor);
   COD cod(0.8f);
   ball.Add(cod);

   PhysicsWorld world();
   world.Add(ball);
   Boundary bounds(0,0,640,480);
   world.Add(bounds);

   // Default constructor assumes gravity is 1.0f in the -Y direction. 
   PointGravity gravity(150.0f, 150.0f);
   world.Add(gravity);

   PointAtmosphere atmos(150.0f, 150.0f, 15.0f);
   world.Add(atmos);


   Timer timer;
   Keyboard keys;
   Mouse mouse;

   while(!keys.AnyKey() && !mouse.RightButton())
   {
      world.update(timer);
   }
}

We add a drag coefficient to the ball and add an atmosphere to the gravity source. Well, that's OK, but this is for my physics class and that really doesn't cut it.

How about this?

void main()
{
   Thing ball();
   ball.velocity(1.0f, 1.0f);
   ball.position(10.0f, 20.0f);
   COR cor(0.7f);
   ball.Add(cor);
   COD cod(0.8f);
   ball.Add(cod);

   PhysicsWorld world();
   world.Add(ball);

   Planet earth(1.0f, 6,371.0f);
   Atmosphere atmos(1.0f);
   earth.Add(atmos);
   world.Add(earth);

   Timer timer;
   Keyboard keys;
   Mouse mouse;

   while(!keys.AnyKey() && !mouse.RightButton())
   {
      world.update(timer);
   }
}

You could continue and make the whole solar system and if you get the data correct, you will have a stable system, or at least as stable as the real one is.

Notice that I add a planet, but do not extract the gravity from it and add that as an additional step. Why should I? The function is customized for planets so why wouldn't the function do it? Also, I could add atmosphere to a region of space or to a planet. In either case the atmosphere is the same. I did use point instead of ambient, but the principle is the same. I could extend the concept of point atmosphere to add a hurricane to the earth.

We had a ball and rather than giving the ball properties, we instead created adverbs to append properties. So we had a "High drag bouncy ball." Rather than creating complex objects we created a noun phrase. "A planet with the same mass as earth and a radius of 6,371 km and one standard atmosphere." We could have added water and a molten core if we wanted.

I hope I made a reasonably clear case for not only creating a language to describe your problem, but also to use language constructs to build your language. 

Monday, January 16, 2012

Don't Solve The Problem. Write The Language That Lets You Define The Problem.

I've often used an example to explain why so much code is big, bloated, hard to follow, and impossible to maintain.

Imagine you have a need in code to answer the question what is 2 + 2? You sit down and write the function, after counting on both your toes and your fingers to ensure the answer is portable:

int TwoPlusTwo()
{
   return 4;
}

Feeling very pleased with the speed and efficiency of the code and how remarkably exception safe it is you continue on. And you need 3 + 5.

At first you think I'll just copy and paste that other function and make it return 7. But something inside says that's not the right approach. This one function should be able to do more than that one line. So:

int TwoPlusTwo(int val)
{
   if(val == 2)
   {
      return 4;
   }
   else
   {
      return 8;
   }
}

But you look at this and inspiration strikes!

int Equation(int val)
{
   return pow(2, val);
}

I know in this trivial example it's easy to see that this is stupid, but it is very common to write this. I've seen this code from Microsoft, EA, Sony, all the big companies have plenty of this king of code.

What's missing and why do we write this way?
We are writing the answer over and over. When we learned math we did not memorize every possible outcome of every possible equation. Instead we learned a mathematical language. What's missing then is not the solution. It's the language we need to express a solution. When I write:

2 * 35 + 64 / 3 - 125^14

I don't know the result of that, but if I entered that into a calculator I would get the answer! The guy who made the calculator didn't even know that question would ever be asked and yet it gives a result.

How about another less trivial example. I won't write the language, but I want to show how a properly written language can express complex ideas in very simple terms. I'll write in psuedo code to avoid language dependency and the C++ template arguments that would make it less clear.

Here is my cross platform graphics engine in use for a simple example:

void main()
{
   System system(640, 480);
   Sprite ball("Ball.PNG");
   ball.velocity(1.0f, 1.0f);
   ball.position(10.0f, 20.0f);
   PhysicsWorld world();
   Scene scene();
   world.Add(ball);
   scene.Add(ball);
   Boundary bounds(0,0,640,480);
   world.Add(bounds);

   Timer timer;
   Keyboard keys;
   Mouse mouse;

   while(!keys.AnyKey() && !mouse.RightButton())
   {
      world.update(timer);
      scene.update(timer);
      Renderer renderer;
      renderer.Render(scene);
   }
}

There really isn't much I left out except for some template fields. And I do usually write with many comments and I'll write a post on why and how I go about writing in another post. But the thing to note here is that it's pretty easy to see what's going on and it's pretty easy to add more things if desired. The System object turns a console app into a Windows app or a Mac app or even PS3 or Wii. There is no need for a WinMain() or Objective C.

Sprite is the object least according to it's own form. It would have a template list of everything it was composed of. It's policy classes. I'll write about that, but much later. For now just know it can be a render object or a physics object, but it inherits from nothing in the classic understanding of inheritance.

The Physics world detects and dispatches collisions, but under the hood all it really does is present close by objects to each other and let them work out if they hit or not.

Scene is an object for rendering views.

Boundary is an area that restrains physics.

Timer is the local clock with some extra abilities, and keyboard and mouse are pretty self explanatory.

Renderer is an object I put in the main loop because I wanted to point out something. When you write small, simple, and lightweight objects you don't have to worry as much about efficiency. Here it makes no difference where I put it from the point of efficiency.

So what you get is a ball bouncing around the screen like if you leave your DVD player on too long. An entire graphics application start to finish in 25 lines of code. It wouldn't take much more than that to make break out, space invaders, defender, or other arcade games because I wrote the language of arcade games. All I have to do is assemble sentences.

Let me know what you'd like to understand better and I'll make that my next post. Otherwise I'll go on to how I start a new project.