Tau Engine now open source!

I’ve released Tau Engine, the 2d game engine that powers my first game, Galaxy Wild, under an MIT license.

You can find the source code here on Github.

Posted in Announcements | Leave a comment

How to animate objects with GLKit (Part 3)

This is the twelfth post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

This post will teach you how to animate your sprites.

Iteration 17: Sprite animation

Sprite animation is nothing more than a flip book: you change the texture to different frames of your animated sprite. It’s like an animated gif. In fact, I’m going to use an animated gif that I found on AnimatedGif.net as the example.

If you have Photoshop, you can easily extract each frame to a separate image as it encodes them as separate layers.

Sprite animation really only requires two things: your frames, and how long to display each one. We’ll encapsulate this image in a separate animation class, storing the frames as ready-to-go textures. Note that we’re skipping error detection for ease here, but robust code would include it. We’re also not storing separate texture coordinates for the animation—our assumption is that our animated object is already using a sprite and has texture coordinates set up, and that our animation sprites have the same characteristics, i.e. same size and shape. An alternate implementation might not make these assumptions, especially if sprite sheet support was necessary.

// EESpriteAnimation.h
@interface EESpriteAnimation : NSObject {
  NSArray *frames;
  float timePerFrame;
}
-(id)initWithTimePerFrame:(float)timePerFrame framesNamed:(NSArray *)frameNames;
@end
 
 
// EESpriteAnimation.m
-(id)initWithTimePerFrame:(float)time framesNamed:(NSArray *)frameNames {
  self = [super init];
  if (self) {
    timePerFrame = time;
    frames = [NSMutableArray arrayWithCapacity:[frameNames count]];
    for (NSString *name in frameNames)
      [(NSMutableArray*)frames addObject:
      [GLKTextureLoader textureWithCGImage:[UIImage imageNamed:name].CGImage 
                                   options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:GLKTextureLoaderOriginBottomLeft] 
                                     error:nil]];
  }
  return self;
}

Now we need to be able to know what frame of the animation we’re on at any given time. For this we’ll add a property to store how much time has elapsed, an update method to increment it, and a method to get the texture for the current frame.

// EESpriteAnimation.h
@interface EESpriteAnimation : NSObject {
  float elapsedTime;
  ...
}
-(void)update:(NSTimeInterval)dt;
-(GLKTextureInfo *)currentFrame;
...
@end
 
 
// EESpriteAnimation.m
elapsedTime = 0; // in initWithTimePerFrame:framesNamed:
 
-(void)update:(NSTimeInterval)dt {
  elapsedTime += dt;
}
 
-(GLKTextureInfo *)currentFrame {
  return [frames objectAtIndex:((int)(elapsedTime/timePerFrame))%[frames count]];
}

Next we need to associate this animation with one of our shapes. Since it doesn’t make sense to have multiple sprite animations active at once, we’ll add a simple instance variable.

// EEShape.h
 
@interface EEShape : NSObject {
  EESpriteAnimation *spriteAnimation;
  ...
}
@property(strong) EESpriteAnimation *spriteAnimation;
...
@end
 
 
// EEShape.m
@synthesize spriteAnimation;

The only things left are to update our update code to update the animation, and to update our rendering code to use the animation if it’s available.

// EEShape.m in renderInScene:
-(void)update:(NSTimeInterval)dt {
  ...
  [spriteAnimation update:dt];
}
 
-(void)renderInScene:(EEScene *)scene {
  ...
  if (self.texture != nil) {
    effect.texture2d0.envMode = GLKTextureEnvModeReplace;
    effect.texture2d0.target = GLKTextureTarget2D;
    if (self.spriteAnimation != nil)
      effect.texture2d0.name = [self.spriteAnimation currentFrame].name;
    else
      effect.texture2d0.name = self.texture.name;
  }
  ...
}

Now we test it with a scene…

// WalkingAnimationScene.m
-(id)init {
  self = [super init];
  if (self) {
    EESprite *sprite = [[EESprite alloc] initWithImage:[UIImage imageNamed:@"alfred0.png"] pointRatio:100];
    NSArray *frameNames = [NSArray arrayWithObjects:@"alfred1.png", @"alfred2.png", @"alfred3.png", @"alfred4.png", @"alfred5.png", @"alfred6.png", @"alfred7.png", @"alfred8.png", @"alfred9.png", nil];
    sprite.spriteAnimation = [[EESpriteAnimation alloc] initWithTimePerFrame:1.0/9 framesNamed:frameNames];
 
    [self.shapes addObject:sprite];
  }
  return self;
}

iteration-17a

If you combine sprite animation with transformation-based animations, you can create the types of effects seen in most 2D games. Here’s our character walking across the screen.

// WalkingAnimationScene.m in init:
sprite.position = GLKVector2Make(3, 0);
sprite.velocity = GLKVector2Make(-1, 0);

iteration-17b

There are many other ways to implement this, as well as potential optimizations like using a sprite sheet. But all approaches will be similar in that they use multiple images that change over time.

There may be more articles in this series in the future, but that’s it for now! If there’s a particular topic you’d like to see covered, feel free to email me at ian.terrell@gmail.com.

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment

How to animate objects with GLKit (Part 2)

This is the eleventh post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

This post will teach you how to animate any attributes of your objects’ through specifying what they should be and a duration that the animation should take.

Iteration 16: Animating objects manually

Manually animating position

Not all attributes of your objects have natural correspondents to velocity and acceleration. Color and scale, for instance, do not (although you could invent them if you wished; perhaps growth rate for scale). And sometimes even for position and rotation you’ll want to manually animate them, as it will be simpler than using velocity and acceleration.

To animate an attribute manually you need to know how much it’s going to change and how long it’s going to take to do it. With that information you can calculate each time step’s contribution to the value change.

There are any number of approaches you can take here, and any number of APIs you could create, but we’ll do the following:

  • Create a class to wrap our animation information.
  • Give shapes an array of animations to apply to their attributes.
  • Every time step, iterate through the current animations and update our attributes with their incremental values.

Let’s start out simple and just animate position as we build out our animation class. We can add in the rest as we go.

// EEAnimation.h
@interface EEAnimation : NSObject {
  NSTimeInterval duration, elapsedTime;
  GLKVector2 positionDelta;
}
@property NSTimeInterval duration;
@property(readonly) NSTimeInterval elapsedTime;
@property GLKVector2 positionDelta;
@end
 
 
// EEAnimation.m
@synthesize duration, elapsedTime, positionDelta;
 
-(id)init {
  self = [super init];
  if (self) {
    elapsedTime = 0;
    duration = 0;
    positionDelta = GLKVector2Make(0,0);
  }
  return self;
}

We’re storing how much the position changes in positionDelta and how long it takes in duration. We’re also keeping track of how long the animation has been running in elapsedTime so that we know when we can stop; we make this readonly so that only the animation class can manage it. We’re setting defaults in the init method to make this an animation that doesn’t do anything unless the client code sets new values, which is reasonable behavior.

Now we need a way to update the shape that the animation is applied to. The math will be just like our velocity additions.

1
2
3
4
5
6
7
8
9
10
-(void)animateShape:(EEShape *)shape dt:(NSTimeInterval)dt {
  elapsedTime += dt;
 
  if (elapsedTime > duration)
    dt -= elapsedTime - duration;
 
  float fractionOfDuration = dt/duration;
  GLKVector2 positionIncrement = GLKVector2MultiplyScalar(positionDelta, fractionOfDuration);
  shape.position = GLKVector2Add(shape.position, positionIncrement);
}

