Un poco de programar videojuegos

Las matemáticas no sirven para nada y tal…


Trigonometry for Game Programming: Part 1/2



Does the thought of doing mathematics give you cold sweats? Are you ready to give up on your career as a budding game developer because the math just doesn’t make any sense to you?

Don’t fret – math can be fun, and this cool 2-part game tutorial will back up that claim!

Here’s a little secret: as an app developer, you don’t really need to know a lot of math. If you can add or multiply two numbers together, you’re already halfway there. Most of the computations that we do in our professional lives don’t go much beyond basic arithmetic.

That said, for making games it is useful to have a few more math skills in your toolbox. You don’t need to become as smart as Archimedes or Einstein, but a basic understanding of trigonometry, combined with some common sense, will take you a long way.

In this tutorial, you will learn about some important trigonometric functions and how you can use them in your games. Then you’ll get some practice applying the theories by developing a simple spaces shooter iPhone game that requires a lot of trigonometry, using the Cocos2D game framework.

Don’t worry if you’ve never used Cocos2D before or are going to use a different framework for your game – the mathematics we’ll cover in this tutorial will apply to your engine no matter what. And you don’t need any prior experience, as I’ll walk through the process step-by-step.

If you supply the common sense, this tutorial will get you up to speed on the trig, so let’s get started!

Getting Started: It’s All About Triangles

It sounds like a mouthful, but trigonometry simply means calculations with triangles (that’s where the tricomes from).

You may not have realized it until now, but games are full of triangles. For example, imagine you have a spaceship game, and you want to calculate the distance between these ships:

Distance between ships

You have X and Y position of each ship, but how can you find the length of that line?

Well, you can simply draw a line from the center point of each ship to form a triangle like this:

Then, since you know the X and Y coordinates of each ship, you can compute the length of each of the new lines. Now that you know the lengths of two sides of the triangle, you can use some Trigonometry to compute the length of the diagonal line – the distance between the ships.

Note that one of the corners of this triangle has an angle of 90 degrees. This is also known as a right triangle, and that’s the sort of triangle you’ll be dealing with in this tutorial.

Any time you can express something in your game as a triangle with a 90-degree right angle – such as the spatial relationship between the two sprites in the picture – you can use trigonometric functions to do calculations on them.

So in summary, Trigonometry is the mathematics that you use to calculate the lengths of the sides of these triangles, as well as the angles between those sides. And that comes in handy more often that you might think.

For example, in this space ship game you might want to:

  • Have one ship shoot a laser in the direction of the other ship
  • Have one ship start moving in the direction of another ship to chase
  • Play a warning sound effect if an enemy ship is getting too close

All of this and more you can do with the power of Trigonometry!

Your Arsenal of Functions

First, let’s get the theory out of the way. Don’t worry, I’ll keep it short so you can get to the fun coding bits as quickly as possible.

These are the parts that make up a right triangle:

In the picture above, the slanted side is called the hypotenuse. It always sits across from the corner with the 90-degree angle (also called a right angle), and it is always the longest of the three sides.

The two remaining sides are called the adjacent and the opposite, as seen from one particular corner of the triangle, the bottom-left corner in this case.

If you look at the triangle from the other corner (top-right), then the adjacent and opposite change places:

Alpha (α) and beta (β) are the names of the two other angles. You can call these angles anything you want (as long as it sounds Greek!) but usually alpha is the angle in the corner of interest and beta is the angle in the opposing corner. In other words, you label your opposite and adjacent sides with respect to alpha.

The cool thing is that if you only know two of these things, trig allows you to find out all the others using the sine, cosine and tangent functions. For example, if you know an angle and the length of one of the sides, then the sine, cosine and tangent functions can tell you the length of the other sides:

Think of the sin, cos, and tan functions as “black boxes” – you plug in numbers and get back results. They are pre-written functions you can call from almost any programming language.

Know Angle and Length, Need Sides

Let’s consider an example. Say you know the alpha angle between the ships is 45 degrees, and the length between the ships (the hypotenuse) is 10 points long. You can then plug these values into the formula:

sin(45) = opposite / 10

To solve this for the hypotenuse, you shift things around a bit:

opposite = sin(45) * 10

If you call the built-in sin function, you’ll find the sine of 45 degrees is 0.707 (rounded off), and filling that in that gives you the result:

opposite = 0.707 * 10 = 7.07

There is a handy mnemonic for remembering what these functions do that you may remember from high school: SOH-CAH-TOA (where SOH stands for Sine is Opposite over Hypotenuse, and so on), or if you need something more catchy: Some Old Hippy / Caught Another Hippy / Tripping On Acid. (That hippy was probably a mathematician who did a little too much trig.) :]

Know 2 Sides, Need Angle

The above formulas are useful when you already know an angle, but that is not always the case – sometimes it is the angle you are looking for. Then you need to know the lengths of at least two of the sides and plug these into the inverse functions:

Inverse trig functions

  • angle = arcsin(opposite/hypotenuse)
  • angle = arccos(adjacent/hypotenuse)
  • angle = arctan(opposite/adjacent)

In other words, if sin(a) = b, then it is also true that arcsin(b) = a. Of these inverse functions, you will use the arc tangent (arctan) the most in practice. Sometimes these functions are also notated as sin-1, cos-1, and tan-1, so don’t let that fool you.

Is any of this sinking in or sounding familiar? Good, but you’re not done yet with the theory lesson – there is still more that you can calculate with triangles.

Know 2 Sides, Need Remaining Side

Sometimes you may know the length of two of the sides and need to know the length of the third, like in the example at the beginning of this tutorial where you wanted to find the distance between the two space ships.

This is where Trigonometry’s Pythagorean Theorem comes to the rescue. Even if you forgot everything else about math, this is probably the one formula you do remember:

a2 + b2 = c2

Or, put in terms of the triangle that you saw earlier:

opposite2 + adjacent2 = hypotenuse2

If you know any two of these sides, then calculating the third is simply a matter of filling in the formula and taking the square root. This is a very common thing to do in games and you’ll be seeing it several times in this tutorial.

Note: Want to drill this formula into your head while having a great laugh at the same time? Search YouTube for “Pythagoras song” – it’s an inspiration for many!

Have Angle, Need Other Angle

Lastly, the angles. If you know one of the non-right angles from the triangle, then figuring out the other ones is a piece of cake. In a triangle, all angles always add up to a total of 180 degrees. Because this is a right triangle, you already know that one of the angles is 90 degrees. That leaves:

alpha + beta + 90 = 180

Or simply:

alpha + beta = 90

The remaining two angles must add up to 90 degrees. So if you know alpha, you also know beta, and vice-versa.

And those are all the formulas you need to know! Which one to use in practice depends on the pieces that you already have. Usually you either have the angle and the length of at least one of the sides, or you don’t have the angle but you do have two of the sides.

Enough theory. Let’s put this stuff into practice.

To Skip, or Not to Skip?

In the next few sections, you will be setting up a basic Cocos2D project with a space ship that can move around the screen using the accelerometer. This won’t involve any trigonometry (yet), so if you already know Cocos2D and feel like this guy:

"F that!" guy

Then feel free to skip ahead to the Begin the Trigonometry! section below – I have a starter project waiting for you there.

But if you’re the type who likes to code everything from scratch, keep reading! :]

Getting Started

To get started, download the latest version of the Cocos2D v2.1 branch. At the time of writing, this is the “unstable” version cocos2d-iphone-2.1-rc1.

After Cocos2D is done downloading and unzipping, you want to install the project templates. Open a Terminal window to the directory where you downloaded Cocos2D and enter the following command:

