Chapter 4 introduces Sphere World, an app that progressively improves as the chapters roll by. This first version simply draws the ground as a gridded plane, with a spinning torus at the center, orbited by a small sphere. Fifty other spheres are distributed randomly across the plane (they’re all at the same height), and the user can use the arrow keys to move around.The torus and all the spheres are drawn as wireframes in this version.
Getting it working required fixing the usual minor bugs, plus three interesting issues. I had to look up how to deal with keyboard events, and in doing so discovered that the constants for the arrow keys (like
NSUpArrowFunctionKey) didn’t match the keycodes that came in when I pressed the arrow keys. I hard-coded the relevant values, but I need to find out how it’s actually done to get away from having “magic numbers” in my code. I also forgot at first to override acceptsFirstResponder to return
WorldView. That resulted in the app beeping at me with every keystroke, very annoying.
Next up, the randomly drawn spheres didn’t appear. The problem turned out to be that the
ApplyActorTransformation method of the book’s
GLFrame class was producing an all-zero transformation matrix. That turned out to result from the
GLFrame instance having zero vectors for the
Those should have been initialized to unit vectors in the constructor, which got called once, apparently without setting the
forward vectors for any of the spheres. Since the spheres were in a static array, I thought that they would be initialized automatically, without using
new like you would do for a pointer-based instance. At any rate, I set the vectors (to their defaults!) when initializing the locations of the spheres, and that took care of things.
I also spent some time figuring out how to make the animation look smooth. The first idea was just to make the incremental change in position or angle smaller, which helped with forward and back motion, but didn’t do much for rotation. The second idea was to increase the frame rate, which didn’t do much except change the speed at which the torus and orbiting sphere moved.
The idea that worked was to move or rotate a bit with each frame. I checked how often the
keyDown: methods were being called when holding down a key for autorepeat. That turned out to be five (occasionally four) calls to
drawRect: per call to
Based on that, I had
keyDown: set a value for a counter (positive for forward or right, negative for backward or left), and then
moveObjects (called from a timer) would check the counters. If they were non-zero, it would decrement or increment the counter toward zero and move or rotate appropriately. That gave swift smooth motion: very nice!
Now on to chapter 5…