We anticipate that a shape will be passed to us, along with a time step. With that, we can calculate how far the shape has moved based on the fraction of the duration that the time step makes up, and with that update its position. The only surprise is on lines 4 and 5, which adjusts our time step to make sure we don’t overshoot our expected value.

Now we need to be able to add animations to our shapes, and iterate through them on updates.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// EEShape.h
@interface EEShape : NSObject {
  NSMutableArray *animations;
  ...
}
@property(strong, readonly) NSMutableArray *animations;
...
@end
 
 
// EEShame.m
@synthesize animations;
 
-(id)init {
  self = [super init];
  if (self) {
    animations = [[NSMutableArray alloc] init];
 
    ... // Rest of initialization
  }
  return self;
}
 
-(void)update:(NSTimeInterval)dt {
  [animations enumerateObjectsUsingBlock:^(EEAnimation *animation, NSUInteger idx, BOOL *stop) {
    [animation animateShape:self dt:dt];
  }];
 
  [animations filterUsingPredicate:
    [NSPredicate predicateWithBlock:^BOOL(EEAnimation *animation, NSDictionary *bindings) {
      return animation.elapsedTime <= animation.duration;
    }]
  ];
 
  ... // Rest of update
}
  • Lines 1-22 are the boilerplate that we’re now well accustomed to.
  • Lines 25-27 loop through the object’s animations and ask each one to update the shape’s attributes. This is the same strategy as EEScene‘s asking each object to update itself.
  • Lines 29-33 filter the shape’s animations array to only keep animations that have not yet finished.

Alright! Let’s test it out.

// ManuallyMovedTree.m
-(id)init {
  self = [super init];
  if (self) {
    Tree *tree = [[Tree alloc] init];
    tree.position = GLKVector2Make(-1.5,0);
 
    EEAnimation *moveRightAnimation = [[EEAnimation alloc] init];
    moveRightAnimation.positionDelta = GLKVector2Make(3, 0);
    moveRightAnimation.duration = 3;
    [tree.animations addObject:moveRightAnimation];
 
    [self.shapes addObject:tree];
  }
  return self;
}

iteration-16a

Manually animating scale, rotation, and color

This approach works equally well with any attribute, whether scalar or vector. We can simply extend our existing animation class to take into account other animatable attributes.

// EEAnimation.h
@interface EEAnimation : NSObject {
  float rotationDelta;
  GLKVector2 scaleDelta;
  GLKVector4 colorDelta;
  ...
}
@property float rotationDelta;
@property GLKVector2 scaleDelta;
@property GLKVector4 colorDelta;
...
@end
 
 
// EEAnimation.m
@synthesize rotationDelta, scaleDelta, colorDelta;
 
-(id)init {
  self = [super init];
  if (self) {
    rotationDelta = 0;
    scaleDelta = GLKVector2Make(0,0);
    colorDelta = GLKVector4Make(0,0,0,0);
    ... // rest of initialization
  }
  return self;
}
 
-(void)animateShape:(EEShape *)shape dt:(NSTimeInterval)dt {
  ... // previous implementation
  GLKVector4 colorIncrement = GLKVector4MultiplyScalar(colorDelta, fractionOfDuration);
  shape.color = GLKVector4Add(shape.color, colorIncrement);
 
  GLKVector2 scaleIncrement = GLKVector2MultiplyScalar(scaleDelta, fractionOfDuration);
  shape.scale = GLKVector2Add(shape.scale, scaleIncrement);
 
  shape.rotation += rotationDelta * fractionOfDuration;
}

The only thing left is to test it all.

// ComplexAnimationScene.m
-(id)init {
  self = [super init];
  if (self) {
    EERectangle *rectangle = [[EERectangle alloc] init];
    rectangle.position = GLKVector2Make(-1, -1);
    rectangle.width = 2;
    rectangle.height = 1;
    rectangle.scale = GLKVector2Make(0.5, 1);
    rectangle.color = GLKVector4Make(1, 0, 0, 0);
 
    EEAnimation *complexAnimation = [[EEAnimation alloc] init];
    complexAnimation.positionDelta = GLKVector2Make(2, 2);
    complexAnimation.scaleDelta = GLKVector2Make(1, -0.5);
    complexAnimation.rotationDelta = M_TAU;
    complexAnimation.colorDelta = GLKVector4Make(0, 0, 0, 1);
    complexAnimation.duration = 3;
    [rectangle.animations addObject:complexAnimation];
 
    [self.shapes addObject:rectangle];
  }
  return self;
}

iteration-16b

We’re also already set up for concurrent animations, we just haven’t used any yet. Let’s extend our scene with another animation.

// ComplexAnimationScene.m in init
EEAnimation *secondAnimation = [[EEAnimation alloc] init];
secondAnimation.positionDelta = GLKVector2Make(-1,-1);
secondAnimation.rotationDelta = M_TAU;
secondAnimation.colorDelta = GLKVector4Make(0, 1, 0, 0);
secondAnimation.duration = 2;
[rectangle.animations addObject:secondAnimation];

iteration-16c

A better API

This section doesn’t add any new core functionality, but pretties up the client code through, effectively, a fancy factory method. Feel free to skip it if you like.