./install-templates.sh -f

You should see “Installing cocos2d templates” and a bunch of messages. Now you’re ready to work with Cocos2D.

Fire up Xcode and create a new application from the “cocos2d iOS” template you just installed.

Name the project TrigBlaster and set the device family to iPhone. Build and run the template as-is. If all works OK, you should see the following:

You are going to use ARC in this project, but Cocos2D projects have it disabled by default. Luckily, enabling ARC is really easy. Just go to Edit\Refactor\Convert to Objective-C ARC. Expand the drop-down and select only the last four files (main.m, AppDelegate.m, HelloWorldLayer.m, and IntroLayer.m), then click Check and finish the steps in the wizard.

Note: When you convert a project to ARC or make other large changes, Xcode will prompt you to enable snapshots for the current project. A snapshot saves the state of the entire project as if you copied and pasted the project into another directory. You can learn more about snapshots over here. For the purposes of this tutorial, select disable.

Next download the resources for this tutorial. This file contains the images for the sprites and the sound effects. Unzip it and drag the Images and Sounds folders into Xcode to add them to the project. In the import dialog, make sure Copy items into destination group’s folder is checked.

Import Resources into Project

Great, the preliminaries are over and done with – now let’s get coding for real.

Steering with Accelerometers

Because this is a simple game, you will be doing all your work inside a single class: the HelloWorldLayer. Right now, this layer contains a bunch of stuff that you don’t need.

Replace the contents of HelloWorldLayer.h with:

#import "cocos2d.h"
@interface HelloWorldLayer : CCLayerColor
+ (CCScene *)scene;

This game doesn’t use GameKit, so you got rid of that. You also changed HelloWorldLayer from a plain CCLayerto a CCLayerColor so that you can have a background color other than just black.

There is also a bunch of code in the .m file that you don’t need for this game. Replace HelloWorldLayer.m with:

#import "HelloWorldLayer.h"
#import "SimpleAudioEngine.h"
@implementation HelloWorldLayer
+ (CCScene *)scene
    CCScene *scene = [CCScene node];
    HelloWorldLayer *layer = [HelloWorldLayer node];
    [scene addChild:layer];
    return scene;
- (id)init
    if ((self = [super initWithColor:ccc4(94, 63, 107, 255)]))
    return self;

The scene method is unchanged from the template, but init now calls [super initWithColor] to set a purple background color on the layer.

Build and run, and you should see nothing but purple:

Let’s make things a bit more exciting by adding a spaceship to the scene. You will need some instance variables for this, so modify the @implementation line in HelloWorldLayer.m as follows:

@implementation HelloWorldLayer
    CGSize _winSize;
    CCSprite *_playerSprite;

The _winSize variable stores the dimensions of the screen, which is useful because you will be referring to that quite often. The _playerSprite variable holds the spaceship sprite.

Now change init to the following:

- (id)init
    if ((self = [super initWithColor:ccc4(94, 63, 107, 255)]))
        _winSize = [CCDirector sharedDirector].winSize;
        _playerSprite = [CCSprite spriteWithFile:@"Player.png"];
        _playerSprite.position = ccp(_winSize.width - 50.0f, 50.0f);
        [self addChild:_playerSprite];
    return self;

This is all pretty basic if you have worked with Cocos2D before. The player sprite is positioned in the bottom-right corner of the screen. Remember that with Cocos2D, it is the bottom of the screen that has y-coordinate 0, unlike in UIKit, where y = 0 points to the top of the screen.

Build and run to try it out, and you should see the following:

To move the spaceship, this game uses the iPhone’s built-in accelerometers. Unfortunately, the iOS Simulator cannot simulate the accelerometers. That means that from now on, you will need to run the app on your device to test it.

Note: If you are unsure how to put the app on your device, check out this extensive tutorial that explains how to obtain and install the certificates and provisioning profiles that allow Xcode to run apps on your device. It’s not as intimidating as it looks, but you do need to sign up for the paid Apple developer program.

To move the spaceship with the accelerometers, you’ll obviously be rocking your device from side to side. During testing I found that this sometimes caused the screen to autorotate from landscape right to landscape left, which is really annoying when you’re in the middle of a heated battle.

To prevent that from happening, go into the Target Settings screen and in the Summary tab under Supported Interface Orientations, de-select all options except for Landscape Right.

Using the accelerometers from code is pretty straightforward. You first ask Cocos2D to activate the accelerometers. Once enabled, it sends you periodic updates with the values of the three accelerometers that are built into the iPhone. You then use these values to change the speed of the spaceship.

First, add new instance variables to keep track of the accelerometer values. You only store the values for two of the accelerometers; the “Z” one isn’t used by this game.

@implementation HelloWorldLayer
    . . .
    UIAccelerationValue _accelerometerX;
    UIAccelerationValue _accelerometerY;

A good place to activate the accelerometers is inside init. Add the following line to it:

self.accelerometerEnabled = YES;

Finally, add the method that receives the accelerometer values:

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
    const double FilteringFactor = 0.75;
    _accelerometerX = acceleration.x * FilteringFactor + _accelerometerX * (1.0 - FilteringFactor);
    _accelerometerY = acceleration.y * FilteringFactor + _accelerometerY * (1.0 - FilteringFactor);

This bit of logic is necessary to filter – or “smoothen” – the data that you get back from the accelerometers so that it appears less jittery.

Note: An accelerometer records how much gravity currently pulls on it. Because the user is holding the iPhone in her hands, and hands are never completely steady, there are a lot of tiny fluctuations in this gravity value. We are not so much interested in these unsteady motions as in the larger changes in orientation that the user makes to the device. By applying this simple low-pass filter, you retain this orientation information but filter out the less important fluctuations.

Now that you have a stable measurement of the device’s orientation, how can you use this to make the player’s spaceship move?

Movement in games often works like this:

  1. Set the acceleration based on some form of user input, in this case from the accelerometer values.
  2. Add the new acceleration to the spaceship’s current speed. This makes the object speed up or slow down, depending on the direction of the acceleration.
  3. Add the new speed to the spaceship’s position to make it move.

Of course, you have a great mathematician to thank for these equations: Sir Isaac Newton!

You need some more instance variables to pull this off. There is no need to keep track of the player’s position because the CCSprite already does that for you, but acceleration and speed are your own responsibility.

Add these instance variables:

@implementation HelloWorldLayer
    . . .
    float _playerAccelX;
    float _playerAccelY;
    float _playerSpeedX;
    float _playerSpeedY;

It’s good to set some bounds on how fast the spaceship can travel or it would be pretty hard to maneuver. Infinite acceleration sounds like a good idea in theory but in practice it doesn’t work out so well (besides, even Einstein thinks there are limits to how fast you can go).

Add the following lines directly below the #import statement:

const float MaxPlayerAccel = 400.0f;
const float MaxPlayerSpeed = 200.0f;

This defines two constants, the maximum acceleration (400 points per second squared) and the maximum speed (200 points per second).

Now add the following bit of logic to the bottom of accelerometer:didAccelerate::

if (_accelerometerY > 0.05)
    _playerAccelX = -MaxPlayerAccel;
else if (_accelerometerY < -0.05)
    _playerAccelX = MaxPlayerAccel;
if (_accelerometerX < -0.05)
    _playerAccelY = -MaxPlayerAccel;
else if (_accelerometerX > 0.05)
    _playerAccelY = MaxPlayerAccel;

This is a basic technique for controlling a sprite using the accelerometers. When the device is tilted to the left, you give the player maximum acceleration to the left. Conversely, when the device is tilted to the right, you give the player maximum acceleration to the right. Likewise for the up and down directions.