Creating those animations and deltas manually is kind of a pain. We can do a little bit better without a ton of work. Let’s take a little inspiration from UIView‘s animateWithDuration:animations: API. We’ll define a method that takes a block where any animatable attributes modified inside of it are animated.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// EEShape.m
-(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animationsBlock {
  GLKVector2 currentPosition = self.position;
  GLKVector2 currentScale = self.scale;
  GLKVector4 currentColor = self.color;
  float currentRotation = self.rotation;
 
  animationsBlock();
 
  EEAnimation *animation = [[EEAnimation alloc] init];
  animation.positionDelta = GLKVector2Subtract(self.position, currentPosition);
  animation.scaleDelta = GLKVector2Subtract(self.scale, currentScale);
  animation.rotationDelta = self.rotation - currentRotation;
  animation.colorDelta = GLKVector4Subtract(self.color, currentColor);
  animation.duration = duration;
  [self.animations addObject:animation];
 
  self.position = currentPosition;
  self.scale = currentScale;
  self.color = currentColor;
  self.rotation = currentRotation;
}
  • Lines 3-6 store the current values of our animatable attributes.
  • Line 8 calls the block which will update our attributes.
  • Lines 10-16 create our animation with deltas based on the difference between the desired values and the current values of our attributes.
  • Lines 18-21 reset our attributes to what they were before the block; we’re animating to them, after all, not setting them immediately.

Using that API, our ComplexAnimationsScene gets slightly less complex.

// PrettyAPIMoveScene.m
-(id)init {
  self = [super init];
  if (self) {
    Tree *tree = [[Tree alloc] init];
    tree.position = GLKVector2Make(-1.5,0);
    [tree animateWithDuration:3 animations:^() {
      tree.position = GLKVector2Make(1.5,0);
    }];    
    [self.shapes addObject:tree];
  }
  return self;
}

iteration-16d

This produces an animation identical to our earlier ManuallyMovedTree scene, but has much prettier and easier to understand client code. This approach is nice especially with animating between two arbitrary complex values like 4-tuple colors—you only have to specify the start and end values, and the helper method calculates the deltas.

// ColorChangeScene.m
-(id)init {
  self = [super init];
  if (self) {
    EEEllipse *ball = [[EEEllipse alloc] init];
    ball.radiusX = 1;
    ball.radiusY = 1;
    ball.color = GLKVector4Make(0.9, 0.1, 0.1, 1);
    [ball animateWithDuration:3 animations:^() {
      ball.color = GLKVector4Make(0, 0.4, 0.9, 1);
    }];
    [self.shapes addObject:ball];
  }
  return self;
}

iteration-16e

Ideas for future iterations

Our animation framework is not bad, but there’s plenty more it could do. Fortunately, all animation works effectively the same way: there’s some fractional change that occurred based on the time step, the total duration, and the delta for the attribute. Now that you have those skills, you can do all sorts of complex animations. Here are a few directions you could take it.

  • Animate the scene’s projection matrix.
  • Create temporary animations that act only on the modelview matrix.
  • Create reversible animations.
  • Create repeating animations.
  • Create animations with nonlinear tweening, i.e. “ease in” or “ease out”.

Continue on to the next article in the tutorial to learn how to animate objects’ appearances with sprites.

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment

How to animate objects with GLKit

This is the tenth post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

This post will teach you how to animate your objects’ position and rotation using the concepts of velocity and acceleration.

Iteration 15: Animating using formulas

Velocity

Oftentimes in games what we really want with animation is to describe motion: an object will be moving in a certain direction at a certain speed. That’s the velocity of the object, and we can represent it like that in our code and use that to calculate positions. Since objects can move in two dimensions, their velocities have two direction components, and we must use another vector.

// EEShape.h
@interface EEShape : NSObject {
  GLKVector2 velocity;
  ...
}
@property GLKVector2 velocity;
...
@end
 
 
// EEShape.m
@synthesize velocity;

While we intuitively understand that a moving object has a speed and direction, called the velocity, it’s easier to implement if we understand what it is a little more formally. Velocity is the rate of change of the position over time: a velocity of 1 means that after 1 unit of time, an object has moved 1 unit. In our case, it will be simplest to use the second as our unit of time, and so our velocity vector (vx,vy) means that after 1 second our object has moved vx in the x dimension and vy in the y dimension.

The first new idea presented here is that our object’s state is going to be updated. Previously all of our objects were static, and so our work has been in the render step. But there are two steps to OpenGL-backed applications, and now we’re going to focus on the update step.

The second new idea is that we now care about time. If we don’t know how much time has passed, how do we know how far our object has moved with a given velocity?

We’ll make our shape responsible for updating its own position. So we need to add an update method, and take time into account.

1
2
3
4
5
6
7
8
9
// EEShape.h
-(void)update:(NSTimeInterval)dt;
 
 
// EEShape.m
-(void)update:(NSTimeInterval)dt {
  GLKVector2 distanceTraveled = GLKVector2MultiplyScalar(self.velocity, dt);
  self.position = GLKVector2Add(self.position, distanceTraveled);
}

Line 7 calculates the distance that the object has traveled in the time dt passed. This is straightforward because NSTimeInterval is always in seconds. Line 8 increments our position with the distance traveled. In both cases GLKit helper methods are used for readability. (Incidentally, this is one place where C++ really shines with its operator overloading; that could be written very readably as position+=velocity*dt.)

Now we need to call our objects’ update code on each update pass. If you remember, we stubbed out an update method in our scenes, and called it from the view controller delegate method:

// EEAppDelegate.m
- (void)glkViewControllerUpdate:(GLKViewController *)controller {
  [scene update];
}

If we read the documentation on GLKViewController we find that it conveniently exposes several properties that relate to time. The one we need is timeSinceLastUpdate, an NSTimeInterval. We can add this information to our scene’s update.

// EEScene.h
-(void)update:(NSTimeInterval)dt;
 
 
// EEAppDelegate.m
- (void)glkViewControllerUpdate:(GLKViewController *)controller {
  [scene update:controller.timeSinceLastUpdate];
}

Now we just need to update our individual objects. In our scene’s render method, we iterated through our objects with a helper and rendered each one individually. We’ll take a similar approach here. Since NSTimeInterval is a primitive data type, not an object, we can’t use makeObjectsPerformSelector:withObject:, but we can use a block just as easily.

// EEScene.m
-(void)update:(NSTimeInterval)dt {
  [shapes enumerateObjectsUsingBlock:^(EEShape *shape, NSUInteger idx, BOOL *stop) {
    [shape update:dt];
  }];
}

Perfect! Let’s create a test VelocityScene to test it all.

// VelocityScene.m
-(id)init {
  self = [super init];
  if (self) {
    Tree *tree = [[Tree alloc] init];
    tree.scale = GLKVector2Make(0.5,0.5);
    tree.position = GLKVector2Make(-3,-2);
    tree.velocity = GLKVector2Make(1,0.75);
    [self.shapes addObject:tree];
  }
  return self;
}

iteration-15a

Acceleration

When an object is accelerating, not only is its position changing but its velocity is changing as well. We can implement this exactly analogously to velocity.

// EEShape.h
@interface EEShape : NSObject {
  GLKVector2 acceleration;
  ...
}
@property GLKVector2 acceleration;
...
@end
 
 
// EEShape.m
@synthesize acceleration;
...
-(void)update:(NSTimeInterval)dt {
  GLKVector2 changeInVelocity = GLKVector2MultiplyScalar(self.acceleration, dt);
  self.velocity = GLKVector2Add(self.velocity, changeInVelocity);
 
  GLKVector2 distanceTraveled = GLKVector2MultiplyScalar(self.velocity, dt);
  self.position = GLKVector2Add(self.position, distanceTraveled);
}

Here we’ve added the change in velocity calculations to our update: method. The concepts and math are exactly the same.

One well known example of acceleration is the constant acceleration due to the force of gravity. We can mimic this with a constant downward (negative y) acceleration. In free fall, this produces parabolic motion, a sight we’re all used to.

// AccelerationScene.m
-(id)init {
  self = [super init];
  if (self) {
    EEEllipse *ball = [[EEEllipse alloc] init];
    ball.radiusX = 0.2;
    ball.radiusY = 0.2;
    ball.color = GLKVector4Make(1, 0, 0, 1);
    ball.position = GLKVector2Make(-3,-2);
    ball.velocity = GLKVector2Make(1,2.5);
    ball.acceleration = GLKVector2Make(0,-1);
 
    [self.shapes addObject:ball];
  }
  return self;
}

iteration-15b

Angular velocity and acceleration

Velocity and acceleration are concepts that can be applied to rotation as well. The most common units we know of for angular velocity are rpm, revolutions per minute. But a minute is a long time on a handheld device, so we’ll use seconds; and since our rotation property is already in radians we’ll make our angular velocity be measured in radians per second. That means that our choice for angular acceleration will be radians per second per second.

// EEShape.h
@interface EEShape : NSObject {
  float angularVelocity, angularAcceleration;
  ...
}
@property float angularVelocity, angularAcceleration;
...
@end
 
 
// EEShape.m
@synthesize angularVelocity, angularAcceleration;
...
-(void)update:(NSTimeInterval)dt {
  angularVelocity += angularAcceleration * dt;
  rotation += angularVelocity * dt;
 
  ... // existing position updating code
}

We’ll test this out with a test scene of a tree rotating. We’ll start it out with rotation = 0 (it should be pointing up when it starts), angularVelocity = M_TAU (it should be rotating at τ radians, one revolution, per second), and angularAcceleration = 0.5*M_TAU (it should speed up at the rate of an extra half rotation per second).

// RotatingTreeScene.m
-(id)init {
  self = [super init];
  if (self) {
    Tree *tree = [[Tree alloc] init];
    tree.rotation = 0;
    tree.angularVelocity = M_TAU;
    tree.angularAcceleration = 0.5*M_TAU;
    [self.shapes addObject:tree];
  }
  return self;
}

iteration-15c

Continue on to the next article in the tutorial to learn how to animate your objects’ attributes manually.

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment

How to create and render composite objects with GLKit

This is the ninth post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

In this post we’ll learn how to create and render composite objects, or objects that contain several other objects. This will allow us to manipulate their parts together instead of separately.

Iteration 14: Composite objects

Consider our TreeScene from the last post. If we want to move the tree, we have to reposition both the trunk and the leaves. If we want to scale it, it gets even harder: we have to scale both the trunk and the leaves, plus reposition them to make them fit right. Let’s try to scale it down to half size and move it over in its current state just to see the work we’d have to do.

// TreeScene.m in init
leaves.scale = GLKVector2Make(0.5, 0.5);
trunk.scale = GLKVector2Make(0.5, 0.5);
leaves.position = GLKVector2Add(leaves.position, GLKVector2Make(1,0));
trunk.position = GLKVector2Add(trunk.position, GLKVector2Make(1,0));

But that doesn’t quite do it, either: it’s relatively lower than it was, because the original positions didn’t get scaled by our scaling factor.

Manually managing every single shape in your scene is error prone and difficult. The solution is to make objects that are composed of other objects that can be manipulated as if they are one single object. To implement composite objects we can extend our EEShape class to implement a tree.

// EEShape.h
@interface EEShape : NSObject {
  NSMutableArray *children;
  EEShape *parent;
  ...
}
@property(strong, readonly) NSMutableArray *children;
@property(strong) EEShape *parent;
...
@end
 
 
// EEShape.m
@synthesize children, parent;
 
-(id)init {
  self = [super init];
  if (self) {
    children = [[NSMutableArray alloc] init];
    ... // other defaults
  }
  return self;
}

Each shape optionally has child shapes in its children array, and if a shape is a child it knows who its parent is with its parent attribute. Let’s create a helper method that will set this up for us (be sure to declare the method in the header file, which I’m skipping over).

// EEShape.m
-(void)addChild:(EEShape *)child {
  child.parent = self;
  [children addObject:child];
}

It won’t work yet, but let’s start to set up our TreeScene using this framework to make sure we like (and understand) the API we’ve built.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// TreeScene.m
-(id)init {
  self = [super init];
  if (self) {
    EETriangle *leaves = [[EETriangle alloc] init];
    leaves.vertices[0] = GLKVector2Make(-1, 0);
    leaves.vertices[1] = GLKVector2Make( 1, 0);
    leaves.vertices[2] = GLKVector2Make( 0, 3);
    leaves.position = GLKVector2Make(0,-1.2);
    leaves.color = GLKVector4Make(0, 0.5, 0, 1);
 
    EERectangle *trunk = [[EERectangle alloc] init];
    trunk.width = 0.4;
    trunk.height = 1;
    trunk.position = GLKVector2Make(0, -1.25);
    trunk.color = GLKVector4Make(0.4, 0.1, 0, 1);
 
    EEShape *tree = [[EEShape alloc] init];
    [tree addChild:trunk];
    [tree addChild:leaves];
    tree.scale = GLKVector2Make(0.5, 0.5);
    tree.position = GLKVector2Make(1,0);
 
    [self.shapes addObject:tree];
  }
  return self;
}
  • Lines 5-16 set up our leaves and trunk subshapes.
  • Lines 18-20 set up our composite tree object with the child shapes.
  • Lines 21 and 22 attempt to scale and position the tree as a whole.

Note also that I’ve now removed the variable declarations from TreeScene.h since it’s no longer necessary to keep track of them individually.

This compiles and runs, but produces a blank screen: our tree object has no real shape information, and the child objects were not added to the scene’s shapes array and are so their renderInScene: methods are never called. In our tree structure, the responsibility to render child objects lies with their parent. We can add it very simply at the very bottom of our renderInScene: method.

// EEShape.m at the bottom of renderInScene:
[children makeObjectsPerformSelector:@selector(renderInScene:) withObject:scene];

Partial success! Our tree is rendered whole, but it is not repositioned or rescaled. This makes sense: our child objects are using their own modelview matrices, but not taking into account the parent modelview matrix. The first step to fixing this is extracting the modelview matrix calculations out into its own method.

// EEShape.h
@property(readonly) GLKMatrix4 modelviewMatrix;
 
 
// EEShape.m
-(GLKMatrix4)modelviewMatrix {
  GLKMatrix4 modelviewMatrix = ... //
  return modelviewMatrix;
}
 
// in renderInScene:
effect.transform.modelviewMatrix = self.modelviewMatrix;

Next we need to take into account our parents’ matrices—plural in the case of grandparents, great-grandparents, etc. We do this by recursing up through our parents, and multiplying our matrix by theirs.

// EEShape.m
-(GLKMatrix4)modelviewMatrix {
  GLKMatrix4 modelviewMatrix = ... /* create our matrix */
 
  if (parent != nil)
    modelviewMatrix = GLKMatrix4Multiply(parent.modelviewMatrix, modelviewMatrix);
 
  return modelviewMatrix;
}

It worked! We can now manipulate our tree however we like, and the changes will be applied to all of its children.

Note that 4×4 matrix multiplication is an expensive operation; while we’re not caching any results here, in a production game engine it will most likely make sense to cache the modelview matrix and only recompute it when it changes.

iteration-14a

Encapsulating game objects

I’m going to take a moment to spell out how to capitalize on this approach by creating game object classes. We’ll create a Tree that represents our tree.

// Tree.h
@interface Tree : EEShape
@end
 
 
// Tree.m
-(id)init {
  self = [super init];
  if (self) {
    EETriangle *leaves = [[EETriangle alloc] init];
    leaves.vertices[0] = GLKVector2Make(-1, 0);
    leaves.vertices[1] = GLKVector2Make( 1, 0);
    leaves.vertices[2] = GLKVector2Make( 0, 3);
    leaves.position = GLKVector2Make(0,-1.2);
    leaves.color = GLKVector4Make(0, 0.5, 0, 1);
 
    EERectangle *trunk = [[EERectangle alloc] init];
    trunk.width = 0.4;
    trunk.height = 1;
    trunk.position = GLKVector2Make(0, -1.25);
    trunk.color = GLKVector4Make(0.4, 0.1, 0, 1);
 
    [self addChild:trunk];
    [self addChild:leaves];
  }
  return self;
}

We only need to build a tree once, but we can use it as many times as we would like with ease. Here’s a sample ForestScene.

// ForestScene.m
-(id)init {
  self = [super init];
  if (self) {
    for (int i = 0; i < 10; i++) {
      for (int j = 0; j < 10; j++) {
        Tree *tree = [[Tree alloc] init];
        tree.scale = GLKVector2Make(0.08+((i+j)%2)*0.04, 0.08+((i+j)%2)*0.04);
        tree.rotation = ((i+j)%12)/12.0*M_TAU;
        tree.position = GLKVector2Make(-2.7+i*0.6,-1.8+j*0.4);
        [self.shapes addObject:tree];
      }
    }
  }
  return self;
}

(If all else fails, perhaps I can pursue a career in wrapping paper design.)

iteration-14b

Optimizing for memory

This is an optimization aimed at more advanced programmers, and isn’t necessary in order to continue to the next lesson. I haven’t replicated the source code here either, so you’ll need to peruse the repository in order to see the implementation.

Since all of the trees are the same—they only differ in their modelview matrices—as an optimization they could all be made to use the same vertex data for their leaves and trunks. One approach to do this is to use prototype objects that are the sole source of vertex data (and in the general case, color and texture data as well). In this approach, an OptimizedShape will delegate all of its relevant methods (currently numVertices, vertices, color, vertexColors, texture, and textureCoordinates) to the prototype object.

You can view the source changes required for an OptimizedTree here on Github.

iteration-14optimized

Continue on to the next article in the tutorial to learn how to make your objects move around.

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment

How to render multiple objects with GLKit

This is the eighth post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

In this post we’ll learn how to render multiple objects easily within a single scene, so that you can build a game with a good guy and bad guys all at once.

Iteration 13: Rendering multiple objects

If we were content with static scenes, we could render multiple objects by hand. Let’s do that in an example TreeScene.

// TreeScene.h
@interface TreeScene : EEScene {
  EETriangle *leaves;
  EERectangle *trunk;
}
@end
 
 
// TreeScene.m
-(id)init {
  self = [super init];
  if (self) {
    leaves = [[EETriangle alloc] init];
    leaves.vertices[0] = GLKVector2Make(-1, 0);
    leaves.vertices[1] = GLKVector2Make( 1, 0);
    leaves.vertices[2] = GLKVector2Make( 0, 3);
    leaves.position = GLKVector2Make(0,-1.2);
    leaves.color = GLKVector4Make(0, 0.5, 0, 1);
 
    trunk = [[EERectangle alloc] init];
    trunk.width = 0.4;
    trunk.height = 1;
    trunk.position = GLKVector2Make(0, -1.25);
    trunk.color = GLKVector4Make(0.4, 0.1, 0, 1);
  }
  return self;
}
 
-(void)render {
  [super render];
  [leaves renderInScene:self];
  [trunk renderInScene:self];
}

In this case, we rendered our tree manually, leaves first and then the trunk after. OpenGL uses the painter’s algorithm, which is just that objects are rendered on top of anything that came before, like layering on paint.

If we wanted to fix it, we could render the trunk first.

// TreeScene.m
-(void)render {
  [super render];
  [trunk renderInScene:self];
  [leaves renderInScene:self];
}

iteration-13a

But games aren’t usually static: objects come into the scene and leave the scene, or they’re created and destroyed dynamically at runtime, or any number of other issues. It would be better to keep a dynamic list of them than have to keep track of them manually.

Let’s add an array of shapes in our scene:

// EEScene.h
@interface EEScene : NSObject {
  NSMutableArray *shapes;
  ...
}
@property(strong,readonly) NSMutableArray *shapes;
...
@end
 
 
// EEScene.m
@synthesize shapes;
-(id)init {
  self = [super init];
  if (self) {
    shapes = [[NSMutableArray alloc] init];
  }
  return self;
}

And now to render our shapes we can iterate through the array, rendering each item, using a helper method.

// EEScene.m
-(void)render {
  glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
  glClear(GL_COLOR_BUFFER_BIT);
 
  [shapes makeObjectsPerformSelector:@selector(renderInScene:) withObject:self];
}

To refactor our tree scene to use the new scene implementation, we will add our tree’s shapes to the scene’s shapes array, and remove our overridden render method entirely.

// TreeScene.m in init
[self.shapes addObject:trunk];
[self.shapes addObject:leaves];

iteration-13b

Continue on to the next article in the tutorial to learn how to create and render composite objects that group multiple objects together.

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment

How to manipulate objects with GLKit

This is the seventh post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

In this post we’ll learn how to manipulate our 2D objects along their axes of freedom: position them in x and y, scale them in x and y, and rotate them around z.

Iteration 12: Manipulating objects

Positioning objects

When developing from scratch I like to start with data. What is the information I need to implement this feature, even if I don’t know how to do the full implementation? In this case, we know we want to position our object in x and y, so we know we need to store a position coordinate. Let’s add that to our shape class to get started.

// EEShape.h
@interface EEShape : NSObject {
  GLKVector2 position;
  ...
}
@property GLKVector2 position;
...
@end
 
 
// EEShape.m
@synthesize position;

Now how do we use that information when we’re rendering to properly place our object? The answer is what’s called the model view matrix.

Our objects are defined in “object space”—this is the universe that contains only them. This is why all of our shapes so far have been centered around the origin: according to them, they’re all that exists, so they’re at the center of everything.

The modelview matrix takes an individual instance of an object and moves it from object space to “world space.” To do so, it may translate it (move it), rotate it, and/or scale it. Different modelview matrices are what allow a video game to reuse objects—i.e., we could have two trees that are the same object, but scale to different sizes and placed in different parts of the world.

To move our object, we need to create a modelview matrix that translates our object according to our position. Just like the projection matrix, this is done on our GLKBaseEffect instance, and GLKit provides some helper methods to create the matrix.

// EEShape.m in renderInScene:
effect.transform.modelviewMatrix = GLKMatrix4MakeTranslation(position.x, position.y, 0);

That was straightforward. Let’s modify our sprite scene to test it by moving our sprite 2 units to the right and 1 unit down. Now we’d expect to see it in the lower right hand side of the screen.

// SpriteScene.m in init
sprite.position = GLKVector2Make(2,-1);

iteration-12a

Rotating objects

Let’s start with data again. We’ll define rotation to be in radians around the positive z axis. Using the right hand rule, that means that when we align our hands with our thumbs pointing toward us, positive values rotate in the direction our fingers curl.

// EEShape.h
@interface EEShape : NSObject {
  float rotation;
  ...
}
@property float rotation;
...
@end
 
 
// EEShape.m
@synthesize rotation;

While we can only have one modelview matrix per object, multiple effects can be combined into a single matrix through matrix multiplication. Matrix multiplication is not communicative, which means that multiplying AxB will produce a different result than BxA. For transformation matrices, the transformation represented by the matrix on the right hand side of the multiplication will be applied first.

// EEShape.m in renderInScene:
effect.transform.modelviewMatrix = 
  GLKMatrix4Multiply(GLKMatrix4MakeRotation(rotation, 0, 0, 1),
                     GLKMatrix4MakeTranslation(position.x, position.y, 0));

Here we’ll translate first and then rotate the object. In our test sprite scene we’ll rotate it a quarter turn (τ/4).

// SpriteScene.m in init
sprite.position = GLKVector2Make(2,-1);
sprite.rotation = 0.25*M_TAU;

Uh oh! That’s not quite what I imagined when I rotated the sprite a quarter turn. This is because our rotation matrix rotates around the origin. In our case, we’ve translated the object first, so the whole translation distance is rotated also.

There’s an easy way to visualize this. Hold a pen in your hand. If you were to translate the pen the length of your forearm and then rotate it, it’s equivalent to bending your arm at the elbow. However, if you were to rotate it before translating it, it’s equivalent to spinning the pen around in your hand.

There’s nothing “wrong” with translating before rotating—sometimes you’ll want to do just that. But for a rotation attribute on an object, it feels more natural for that to mean a rotation around its own origin.

// EEShape.m in renderInScene:
effect.transform.modelviewMatrix = 
  GLKMatrix4Multiply(GLKMatrix4MakeTranslation(position.x, position.y, 0),
                     GLKMatrix4MakeRotation(rotation, 0, 0, 1));

iteration-12b

Scaling objects

Scaling in 2D has two degrees of freedom, so we can use another vector to represent it (if you wanted to enforce object aspect ratios, you could use a single float value instead of a vector).

// EEShape.h
@interface EEShape : NSObject {
  GLKVector2 scale;
  ...
}
@property GLKVector2 scale;
...
@end
 
 
// EEShape.m
@synthesize scale;

Scaling too is done by the modelview matrix, and again order of operations is important. Let’s try the following.

// EEShape.m in renderInScene:
GLKMatrix4 modelviewMatrix = 
  GLKMatrix4Multiply(GLKMatrix4MakeTranslation(position.x, position.y, 0),
                     GLKMatrix4MakeRotation(rotation, 0, 0, 1));
modelviewMatrix = GLKMatrix4Multiply(GLKMatrix4MakeScale(scale.x, scale.y, 1), 
                                     modelviewMatrix);
effect.transform.modelviewMatrix = modelviewMatrix;
// SpriteScene.m in init
sprite.position = GLKVector2Make(2,-1);
sprite.rotation = 0.25*M_TAU;
sprite.scale = GLKVector2Make(0.5, 1.5);

Our code is equivalent to the matrix math Sx(TxR), with S, T, and R representing scaling, translation, and rotation matrices. Since transformations occur right to left, the rotation is applied first, then the translation, then the scaling. This means that our scale is influencing both the translation (we’ve now moved to a different place than we intended) and rotation (we wanted a tall, thin sprite but we have a short, fat one because we rotated him sideways). Let’s fix this by scaling first, then rotating, then translating.

// EEShape.m in renderInScene:
GLKMatrix4 modelviewMatrix = 
  GLKMatrix4Multiply(GLKMatrix4MakeTranslation(position.x, position.y, 0),
                     GLKMatrix4MakeRotation(rotation, 0, 0, 1));
modelviewMatrix = GLKMatrix4Multiply(modelviewMatrix,
                                     GLKMatrix4MakeScale(scale.x, scale.y, 1));
effect.transform.modelviewMatrix = modelviewMatrix;

iteration-12c

Sensible Defaults

If we load up our RectangleScene as it existed when we last saw it, we get a blank screen.

This is because our scale vector is initialized to (0,0) and we didn’t change it explicitly, so it’s shrunk down to nothing. It’s a good habit to get into to provide (and document) sensible defaults for attributes. Let’s make an explicit initialization for our shapes that will work well as a base.

// EEShape.m
-(id)init {
  self = [super init];
  if (self) {
    // Draw with the color white
    useConstantColor = YES;
    color = GLKVector4Make(1,1,1,1);
 
    // No texture
    texture = nil;
 
    // Center on the origin
    position = GLKVector2Make(0,0);
 
    // Don't rotate
    rotation = 0;
 
    // Scale to original size
    scale = GLKVector2Make(1,1);
  }
  return self;
}

Running our RectangleScene again, we see what we expect.

iteration-12d

Continue on to the next article in the tutorial to learn how to render more than one object in your scenes.

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment

How to texturize objects with GLKit (Part 2)

This is the sixth post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

In this post we’ll learn how to create a sprite class to encapsulate our texture information, and turn on transparency for nicer graphics.

Iteration 11: Sprites and other transparent textures

Let’s go ahead and start a sprite class that will encapsulate the rectangular texture code we’ve handled above. Since a sprite is a rectangle, we’ll descend from EERectangle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// EESprite.h
@interface EESprite : EERectangle
-(id)initWithImage:(UIImage*)image pointRatio:(float)ratio;
@end
 
 
// EESprite.m
-(id)initWithImage:(UIImage*)image pointRatio:(float)ratio {
  self = [super init];
  if (self) {
    self.width = image.size.width/ratio;
    self.height = image.size.height/ratio;
 
    [self setTextureImage:image];
    self.textureCoordinates[0] = GLKVector2Make(1,0);
    self.textureCoordinates[1] = GLKVector2Make(1,1);
    self.textureCoordinates[2] = GLKVector2Make(0,1);
    self.textureCoordinates[3] = GLKVector2Make(0,0);
  }
  return self;
}

This lets us initialize a sprite with just an image file. The width and height are calculated based on the image’s size and scaled by a ratio parameter (i.e. N pixels represent 1 unit in our world coordinates).

To test, we’ll use a free sprite by Danc at LostGarden (I’ve trimmed it of the extra whitespace).

Now we’ll create a test scene that uses our sprite class.

// SpriteScene.m
-(id)init {
  self = [super init];
  if (self) {
    sprite = [[EESprite alloc] initWithImage:[UIImage imageNamed:@"boy-sprite.png"] pointRatio:100];
  }
  return self;
}

Uh oh! The transparent parts are showing up as black—not as transparent.

By default, OpenGL doesn’t turn on transparency. We’ll need to set it ourselves using a blend function in our rendering code. It’s just a few lines of code.

// EEShape.m in renderInScene:
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
...  
glDrawArrays(GL_TRIANGLE_FAN, 0, self.numVertices);
...
glDisable(GL_BLEND);

The blend function tells OpenGL to use our alpha values to create transparent effects. It’s actually more complicated than that, and there’s a whole slew of ways that OpenGL can blend your colors. To give you a bit of flavor for what’s going on under the curtains (feel free to skip), the OpenGL FAQ on transparency says about this blend function:

[glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)] modifies the incoming color by its associated alpha value and modifies the destination color by one minus the incoming alpha value. The sum of these two colors is then written back into the framebuffer.

The pragmatist in me says that it’s not necessary to have anything but the vaguest of grasps on that to use it: it turns on transparency in the default case.

As a nice side effect, partially transparent colored objects without textures work now, too.

iteration-11

Hey! Our EEScene‘s renderInScene: has gotten kind of big, so iteration 11 includes comments to make sure you understand what each part is doing!

Continue on to the next article in the tutorial to learn how to move, scale, and rotate your shapes and sprites.

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment

How to texturize objects with GLKit

This is the fifth post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

In this post we’ll learn how to add textures (images) to our shapes. In addition to adding visual flair, this technique allows us to use sprites as game objects.

Iteration 9: Texturizing a rectangle

GLKit uses OpenGL ES 2.0, so the full array of possibilities exists for you if you wish to write your own shader programs. We’re keeping it simple, though, and we’ll use GLKBaseEffect (which mimics OpenGL ES 1.1 functionality) to handle our textures.

Images are always rectangular, so we’ll start with a 1-to-1 mapping of an image onto a rectangle. We’ll use this nice landscape photo I found on Flickr, licensed CC-Attribution by NeilsPhotography.

There are three steps to using textures with GLKit.

  1. Loading the texture.
  2. Configuring the GLKBaseEffect.
  3. Sending texture coordinates to OpenGL.