Note: You’re using the _accelerometerY value for the x-direction. That’s as it should be. Remember that this game is in landscape, so the Y-accelerometer runs from left to right in this orientation, and the X-accelerometer from top to bottom.

You’re almost there. The last step is applying the _playerAccelX and Y values to the speed and position of the spaceship. You will do this in the game’s update method. This method is called 60 times per second, so it’s the natural place to perform all of the game logic.

Add updatePlayer to the class:

- (void)updatePlayer:(ccTime)dt
    // 1
    _playerSpeedX += _playerAccelX * dt;
    _playerSpeedY += _playerAccelY * dt;
    // 2
    _playerSpeedX = fmaxf(fminf(_playerSpeedX, MaxPlayerSpeed), -MaxPlayerSpeed);
    _playerSpeedY = fmaxf(fminf(_playerSpeedY, MaxPlayerSpeed), -MaxPlayerSpeed);
    // 3
    float newX = _playerSprite.position.x + _playerSpeedX*dt;
    float newY = _playerSprite.position.y + _playerSpeedY*dt;
    // 4
    newX = MIN(_winSize.width, MAX(newX, 0));
    newY = MIN(_winSize.height, MAX(newY, 0));
    _playerSprite.position = ccp(newX, newY);

If you’ve programmed games before (or studied physics), then this should look very familiar. Here’s how it works:

  1. Add the current acceleration to the speed.The acceleration is expressed in points per second (actually, per second squared, but don’t worry about that) but update: is performed a lot more often than once per second. To compensate for this difference, you multiply the acceleration by the elapsed or “delta” time, dt. Without this, the spaceship would move about sixty times faster than it should!
  2. Clamp the speed so that it doesn’t go faster than MaxPlayerSpeed if it is positive or -MaxPlayerSpeed if it is negative. You could write this using an if statement instead:
    if (_playerSpeedX < -MaxPlayerSpeed) 
        _playerSpeedX = -MaxPlayerSpeed;
    else if (_playerSpeedX > MaxPlayerSpeed) 
        _playerSpeedX = MaxPlayerSpeed;

    However, I like the succinctness of the one-line version. fminf() makes sure the speed doesn’t become larger than MaxPlayerSpeed because it always picks the smallest of the two, while fmaxf() makes sure the speed doesn’t drop below -MaxPlayerSpeed because it always picks the largest value. It’s a neat little trick.

  3. Add the current speed to the sprite’s position. Again, speed is measured in points per second, so you need to multiply it by the delta time to make it work correctly.
  4. Clamp the new position to the sides of the screen. You don’t want the player’s spaceship to go offscreen, never to be found again!

Finally, add the following line to init to make update active:

[self scheduleUpdate];

Then add update itself:

- (void)update:(ccTime)dt
    [self updatePlayer:dt];

That should do it. Build and run the game. You can now control the spaceship by tilting the device.

Moving the ship with the accelerometer

Begin the Trigonometry!

If you skipped ahead to this section, here is the starter project at this point. Build and run it on your device – you’ll see there’s a spaceship that you can move around with the accelerometer.

You haven’t seen any trigonometry yet, so let’s put some into action.

Note: If you’ve used Cocos2D before, you might know Cocos2D comes with handy built-in methods like ccpLength, ccpAngle, and more that are wrappers over the raw trigonometry functions for ease of use. In this tutorial, you will not be using these functions – instead you will be going low level and issuing the raw trigonometry calls yourself, for the learning experience. In an actual game, you might want to use the wrapper methods however.

It would be cool – and much less confusing to the player! – to rotate the spaceship in the direction it is currently moving rather than having it always pointing upward.

To rotate the spaceship, you need to know the angle to rotate it. But you don’t know what that is, so how can you figure that out?

Let’s think about what you do know. You do have the player’s speed, which consists of two components: a speed in the x-direction and a speed in the y-direction:

If you rearrange these a little, you can see that they form a triangle:

Here you know the lengths of the adjacent (_playerSpeedX) and the opposite (_playerSpeedY) sides.

So basically, you know 2 sides of a right triangle, so you are in the Know 2 Sides, Need Angle case mentioned in the beginning of this tutorial. That means you need to use one of the inverse functions: arcsin, arccos or arctan.

You know the opposite and adjacent sides, so you want to use the arctan function to find the angle to rotate the ship. Remember, that looks like the following:

angle = arctan(opposite / adjacent)

The Objective-C math library comes with the atan() function that computes the arc tangent, but it has one big issue: what if the x-speed is 0? In that case, the adjacent is 0 and dividing by 0 is mathematically undefined. Your app might crash or just behave weirdly when that happens.

Instead of using atan(), it is better to use the function atan2(), which takes two parameters and correctly handles the division-by-zero scenario without you having to worry about it:

angle = atan2(opposite, adjacent)

Important: The angle that atan2f() gives you is not the inner angle inside the triangle, but the angle that the hypotenuse makes with that 0-degree line:

This is another reason why atan2() is a lot more useful than plain, old atan().

So let’s give that a shot. Add the following two lines to the bottom of updatePlayer:

float angle = atan2f(_playerSpeedY, _playerSpeedX);
_playerSprite.rotation = angle;

Note that the arctan function you are using is actually called atan2f() instead of just atan2(). More about that in a moment. Also notice that the y-coordinate goes first. A common mistake is to write atan2f(x, y) but that’s the wrong way around. Remember the first parameter is the opposite, and in this case the Y coordinate is the opposite.

Build and run the app to try it out:

Non-rotating ship

Hmm, this doesn’t seem to be working quite right. What is wrong here?

Radians vs. Degrees

Normal human beings tend to think of angles as values between 0 and 360. Mathematicians, however, measure angles in radians, which are expressed in terms of π (the Greek letter Pi, which sounds like pie but doesn’t taste as good).

It’s not essential to understand, but if you’re curious, one radian is the angle you get when you travel the distance of the radius along the arc of the circle. You can do that 2π times (roughly 6.28 times) before you end up at the beginning of the circle again.

So while you may think of angles as values from 0 to 360, a mathematician sees values from 0 to 2π. And that’s where your problem lies. atan2f() returns a value in radians but Cocos2D’s sprite rotation property expects degrees. That’s why you only saw the sprite rotate a little: angles measured in radians are much smaller than angles measured in degrees.

Fortunately, it is easy to convert from one to the other. Change the line that sets _playerSprite.rotation to:

_playerSprite.rotation = CC_RADIANS_TO_DEGREES(angle);

The CC_RADIANS_TO_DEGREES macro is Cocos2D’s way of doing the conversion but you can easily write your own function or macro to do this. I don’t have these formulas memorized but they are easy enough to derive yourself when you realize than an angle of 360 degrees corresponds to 2π:

Angle in degrees / 360 degrees = Angle in radians / 2π radians

To go from radians to degrees, the formula becomes:

Angle in degrees = (Angle in radians / 2π) * 360

And the other way around, from degrees to radians:

Angle in radians = (Angle in degrees / 360) * 2π

I point this out because forgetting to convert between radians and degrees is probably the most common mistake programmers make when they are dealing with trigonometry! So if you don’t see the rotation you expected, make sure you’re not mixing up your degrees and radians…

So what the @#! is π? Pi is the ratio of the circumference of a circle to its diameter. In other words, if you measure the outside of any circle and divide it by its diameter (which is twice the radius), you get the number 3.141592… and that is what we call π. It takes 2π radians to go all the way around a circle, which is why 360 degrees corresponds to 2π. Pi is a very important concept in mathematics that shows up all the time, especially with anything that is cyclical.