Before we get started loading the texture, we need to add it to the project. Save the photo linked above to your Desktop as “landscape.jpg” (or another file and name of your choice, naturally). Right click on a group and select “Add Files to ‘ExampleEngine’…”, select the file, check “Copy items into Destination’s group folder”, and make sure your ExampleEngine target is checked; then click “Add.” We now have access to the file from within our application’s main bundle.

Textures are stored with the GLKTextureInfo class, so we’ll add one of those to our shape base class. But we’d like to encapsulate the texture loading code in our class, so we’ll let the client code set the texture as an image.

// EEShape.h
@interface EEShape : NSObject {
  GLKTextureInfo *texture;
  ...
}
-(void)setTextureImage:(UIImage *)image;
...
@end
 
// EEShape.m
-(void)setTextureImage:(UIImage *)image {
  NSError *error;
  texture = [GLKTextureLoader textureWithCGImage:image.CGImage options:nil error:&error];
  if (error) {
    NSLog(@"Error loading texture from image: %@",error);
  }
}

That’s all it takes using GLKTextureLoader. There are some options we can pass (we’ll look at a few in a moment), and we’re not doing any error handling aside from a log statement, but this’ll work for the common case.

Next we have to configure the GLKBaseEffect to use the texture. We’ll only do this if we’ve set the texture, so that we can continue to have shapes without textures as well. Add this to your effect configuration:

// EEShape.m in renderInScene:
if (texture != nil) {
  effect.texture2d0.envMode = GLKTextureEnvModeReplace;
  effect.texture2d0.target = GLKTextureTarget2D;
  effect.texture2d0.name = texture.name;
}

Easy peasy! Although I notice again that we’re creating our effect on every single frame, which is inefficient. Caching the effect in the object is an optimization that a production game engine would want to make in order to give it more time to process game logic in between frames.

Finally, we need to pass in texture coordinates per vertex. This defines where and how the texture is placed on the shape. OpenGL uses a normalized coordinate system for texture coordinates, with an origin in the lower left of an image. And instead of using x and y, since those are already used for position data, the convention is to refer to the coordinates as s and t.

The word “normalized” means that the coordinate system only goes from 0 to 1; the right side of the texture image is at s=1, and the top is at t=1. For a rectangle, mapping the whole image to it is simple: the bottom left corner is (0,0), the bottom right is (1,0), the top right is (1,1), and the top left is (0,1).

In our code, we still need a place to store our texture coordinates per vertex. We’ll take the same approach as we did with per-vertex coloring and position vertex data.

// EEShape.h
@interface EEShape : NSObject {
  NSMutableData *textureCoordinateData;
  ...
}
@property(readonly) GLKVector2 *textureCoordinates;
...
@end
 
 
// EEShape.m
@implementation EEShape
- (GLKVector2 *)textureCoordinates {
  if (textureCoordinateData == nil)
    textureCoordinateData = [NSMutableData dataWithLength:sizeof(GLKVector2)*self.numVertices];
  return [textureCoordinateData mutableBytes];
}
...
@end

And we need to enable the data and send it to OpenGL just like we did with colors and positions as well. We’ll need to add the following set up and tear down code to our rendering code.

// EEShape.m in renderInScene:
...
if (texture != nil) {
  glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
  glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, 0, self.textureCoordinates);
}
...
glDrawArrays(GL_TRIANGLE_FAN, 0, self.numVertices);
...
if (texture != nil)
  glDisableVertexAttribArray(GLKVertexAttribTexCoord0);

Now we’re ready to make sure it all works! Create a LandscapeScene, set it up with a rectangle, and configure the texture and texture coordinates like so to make it full screen (don’t worry, I too had to look back into EERectangle.m to remember which vertex index corresponded to which corner).

// LandscapeScene.m
-(id)init {
  self = [super init];
  if (self) {
    rectangle = [[EERectangle alloc] init];
    rectangle.width = 6;
    rectangle.height = 4;
 
    [rectangle setTextureImage:[UIImage imageNamed:@"landscape.jpg"]];
    rectangle.textureCoordinates[0] = GLKVector2Make(1,0);
    rectangle.textureCoordinates[1] = GLKVector2Make(1,1);
    rectangle.textureCoordinates[2] = GLKVector2Make(0,1);
    rectangle.textureCoordinates[3] = GLKVector2Make(0,0);
  }
  return self;
}

Running it, we see…

An upside down landscape! In this case it has to do with the fact that GLKit tries to match UIKit’s convention of having the origin in the top left instead of the bottom left.

Honestly, no matter how many times I use textures and try to be careful about it, I hit the upside down problem all the time. It’s eminently solvable, though, and quick to catch in most cases, so I wouldn’t worry about trying to memorize everything—just flip stuff when you catch it.

Remember the options parameter when we loaded our texture? Investigating the documentation I see that we could have passed a GLKTextureLoaderOriginBottomLeft boolean—if YES it flips our data for us. So now we have two options: set the option, or flip our texture coordinates.

If we want to think of the texture mapping as having the origin at the top left instead of the bottom left, we don’t have to do anything except change our texture coordinates for our shapes.

rectangle.textureCoordinates[0] = GLKVector2Make(1,1);
rectangle.textureCoordinates[1] = GLKVector2Make(1,0);
rectangle.textureCoordinates[2] = GLKVector2Make(0,0);
rectangle.textureCoordinates[3] = GLKVector2Make(0,1);

Or, if we’d like to think of the texture mapping as having the origin at the bottom left, we could refrain from flipping it on texture load.

[GLKTextureLoader textureWithCGImage:image.CGImage 
                             options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] 
                                                                 forKey:GLKTextureLoaderOriginBottomLeft] 
                               error:&error];

It’s up to you which solution you want to pursue—I’m going to stick with GLKTextureLoader solution and keep my s,t coordinate system origin in the bottom left.

The next “problem” with our texture is that it doesn’t actually quite fit the aspect ratio of our shape. Our shape matches our screen with a 3:2 ratio, but our original texture is 4:3. It’s not very visible in this example, but the mountains appear a little squished. You don’t have to fix this if you don’t want to—you might even like the effect in some cases. But we’ll fix it here to be proper and since it’s easy. To fix it, we’ll just trim off some of the image by using our texture coordinates.

rectangle.textureCoordinates[0] = GLKVector2Make(1,0);
rectangle.textureCoordinates[1] = GLKVector2Make(1,0.88);
rectangle.textureCoordinates[2] = GLKVector2Make(0,0.88);
rectangle.textureCoordinates[3] = GLKVector2Make(0,0);

The number 0.88 was obtained by doing a little algebra to see what fraction would create the proper 3:2 aspect ratio (0.88*768/1024=2/3). And presto, we’ve cut off the top of the sky!

iteration-9

Update: Iteration 9′s tagged code was built with iOS 5 beta 5; in beta 6 the API for textures changed slightly. You changes necessary are applied in 04d7980.

Iteration 10: Texturizing other shapes

Other shapes are no different to texturize, but you may need to be careful when you specify your texture coordinates.

Here we’ll texture a circle with this sandy tennis (?) ball picture by marc falardeau on Flickr. First we’ll trim the ball image down to a square shape.

This makes our math easier. Because the texture coordinate system is normalized from 0 to 1 on each axis, we need to find the texture coordinates that correspond to a circle centered at (0.5,0.5) with a radius of 0.5 units. We’ve already calculated the points on a circle centered at the origin with any given radius, so this is the same thing with each point shifted over and up 0.5 units each.

// BeachBallScene.m
-(id)init {
  self = [super init];
  if (self) {
    ellipse = [[EEEllipse alloc] init];
    ellipse.radiusX = 1;
    ellipse.radiusY = 1;
 
    [ellipse setTextureImage:[UIImage imageNamed:@"ball.jpg"]];
    float textureBallRadius = 0.5;
    float textureCenterOffset = 0.5;
    for (int i = 0; i < ellipse.numVertices; i++){
      float theta = ((float)i) / ellipse.numVertices * M_TAU;
      ellipse.textureCoordinates[i] = GLKVector2Make(textureCenterOffset+cos(theta)*textureBallRadius, 
                                                     textureCenterOffset+sin(theta)*textureBallRadius);
    }
  }
  return self;
}