By the way, not everyone likes π. Some people believe that it would have been better to choose the ratio of the circumference to the radius, instead of to the diameter. This number is called tau (τ) and is equal to 2π, thus simplifying many of the calculations. You can read more about tau at tauday.com.

Anyway, back to the game. Build and run to see some glorious rotation in action.

Rotating ship, but opposite

Whoops, something is still not right. The spaceship certainly rotates but it seems to be pointing in the direction opposite to where it’s flying!

Here’s what’s happening: the sprite for the spaceship points straight up, which corresponds to the default rotation value of 0 degrees. But in mathematics, an angle of 0 degrees (or radians) doesn’t point upward, but to the right:

And that’s not the only problem: in Cocos2D, rotation happens in a clockwise direction, but in mathematics it goes counterclockwise.

To overcome these differences, change the line to:

_playerSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(angle);

This adds 90 degrees to make the sprite point to the right at an angle of 0 degrees, so that it lines up with the way atan2f() does things. Then it adds the negative angle – in other words, subtracts the angle – in order to rotate the proper way around.

Build and run once more to try it out. You’ll finally have a spaceship that has its head on straight!

Spaceship flying with correct rotation

Bouncing Off the Walls

You have a spaceship that you can move using the accelerometers and you’re using trig to make sure it points in the direction it’s flying. That’s a good start.

Unfortunately, having the spaceship get stuck on the edges of the screen isn’t very satisfying. You’re going to fix that by making it bounce off the screen borders instead.

First, comment these lines from updatePlayer:

//newX = MIN(_winSize.width, MAX(newX, 0));
//newY = MIN(_winSize.height, MAX(newY, 0));

Then add the following code to updatePlayer, just after the commented lines:

BOOL collidedWithVerticalBorder = NO;
BOOL collidedWithHorizontalBorder = NO;
if (newX < 0.0f)
    newX = 0.0f;
    collidedWithVerticalBorder = YES;
else if (newX > _winSize.width)
    newX = _winSize.width;
    collidedWithVerticalBorder = YES;
if (newY < 0.0f)
    newY = 0.0f;
    collidedWithHorizontalBorder = YES;
else if (newY > _winSize.height)
    newY = _winSize.height;
    collidedWithHorizontalBorder = YES;

This checks whether the spaceship hit any of the screen borders and if so, sets a BOOL variable to YES. But what to do after such a collision takes place? To make the spaceship bounce off the border, you can simply reverse its speed and acceleration.

First, define a constant at the top of the file, above the @implementation line:

const float BorderCollisionDamping = 0.4f;

Add the following lines in updatePlayer, directly below the code you last added there:

if (collidedWithVerticalBorder)
    _playerAccelX = -_playerAccelX * BorderCollisionDamping;
    _playerSpeedX = -_playerSpeedX * BorderCollisionDamping;
    _playerAccelY = _playerAccelY * BorderCollisionDamping;
    _playerSpeedY = _playerSpeedY * BorderCollisionDamping;
if (collidedWithHorizontalBorder)
    _playerAccelX = _playerAccelX * BorderCollisionDamping;
    _playerSpeedX = _playerSpeedX * BorderCollisionDamping;
    _playerAccelY = -_playerAccelY * BorderCollisionDamping;
    _playerSpeedY = -_playerSpeedY * BorderCollisionDamping;

If a collision registered, you flip the acceleration and speed values around. Notice that this also multiplies the acceleration and speed by a damping value, BorderCollisionDamping.

As usually happens in a collision, some of the movement energy is dissipated by the impact. In this case, you make the spaceship retain only 40% of its speed after bumping into the screen edges.

Try it out. Smash the spaceship into the border and see what happens. Who said spaceships can’t bounce?

Bouncing spaceship

For fun, play with the value of BorderCollisionDamping to see the effect of different values for this constant. If you make it larger than 1.0f, the spaceship actually gains energy from the collision.

Note: Now why is there an f behind those numbers in the code: 0.4f, 0.1f, 0.0f, and so on? And why did you use atan2f() instead of just atan2()? When you write games, you want to work with floating point numbers as much as possible because, unlike integers, they allow for digits behind the decimal point. This allows you to be much more precise.

There are two types of floating point numbers: floats and doubles (there is also a “long double”, but that’s the same as a double on the iPhone). Doubles are more precise than floats but they also take up more memory and are slower in practice. When you don’t put the f behind the number and just use 0.4, 0.1, 0.0, or when you use the version of a math function without the f suffix, you are working with doubles and not floats.

It doesn’t really matter if you use a double here and there. For example, the time value that CACurrentMediaTime() returns is a double. However, if you’re doing hundreds of thousands of calculations every frame, you will notice the difference. I did a quick test on a couple of my devices and the same calculations using doubles were 1.5 to 2 times slower. So it’s a good habit to stick to regular floats where you can.

You may have noticed a slight problem. Keep the spaceship aimed at the bottom of the screen so that it continues smashing into the border over and over, and you’ll see a constant flicker between the up and down angles.

Using the arc tangent to find the angle between a pair of x- and y-components works quite well, but only if those x and y values are fairly large. In this case, the damping factor has reduced the speed to almost zero. When you apply atan2f() to very small values, even a tiny change in these values can result in a big change in the resulting angle.

One way to fix this is to not change the angle when the speed is very slow. That sounds like an excellent reason to give a call to our old friend, Pythagoras.


Right now you don’t have such a thing as “the ship’s speed”. Instead, you have two speeds: one in the x-direction and one in the y-direction. But in order to draw any conclusions about “the ship’s speed” – is it too slow to actually rotate the ship? – you need to combine these x and y speed values into one single value:

Here you are in the Know 2 Sides, Need Remaining Side case discussed earlier.

As you can see, the true speed of the spaceship, that is, how many points it moves across the screen in a second, is the hypotenuse of the triangle that is formed by the speed in the x-direction and the speed in the y-direction. Put in terms of the Pythagorean formula:

true speed2 = _playerSpeedX2 + _playerSpeedY2

To find the actual value, you need to take the square root:

true speed = √(_playerSpeedX2 + _playerSpeedY2)

Add this code to updatePlayer. First remove this block of code:

float angle = atan2f(_playerSpeedY, _playerSpeedX);
_playerSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(angle);

And replace it with this block of code:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
    _playerSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(angle);

Build and run. You’ll see the spaceship now acts a lot more stable at the edges of the screen. If you’re wondering where the value 40.0f came from, the answer is: experimentation. I put some NSLog() statements into the code to look at the speeds at which the craft typically hit the borders, and then I tweaked this value until it felt right.

Blending Angles for Smooth Rotation

Of course, fixing one thing always breaks something else. Try slowing down the spaceship until it has stopped, then flip the device so the spaceship has to turn around and fly the other way.

Previously, that happened with a nice animation where you actually saw the ship turning. But because you just added some code that prevents the ship from changing its angle at low speeds, the turn is now very abrupt. It’s only a small detail, but it is the details that make great products.

The fix is to not switch to the new angle immediately, but to gradually “blend” it with the previous angle over a series of successive frames. This re-introduces the turning animation and still prevents the ship from rotating when it is not moving fast enough.

Blending sounds fancy, but it’s actually quite easy to implement. It does require you to keep track of the spaceship’s angle between updates, so add a new instance variable for it in the implementation block in HelloWorldLayer.m:

@implementation HelloWorldLayer
    float _playerAngle;

Change the rotation code in updatePlayer from this:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
    _playerSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(angle);

To this:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
    const float RotationBlendFactor = 0.2f;
    _playerAngle = angle * RotationBlendFactor + _playerAngle * (1.0f - RotationBlendFactor);
_playerSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(_playerAngle);

The _playerAngle variable combines the new angle and its own previous value by multiplying them with a blend factor. In human-speak, this means the new angle only counts for 20% towards the actual rotation that you set on the spaceship. Of course, over time more and more of the new angle gets added so that eventually the spaceship does point in the proper direction.

Note that the line that sets the _playerSprite’s rotation property is now outside the if statement.

Build and run to verify that there is no longer an abrupt change from one rotation angle to another.

Now try flying in a circle a couple of times, both clockwise and counterclockwise. You’ll notice that at some point in the turn the spaceship suddenly spins in the opposite direction. It always happens at the same point in the circle. What’s going on?

Well, there is something you should know about atan2f(). It does not return an angle in the convenient range of 0 to 360 degrees, but a value between +π and –π radians, or between +180 and -180 degrees to us non-mathematicians:

That means if you’re turning counterclockwise, at some point the angle will jump from +180 degrees to -180 degrees; or the other way around if you’re turning clockwise. And that’s where the weird spinning effect happens.

The problem is that when the new angle jumps from 180 degrees to -180 degrees, _playerAngle is still positive because it is trailing behind a bit. When you blend these two together, the spaceship actually starts turning the other way around. It took me a while to figure out what was causing this!

To fix it, you need to recognize when the angle makes that jump and adjust _playerAngle accordingly. Add a new instance variable at the bottom of the HelloWorldLayer implementation block:

@implementation HelloWorldLayer
    float _lastAngle;

And change the rotation code one more time from this:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
    const float RotationBlendFactor = 0.2f;
    _playerAngle = angle * RotationBlendFactor + _playerAngle * (1.0f - RotationBlendFactor);
_playerSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(_playerAngle);

To this:

float speed = sqrtf(_playerSpeedX*_playerSpeedX + _playerSpeedY*_playerSpeedY);
if (speed > 40.0f)
    float angle = atan2f(_playerSpeedY, _playerSpeedX);
    // Did the angle flip from +Pi to -Pi, or -Pi to +Pi?
    if (_lastAngle < -3.0f && angle > 3.0f)
        _playerAngle += M_PI * 2.0f;
    else if (_lastAngle > 3.0f && angle < -3.0f)
        _playerAngle -= M_PI * 2.0f;
    _lastAngle = angle;
    const float RotationBlendFactor = 0.2f;
    _playerAngle = angle * RotationBlendFactor + _playerAngle * (1.0f - RotationBlendFactor);
_playerSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(_playerAngle);

Build and run. That’ll fix things right up and you should have no more problems turning your spacecraft!

This may have seemed like an arcane little problem not worth so much time, but it’s good to be aware that atan2f() gives you angles from -180 degrees (or –π radians) to +180 degrees (or π radians). If you’re using arc tangent to calculate an angle and you get weird behavior like the spinning you saw here, then you might need to verify that your angles really are what you expect them to be.

Using Trig to Find Your Target

This is a great start – you have a spaceship moving along pretty smoothly! But so far the spaceship’s life is too easy and carefree. Let’s spice this up by adding an enemy: a big cannon!

Add two new instance variables in the implementation block in HelloWorldLayer.m:

@implementation HelloWorldLayer
    CCSprite *_cannonSprite;
    CCSprite *_turretSprite;

You’ll set these sprites up in init. Place this code before the creation of _playerSprite so that the spaceship always gets drawn “above” the cannon:

_cannonSprite = [CCSprite spriteWithFile:@"Cannon.png"];
_cannonSprite.position = ccp(_winSize.width/2.0f, _winSize.height/2.0f);
[self addChild:_cannonSprite];
_turretSprite = [CCSprite spriteWithFile:@"Turret.png"];
_turretSprite.position = ccp(_winSize.width/2.0f, _winSize.height/2.0f);
[self addChild:_turretSprite];

The cannon consists of two sprites: the fixed base and the turret that can rotate to take aim at the player. Build and run, and you should have a brand-new cannon sitting smack in the middle of the screen.

Now you’ll give the cannon a target to snipe at – I bet you know who!

Yep, its turret should point at the player at all times. To get this to work, you need to figure out the angle to rotate the turret so that it points toward the player.

Figuring this out will be very similar to how you figured out how to rotate the spaceship in order to point toward the direction it’s moving in. The difference is that this time, the triangle won’t be based on the speed of the spaceship. Instead it will be drawn between the center positions of the two sprites:

Again, you can use atan2f() to calculate this angle. Add the following method:

- (void)updateTurret:(ccTime)dt
	float deltaX = _playerSprite.position.x - _turretSprite.position.x;
	float deltaY = _playerSprite.position.y - _turretSprite.position.y;
	float angle = atan2f(deltaY, deltaX);
	_turretSprite.rotation = 90.0f - CC_RADIANS_TO_DEGREES(angle);

The deltaX and deltaY variables measure the distance between the player sprite and the turret sprite. You plug these values into atan2f() to get angle of rotation.

As before, you need to convert this angle to degrees and adjust for the differences between mathematics and Cocos2D. Remember that atan2() always gives you the angle between the hypotenuse and the 0-degree line. It is not the angle inside the triangle.

Don’t forget to call this new updateTurret: method from update, or nothing much will happen:

- (void)update:(ccTime)dt
    [self updatePlayer:dt];
    [self updateTurret:dt];

Build and run. The cannon is now always pointing at the spaceship. See how easy that was? That’s the power of trig for you!

Turret pointed towards player

Challenge: It is unlikely that a real cannon would be able to act so instantaneously – it would have to be able to predict exactly where the target was going. Instead, a real cannon would always be playing catch up.

You can accomplish this by “blending” the old angle with the new one, just like you did earlier. The smaller the blend factor, the more time the turret needs to catch up with the spaceship. See if you can implement this on your own.

Adding Health Bars… That Move!

Soon the player will be able to fire missiles at the cannon, and the cannon will be able to inflict damage on the player. To show the amount of hit points each object has remaining, you will add health bars to the scene using CCDrawNode, a new feature of Cocos2D v2.1.

First, you have to do some prep. Add a couple of new constants to the top of the file:

const int MaxHP = 100;
const float HealthBarWidth = 40.0f;
const float HealthBarHeight = 4.0f;

Also add a couple of new instance variables in the implementation block:

@implementation HelloWorldLayer
    int _playerHP;
    int _cannonHP;
    CCDrawNode *_playerHealthBar;
    CCDrawNode *_cannonHealthBar;

Add the following to the bottom of init to set up these new variables:

_playerHealthBar = [[CCDrawNode alloc] init];
_playerHealthBar.contentSize = CGSizeMake(HealthBarWidth, HealthBarHeight);
[self addChild:_playerHealthBar];
_cannonHealthBar = [[CCDrawNode alloc] init];
_cannonHealthBar.contentSize = CGSizeMake(HealthBarWidth, HealthBarHeight);
[self addChild:_cannonHealthBar];
_cannonHealthBar.position = ccp(
    _cannonSprite.position.x - HealthBarWidth/2.0f + 0.5f,
    _cannonSprite.position.y - _cannonSprite.contentSize.height/2.0f - 10.0f + 0.5f);
_playerHP = MaxHP;
_cannonHP = MaxHP;

The _playerHealthBar and _cannonHealthBar objects are instances of CCDrawNode, which is like a sprite except that you can draw arbitrary shapes into it. That is ideal for your health bars, which consist of a border and a filled rectangle.

Note that you placed the _cannonHealthBar sprite slightly below the cannon, but didn’t assign a position to the _playerHealthBar yet. That’s because the cannon never moves, so you can simply set the position of its health bar once and forget about it.

However, whenever the spaceship moves, you have to adjust the position of the _playerHealthBar as well. That happens in updatePlayer. Add the following lines to the bottom of that method:

_playerHealthBar.position = ccp(
    _playerSprite.position.x - HealthBarWidth/2.0f + 0.5f,
    _playerSprite.position.y - _playerSprite.contentSize.height/2.0f - 15.0f + 0.5f);

Now all that’s left is drawing the bars themselves. Add this new method to the bottom of the file:

- (void)drawHealthBar:(CCDrawNode *)node hp:(int)hp
    [node clear];
    CGPoint verts[4];
    verts[0] = ccp(0.0f, 0.0f);
    verts[1] = ccp(0.0f, HealthBarHeight - 1.0f);
    verts[2] = ccp(HealthBarWidth - 1.0f, HealthBarHeight - 1.0f);
    verts[3] = ccp(HealthBarWidth - 1.0f, 0.0f);
    ccColor4F clearColor = ccc4f(0.0f, 0.0f, 0.0f, 0.0f);
    ccColor4F fillColor = ccc4f(113.0f/255.0f, 202.0f/255.0f, 53.0f/255.0f, 1.0f);
    ccColor4F borderColor = ccc4f(35.0f/255.0f, 28.0f/255.0f, 40.0f/255.0f, 1.0f);
    [node drawPolyWithVerts:verts count:4 fillColor:clearColor borderWidth:1.0f borderColor:borderColor];
    verts[0].x += 0.5f;
    verts[0].y += 0.5f;
    verts[1].x += 0.5f;
    verts[1].y -= 0.5f;
    verts[2].x = (HealthBarWidth - 2.0f)*hp/MaxHP + 0.5f;
    verts[2].y -= 0.5f;
    verts[3].x = verts[2].x;
    verts[3].y += 0.5f;
    [node drawPolyWithVerts:verts count:4 fillColor:fillColor borderWidth:0.0f borderColor:clearColor];

This sets up an array of points for the corners of the rectangle that you’ll be drawing, then draws it twice: once for the border, which always has the same size, and once for the amount of health, which changes depending on the number of remaining hit points.

Of course, you need to call this method, once for the player and once for the cannon. Add these two lines to update:

[self drawHealthBar:_playerHealthBar hp:_playerHP];
[self drawHealthBar:_cannonHealthBar hp:_cannonHP];

Build and run. Now both the player and the enemy have health bars:

Using Trig for Collision Detection

Right now, the spaceship can fly directly over the cannon without consequence. It would be more fun if it didn’t have it so easy and instead suffered damage for colliding with the cannon. This is where this tutorial enters the sphere of collision detection (don’t miss that pun).

At this point, a lot of game devs think, “I need a physics engine!” and reach for Box2D. While it’s certainly true that you can use Box2D for this, it’s not that hard to do collision detection yourself, especially when you can use circles.

Detecting whether two circles intersect is a piece of cake: all you have to do is look at the distance between them (*cough* Pythagoras) and see if it is smaller than the radius of both circles.

Add two new constants to the top of HelloWorldLayer.m:

const float CannonCollisionRadius = 20.0f;
const float PlayerCollisionRadius = 10.0f;

These are the sizes of the collision circles around the cannon and the player. Looking at the sprite, you’ll see that the radius of the cannon is slightly larger at 25 points, but it’s nice to have a bit of wiggle room. You don’t want your games to be too unforgiving or players will think it is unfair.

The fact that the spaceship isn’t circular at all shouldn’t deter you. Often a circle is a good enough approximation of the shape of your sprite, and it has the big advantage that it makes it easy to do the necessary calculations. In this case, the body of the ship is roughly 20 points in diameter (remember, the diameter is twice the radius).

Add a new method that does the collision detection:

- (void)checkCollisionOfPlayerWithCannon
    float deltaX = _playerSprite.position.x - _turretSprite.position.x;
    float deltaY = _playerSprite.position.y - _turretSprite.position.y;
    float distance = sqrtf(deltaX*deltaX + deltaY*deltaY);
    if (distance <= CannonCollisionRadius + PlayerCollisionRadius)
        [[SimpleAudioEngine sharedEngine] playEffect:@"Collision.wav"];

You’ve seen how this works before: first you calculate the distance between the x-positions of the two sprites, then the y-distance. With these two values, you can calculate the hypotenuse, which is the true distance between these sprites. If that distance is smaller than the collision circles, a sound effect is played.

In update, add a call to this new method, above the calls to drawHealthBar:

[self checkCollisionOfPlayerWithCannon];

Also add this line to init to pre-load the sound effect:

[[SimpleAudioEngine sharedEngine] preloadEffect:@"Collision.wav"];

Give it a whirl and drive the spaceship into the cannon.

Ship colliding with turret

Notice that the sound effect is a little odd. It’s because while the spaceship flies over the cannon, the game registers repeated collisions, one after another. There isn’t just one collision, there are many, and it plays the sound effect for every one of them.

Collision detection is only the first step. The second step is collision response. Not only do you want to play a sound effect, but you also want the spaceship to bounce off the cannon.

Add these lines inside the if statement in checkCollisionOfPlayerWithCannon:

const float CannonCollisionDamping = 0.8f;
_playerAccelX = -_playerAccelX * CannonCollisionDamping;
_playerSpeedX = -_playerSpeedX * CannonCollisionDamping;
_playerAccelY = -_playerAccelY * CannonCollisionDamping;
_playerSpeedY = -_playerSpeedY * CannonCollisionDamping;

This is similar to what you did for making the spaceship bounce off the screen borders. Build and run to see how it works.

Bouncing spaceship

It works pretty well when the spaceship is going fast when it hits the cannon. But if it’s moving too slow, then even after reversing the speed, the ship stays within the collision radius and never makes its way out of it. So this solution has some problems.

Instead of bouncing the ship off the cannon by reversing its existing speed, you will simply expel the ship with a fixed speed so that it always moves outside of the collision area, no matter how fast or slow it was flying when it hit the cannon.

This requires you to add a new constant at the top of HelloWorldLayer.m:

const float CannonCollisionSpeed = 200.0f;

Remove those lines you just added in checkCollisionOfPlayerWithCannon and replace them with the following:

float angle = atan2f(deltaY, deltaX);
_playerSpeedX = cosf(angle) * CannonCollisionSpeed;
_playerSpeedY = sinf(angle) * CannonCollisionSpeed;
_playerAccelX = 0.0f;
_playerAccelY = 0.0f;
_playerHP = MAX(0, _playerHP - 20);
_cannonHP = MAX(0, _cannonHP - 5);

That is the speed you want the spaceship to have after it collides with the cannon. You need to calculate how much of that speed goes in the x-direction and how much in the y-direction, based on the angle of impact:

You can calculate the angle using arctan because you know the current speed of the player. To calculate how fast the player bounces off the cannon in the x- and y-directions, you already have two pieces of information: the angle that you just calculated, and the new speed, CannonCollisionSpeed, which is the hypotenuse of the triangle.

If you look back at the theory section, you will see that you can calculate the new x-component of the speed using the cosine function and the y-component using the sine function:

  • new x-speed = cos(angle) * hypotenuse
  • new y-speed = sin(angle) * hypotenuse

And that’s exactly what you’re doing in the code above. Build and run, and you’ll see the spaceship now bounces properly off the cannon and loses some hit points in the process.

Adding Some Spin

For an additional effect, you can add spin to the spaceship after a collision. This is additional rotation that doesn’t influence the flight direction. It just makes the effect of the collision more profound (and the pilot dizzy). Add a new instance variable in the implementation block:

@implementation HelloWorldLayer
    float _playerSpin;

In checkCollisionOfPlayerWithCannon, add the following line inside the collision detection block:

if (distance <= CannonCollisionRadius + PlayerCollisionRadius)
    _playerSpin = 180.0f * 3.0f;

This sets the amount of spin to a circle and a half, which I think looks pretty good. Now at the bottom of updatePlayer, add the following code to add spin to the rotation:

_playerSprite.rotation += _playerSpin;
if (_playerSpin > 0.0f)
    _playerSpin -= 2.0f * 360.0f * dt;
    if (_playerSpin < 0.0f)
        _playerSpin = 0.0f;

The amount of spin quickly decreases over time – I chose a speed of 720 degrees per second. Once the spin has reached 0, it stops.

Build and run and set that ship spinning!

Where to Go from Here?

Here is the full example project from the tutorial up to this point.

Triangles are everywhere! You’ve seen how you can use this fact to breathe life into your sprites with the various trigonometric functions.

You have to admit, it wasn’t that hard to follow along, was it? Math doesn’t have to be boring if you can apply it to fun projects, such as making games.

But there’s more to come: in Part 2 of this Trigonometry for Game Programming series, you’ll add missiles to the game, learn more about sine and cosine, and see some other useful ways to put the power of trig to work in your games.

In the meantime, drop by the forums to share how it’s going for you so far! Or if you really can’t wait until part 2, why not brush up on some more Cocos2D with some of our other tutorials. Alternatively, if you want to start diving into more game development why not take a look at the games starter kits available on this site.

Credits: The graphics for this game are based on a free sprite set by Kenney Vleugels. The sound effects are based on samples from freesound.org.

Matthijs Hollemans

This is a post by iOS Tutorial Team member Matthijs Hollemans, an experienced iOS developer and designer. You can find him on Google+ and Twitter.”



Casi todas las grandes compañías tienen sus estereotipos. Ya sea escribir nombres en vasos (Starbucks), etiquetar todo (Instagram) o perder cosas (servicios postales), todas las compañías los tienen. El artista conceptual e ilustrador de Kazajstan, Leonid Khan, ha dibujado 12 divertidas ilustraciones para Bright Side mostrando cómo serían las entrevistas de trabajo en las compañías famosas basándose en sus estereotipos. ¿Cuál os parece más real?



entrevista culturainquieta



entrevista1 culturainquieta



entrevista4 culturainquieta



entrevista6 culturainquieta



entrevista7 culturainquieta



entrevista8 culturainquieta



entrevista9 culturainquieta



entrevista10 culturainquieta



entrevista11 culturainquieta



entrevista12 culturainquieta



entrevista13 culturainquieta

via brightside



Así es como los lobbies han arruinado tu alimentación


¿Crees que debes reducir grasas y hacer más ejercicio para adelgazar? Quizá te interese saber que eso es un mito de la industria para no señalar a los verdaderos culpables
Foto: Margaret Chan - OMS

Actualmente muere más gente por obesidad que por hambre. Un tercio de la población mundial sufre, como mínimo, sobrepeso (se considera sobrepeso contar con un IMC > 25). Ningún gobierno del mundo ha conseguido reducir esta lacra en las últimas 3 décadas y, según McKinsey, en el 2030 a la mitad de la población mundial le sobrarán kilos. La obesidad es, de hecho, uno de los principales costes de nuestros sistemas sanitarios y no para de aumentar. Da igual que hablemos de países desarrollados o emergentes, estamos ante una epidemia silenciosa y global. ¿Qué está pasando?

Puede que el primer culpable en nuestra mente sea el sedentarismo pero, paradójicamente, hacemos más deporte y vamos más al gimnasio que nunca. Puede que el segundo teórico culpable sea la ingesta excesiva de grasa, al fin y al cabo cuando miramos nuestros michelines es lo que vemos, y si está ahí será porque antes la hemos comido. O quizá culpemos al exceso de calorías en nuestra dieta diaria: si comemos más calorías de las que necesitamos la diferencia se acumula en grasa, o eso dicen las creencias populares.

Yo también lo creía. Creía que el sobrepeso y la obesidad eran por no cuidarse lo suficiente, y que, cuando alguien afirmaba seguir una dieta relativamente saludable y ganar peso de igual forma, simplemente no estaba diciendo toda la verdad. Como a mí no me pasaba, creía simplemente que otros no hacían lo suficiente. Pero me equivocaba, como así pude comprobar tras meses y meses leyendo estudios científicos sobre nutrición. Mi conclusión actual es que casi todo lo que creemos saber sobre cómo alimentarnos no es correcto y, además, sí existen unos culpables claros en la epidemia de la obesidad.


Puede parecer extraño tratar un tema así en una sección de economía, pero el hecho es que me siento en la obligación de contar lo aprendido, aun siendo una ‘verdad incómoda’. Además, no podríamos estar ante un tema más económico pues, si las cosas son como son es, seguramente, por la fuerte presión que han ejercido los lobbies de la industria alimentaria en las últimas décadas. Si a eso le añadimos a gobernantes que han preferido velar por ciertos intereses privadosen lugar de por la salud pública, y científicos que venden su voz a cambio de considerables sumas de dinero, ya tenemos el guion de esta historia.

Lo que la evidencia científica independiente parece decir en los últimos años es que el gran problema son el azúcar y las harinas refinadas. Cuando tomamos una dosis de azúcar añadido (o de sucrosa, o fructosa, o glucosa, o dextroxa, o lactosa, o maltosa, o jarabe de maíz, o maltodextrina… o alguno de los cientos de nombres que existen para que no sepas lo que estás tomando cuando compras un producto procesado) o de harina blanca refinada (productos procesados, pan, arroz, pasta y cereales no integrales) lo que provocamos en realidad en el cuerpo es una reacción en forma de insulina, derivada del alto índice glucémico y alta carga glucémicade estos alimentos.

Cuando obtenemos energía por medio de las proteínas y las grasas no se produce este efecto, obteniendo una liberación más progresiva y que, por tanto, compensamos con nuestra actividad diaria. Lo mismo ocurre con los hidratos de carbono con bajo IG. Sin embargo, en el caso de los azúcares y las harinas refinadas, el proceso es tan rápido que el efecto es una transformación rápida en grasa abdominal (con el incremento de riesgo para nuestra salud que eso conlleva: enfermedades cardiovasculares, diabetes e incluso cáncer).


Por ello, aunque parezca contraintuitivo, tendremos menos grasa tomándola directamente que ingiriendo azúcar o harinas refinadas. De hecho, solo las grasas ‘trans’ son claramente negativas, no existiendo evidencias en las demás (o incluso considerándose positivas, recordemos por ejemplo el aceite de oliva).

Existen polémicas sobre innumerables cuestiones: sobre los productos integrales, sobre los lácteos, sobre la carne roja, sobre los edulcorantes, sobre tomar más o menos hidratos de carbono… Sin embargo, por lo que respecta a nuestros ‘amigos blancos’, cada estudio o libro publicado en los últimos meses no financiado por la industria es claro: el azúcar blanco y las harinas refinadas son un culpable claro de la epidemia de la obesidad. Si la evidencia parece tan rotunda, ¿por qué los gobiernos no toman cartas en el asunto? ¿Por qué no se modifican los patrones recomendados de las ‘dietas saludables’? ¿Por qué la población no sabe cómo cuidar su alimentación?