iteration-10a

Other polygons are just the same! Consider a triangle with this Sierpinsky fractal by SphinxTheGeek on it. There’s a little trigonometry below to make sure we create an equilateral triangle (don’t be afraid of math!).

// SierpinskyTriangleScene.m
-(id)init {
  self = [super init];
  if (self) {
    triangle = [[EETriangle alloc] init];
 
    triangle.vertices[0] = GLKVector2Make(-1, -1);
    triangle.vertices[1] = GLKVector2Make( 1, -1);
    triangle.vertices[2] = GLKVector2Make( 0,  -1+2*sin(M_TAU/6));
 
    [triangle setTextureImage:[UIImage imageNamed:@"sierpinksy.jpg"]];
    triangle.textureCoordinates[0] = GLKVector2Make(  0,0);
    triangle.textureCoordinates[1] = GLKVector2Make(  1,0);
    triangle.textureCoordinates[2] = GLKVector2Make(0.5,1);
  }
  return self;
}

iteration-10b

Continue on to the next article in the tutorial to learn how to create sprites and add transparent textures.

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment

How to color objects with GLKit

This is the fourth post in my 2D Game Engine Tutorial. This post relies on code created in earlier posts, so if you jumped in on this post I recommend you at least skim the previous posts in the series. You can also grab the tagged code so that you don’t have to start from scratch if you don’t want to.

In this post we’ll learn how to make our objects colorful.

Iteration 7: Solid colored shapes

We’re going to color our shapes with a single color by configuring the GLKBaseEffect that we’re rendering our objects with. The first thing we need to do is store the color of the shape.

// EEShape.h
@interface EEShape : NSObject {
  GLKVector4 color;
  ...
}
@property GLKVector4 color;
...
@end
 
 
// EEShape.m
@implementation EEShape
@synthesize color;
...
@end

To configure our GLKBaseEffect with a color per shape, we need access to the effect in our shape’s render method. Right now it only exists in EEScene, where it sets up the scene’s projection matrix. The effect needs to be configured with both scene information (boundaries) and shape information (color), so it’s not clear that it should live in one or the other. There are easily half a dozen decent solutions here, so we’ll just pick one.

The scene information can be encapsulated as just the projection matrix, so we can expose that to the world and then pass the scene along to the shape as it’s rendered.

// EEScene.h
@property(readonly) GLKMatrix4 projectionMatrix;
 
 
// EEScene.m
-(void)render {
  glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
  glClear(GL_COLOR_BUFFER_BIT);
}
 
-(GLKMatrix4)projectionMatrix {
  return GLKMatrix4MakeOrtho(left, right, bottom, top, 1, -1);
}

The scene’s render is no longer responsible for creating the GLKBaseEffect, but exposes the projection matrix that the shapes need to create it. We’ll change our render method in the shape to accept the scene as an argument.

// EEShape.m
-(void)renderInScene:(EEScene *)scene {
  GLKBaseEffect *effect = [[GLKBaseEffect alloc] init];
  effect.transform.projectionMatrix = scene.projectionMatrix;
  [effect prepareToDraw];
 
  ... // other OpenGL code
}

Now we just need to update our shapes’ render calls to use renderInScene: in all of our client code scenes (TriangleScene, RectangleScene, EllipseScene, HexagonScene). After that tweak, it should build and run fine.

// HexagonScene.m
-(void)render {
  [super render];
  [polygon renderInScene:self];
}

To add color, we now add just two lines to our effect creation code in render.

// EEShape.m in render
effect.useConstantColor = YES;
effect.constantColor = color;

It’s black! Right. We forgot to set the color. Let’s set it to something.

// HexagonScene.m in init
polygon.color = GLKVector4Make(0.9, 0.9, 0.1, 1.0);

iteration-7

Iteration 8: Vertex colored shapes

There’s another easy way to color shapes in OpenGL, and that’s by assigning a color to each vertex. OpenGL will then extrapolate the colors in between the vertices for each triangle. To use this feature, we’ll need to store the color data for each vertex, and we’ll use the same pattern for the storage that we did with the vertex position data.

// EEShape.h
@interface EEShape : NSObject {
  NSMutableData *vertexColorData;
  ...
}
@property(readonly) GLKVector4 *vertexColors;
...
@end
 
 
// EEShape.m
@implementation EEShape
- (GLKVector4 *)vertexColors {
  if (vertexColorData == nil)
    vertexColorData = [NSMutableData dataWithLength:sizeof(GLKVector4)*self.numVertices];
  return [vertexColorData mutableBytes];
}
...
@end

We also need a way to decide per shape which of our two strategies we’re going to use: solid coloring or per-vertex coloring. Again, there are many options here, and we could dynamically decide based on what colors have been set. For simplicity, we’ll take the “must explicitly set” route, and borrow the naming convention from GLKit.

// EEShape.h
@interface EEShape : NSObject {
  BOOL useConstantColor;
  ...
}
@property BOOL useConstantColor;
...
@end
 
 
// EEShape.m
@implementation EEShape
@synthesize useConstantColor;
...
@end

To render with vertex coloring, you must pass the vertex color data to OpenGL in a way similar to passing position data. Here’s our full render method now.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-(void)renderInScene:(EEScene *)scene {
  GLKBaseEffect *effect = [[GLKBaseEffect alloc] init];
  if (useConstantColor) {
    effect.useConstantColor = YES;
    effect.constantColor = color;
  }
  effect.transform.projectionMatrix = scene.projectionMatrix;
  [effect prepareToDraw];
 
  glEnableVertexAttribArray(GLKVertexAttribPosition);
  glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, 0, self.vertices);
 
  if (!useConstantColor) {
    glEnableVertexAttribArray(GLKVertexAttribColor);
    glVertexAttribPointer(GLKVertexAttribColor, 4, GL_FLOAT, GL_FALSE, 0, self.vertexColors);
  }
 
  glDrawArrays(GL_TRIANGLE_FAN, 0, self.numVertices);
 
  glDisableVertexAttribArray(GLKVertexAttribPosition);
 
  if (!useConstantColor)
    glDisableVertexAttribArray(GLKVertexAttribColor);
}
  • Lines 1, 2, 7-11, and 18-20 are our existing render method, just now spaced out.
  • Lines 3-6 handle the constant color case, but only enable the effect’s constant color if we’ve set the useConstantColor attribute to YES.
  • Lines 13-16 handles the vertex color case, but only send the data to OpenGL if we’ve set the useConstantColor attribute to NO.
  • Lines 22-23 are cleanup code to tell OpenGL that we’re done sending color vertex data (again, only if we sent it to begin with)

We’ll modify our TriangleScene since it has the fewest vertices to set.

// TriangleScene.m in init
triangle.useConstantColor = NO;
triangle.vertexColors[0] = GLKVector4Make(1,0,0,1);
triangle.vertexColors[1] = GLKVector4Make(0,1,0,1);
triangle.vertexColors[2] = GLKVector4Make(0,0,1,1);

Running with that scene set, we see a colored triangle.

iteration-8

Continue on to the next article in the tutorial to learn how to add textures to your shapes—i.e., how to use sprites!

Posted in 2D Game Engine Tutorial, GLKit, Lessons, OpenGL | Leave a comment