Voy a contar una breve historia que quizá ayude a entenderlo. A partir de los años 50, en Estados Unidos empezaron a publicarse estudios señalando al azúcar como un elemento nocivo para la salud, algo que inquietó a la industria: su reducción en la dieta diaria podría perjudicar sus beneficios. Por ello se ofrecieron a ‘colaborar’. Por ejemplo cuando el azúcar se señaló como culpable de la caries, la industria ‘colaboró’ con el Insituto Nacional para la Investigación Dental, quién aceptó el 78% de sus propuestas y abandonó toda intención de limitar el azúcar.

Cuando las evidencias se hicieron aun más fuertes, y los estudios comenzaron a relacionar la ingesta con la obesidad, los lobbies ‘promocionaron’ su propia teoría: el culpable de la obesidad no era tanto el azúcar como las grasas saturadas. Hasta hoy, y eso que no existe evidencia en contra de las grasas saturadas. Así, los gobiernos aceptaron la hipótesis y se estableció que lo correcto era reducir las calorías de los alimentos y reducir sus grasas, aun a costa de aumentar el azúcar añadido. Algo que perdura hoy en día, puesto que detrás de cada alimento ‘bajo en grasa’, existe en realidad uno ‘alto en azúcar’ (o cualquier otro sinónimo), nada recomendable para bajar de peso, por cierto.


Cuando las evidencias se hicieron aun más sólidas, la industria comenzó a contar con personajes más renombrados, contratando a científicos de Harvard para velar por sus intereses. Tal era la influencia de sus lobbies que rápidamente se expandió la idea por todo el mundo: si quieres adelgazar come menos grasa y haz deporte. Y algo parecido se puede decir del trigo, promocionado por departamentos gubernamentales como el Departamento de Agricultura de Estados Unidos, quien de la noche a la mañana se convirtió en un oráculo de la nutrición, a pesar de que su función era la promoción de los productos. La pirámide alimenticia no fue un acto de educación sobre la población, fue un acto de marketing para favorecer la industria autóctona.

Si crees que estamos ante hechos aislados, recomendaría una mayor lectura de la prensa diaria. El pasado mes se descubrió que tanto Coca-Cola como Pepsi pagaron a 96 organizaciones sanitarias estadounidenses para silenciar sus críticas sobre el azúcar. Cada vez que alguien abre la boca criticando, las multinacionales hacen presión para hacerlo cambiar de parecer, dándose situaciones tan rocambolescas como ver a la Asociación Americana contra la Diabetes luchando por eliminar un posible impuesto sobre el azúcar. Más criticable aún que las prácticas de la industria, copiadas de las empleadas tabaqueras, es la facilidad con la que se venden los gobiernos y organismos públicos.

Si alguien, a pesar de todas las evidencias existentes, cree que se trata de una conspiración, quizá debería de escuchar a Margaret Chan, Directora General de la Organización Mundial de la Salud. El organismo, que durante años ha suavizado sus posturas al respecto (y recibido millones de euros), afirma ahora (traducción de Juan Revenga) lo siguiente:

[…] En la actualidad las campañas de promoción de estilos de vida saludables y la adopción de conductas para alcanzarlos se encuentran con la oposición de fuerzas que distan mucho de ser “amables”. Más bien, todo lo contrario.

El esfuerzo público dirigido a prevenir las enfermedades no transmisibles [tras hacer una especial alusión a la obesidad, la diabetes y el cáncer] se enfrenta a intereses comerciales de poderosos agentes económicos. En mi opinión, este es uno de los mayores retos a los que se enfrentan las campañas de promoción de la salud.

Tal y como pone de manifiesto la publicación que resume esta conferencia [ver al completo en este enlace] no solo se trata del problema observado en otro tiempo con las grandes tabacaleras (Big Tobacco). Ahora, la Salud Pública tiene que lidiar también con otras industrias en los mismos términos que entonces; se trata de la “Big Food”, “Big Soda” y “Big Alcohol”. Son estas industrias las que en este momento temen una regulación de sus productos por parte de las administraciones sanitarias y las que están recurriendo a las mismas tácticas que antaño puso en práctica la industria tabacalera.

El pasado reciente aporta pruebas suficientes para documentar que estas tácticas por parte de la industria alimentaria ya se han puesto en marcha. Entre ellas, se incluyen la creación de empresas dentro del mismo grupo con una “cara amable”, la creación de grupos de presión [lobby], el realizar promesas de autorregulación, el interponer demandas y el financiar estudios de investigación que lo que consiguen es tergiversar la evidencia y confundir al ciudadano.

Además, este tipo de tácticas también incluye la realización de donaciones, regalos y contribuciones relacionados con causas nobles, o bien vistas por parte de la comunidad, de forma que estas industrias terminan siendo percibidas como corporaciones respetables tanto a los ojos de la ciudadanía como ante los de la clase política. Entre sus estrategias destacan también el hacer descansar la responsabilidad de una mala salud en cada persona, individualmente; así como pretender hacer creer que las acciones de los gobiernos por regular estas cuestiones no son otra cosa sino una forma más de interferir en la libertad personal de cada cual y su derecho a elegir libremente.

La oposición ejercida es de una magnitud formidable. Ya que un amplio poder en los mercados se traduce en poco tiempo en poder político, son pocos los gobiernos que han priorizado las cuestiones de salud frente a los grandes negocios. Tal y como hemos aprendido de experiencias anteriores, como con la del tabaco, cuando una corporación poderosa se lo propone, puede vender casi cualquier cosa a la población.

Déjenme recordarles una cosa. Ni un solo país del mundo ha conseguido darle la vuelta a la epidemia de obesidad en todos los grupos de edad. Esta realidad no es consecuencia de una falta de voluntad individual. Es consecuencia de la ausencia de voluntad política a la hora de meter mano en este gran negocio.

Estoy profundamente preocupada por dos actuales tendencias:

La primera implica la posibilidad de que la industria y las administraciones lleguen a acuerdos “comerciales” fruto de las denuncias de la primera sobre las segundas. En la actualidad, algunos de los gobiernos que han establecido medidas para proteger la salud de sus ciudadanos están siendo llevados a los tribunales por parte de la industria. Y esto es peligroso.

La segunda se refiere al interés que pone la industria para moldear las políticas de Salud Pública, en especial aquellas medidas que afectan a sus productos. Si una industria está involucrada en la formulación de políticas de Salud Pública, tengan la seguridad de que aquellas medidas más eficaces serán o bien minimizadas o bien apartadas en su totalidad. Esta tendencia también está bien documentada y es así mismo peligrosa.


Yo no soy nutricionista, ni médico. No sé de alimentación más que lo que he leído en meses tratando de aprender al respecto. Pueden creerme, o no. Pueden tomar en cuenta los consejos y reducir azúcar y harinas refinadas (las grasas trans también estarían bien), o no. Pero sí sé como actúan muchas empresas, y la conducta de Coca-Cola o Pepsi, o de otros que venden cereales nada saludables, encaja perfectamente con la de empresas que ven como su negocio podría menguar si la población descubre la verdad.

El azúcar o la harina blanca son adictivos, saben bien y son baratos, la industria no está preparada para prescindir de ellos (sus esperanzas a futuro están puestas en los controvertidos edulcorantes). Sin embargo lo más probable es que sean uno de los elementos más perjudiciales que tenemos en nuestra alimentación actual. Lo más probable es que esa tripa que tienes sea su culpa, no de la falta de ejercicio, no del exceso de grasas. Por supuesto que el deporte es sano, y no hay que caer en dietas extremistas, pero va siendo hora de poner las cosas en su sitio y señalar a los verdaderos culpables. ¿Los ciudadanos? No, las mentiras interesadas que nos han contado.