Sunday, November 9, 2014

Creating Boundaries for our physics world

In the game i am making, i want my player to be inside a cage or locked room. To achieve this is pretty easy using physics:


auto edgeBody = PhysicsBody::createEdgeBox(visibleSize, PHYSICSBODY_MATERIAL_DEFAULT, METER_TO_PIXEL(1));
auto edgeNode = Node::create();
edgeNode->setPosition(Point(visibleSize.width/2,visibleSize.height/2));
edgeNode->setPhysicsBody(edgeBody);
this->addChild(edgeNode);

We need to create a node because bodies need to be attached to something. We could do the same with a sprite that was going to act as a border or could just leave it like that and put the art on top of it as it is not going to be movable.

I am using a couple macros:

#define PTM_RATIO 32

#define METER_TO_PIXEL(n) ((n) * PTM_RATIO) //macro to convert meters to pixels.

#define PIXEL_TO_METER(n) ((n) / PTM_RATIO) //macro to convert pixels to meters.

PTM_RATIO defines how many pixels i have per meter and the other two allow me to switch between them, so i can basically work in meters in my game.
My PTM_RATIO is 32 and my DesignResolution is 480x320, so i have an area of 15mx10m.

In this example i am creating the edge at the end of the visible size, but i am using a thickness of 1 meter (in every direction), so as a result i have a border around my screen of 1 meter (+1 more meter in the non visible area). I doubt he can escape now :P

Using Physics in cocos2dx

In my last entry i tried to explain how to setup a world and a body in cocos2dx. This time i will explain how to replace that with the built in physics in cocos2dx, which is based on chipmunk.

You can follow this tutorial in the documentation.

The concepts are bassically the same, we need a body, attach it to a world and to a sprite and then update the sprite to match our physics. The main advantage of using this engine instead of Box2D is that most of that work is taken care of by the cocos2dx.

In create method you will need:


Scene* GameScene::createScene()
{
    // 'scene' is an autorelease object
 auto scene = Scene::createWithPhysics();
 scene->getPhysicsWorld()->setDebugDrawMask(PhysicsWorld::DEBUGDRAW_ALL);
    
    // 'layer' is an autorelease object
    auto layer = GameScreen::create();
    layer->setPhyWorld(scene->getPhysicsWorld());

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}
The main difference is that you are creating the scene with createWithPhysics().

Your .h should look like this:


using namespace cocos2d;
class GameScene : public cocos2d::Layer
{
public:
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();
    
    // implement the "static create()" method manually
    CREATE_FUNC(GameScreen);

 void goToPauseScene(Ref *pSender);
 void goToGameOverScene(Ref *pSender);
 void update(float dt);

 void setPhyWorld(PhysicsWorld *world){m_world=world;}

private:
 PhysicsWorld *m_world;
};

Don't forget to add your using namespace, or it may start causing some conflict and won't run.
And to end replacing Box2D, our init method will be:


bool GameScene::init(){
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

 auto player = Sprite::create("player.png");
 auto body = PhysicsBody::createCircle(player->getContentSize().width / 2);
 player->setPhysicsBody(body);
 player->setPosition(Point(visibleSize.width/2, visibleSize.height/2));

 this->addChild(player);

    return true;
}

And that's it. As you can see, if you compare it with my past entry, we reduced 16 lines of code to 3, making it easier to understand and mantain.
We didn't even need to update our physics in the main loop or have 2 different scales, one for screen and one for world. The API handles all that for us.

This physics engine, as mentioned before, is based on chipmunk, so it does not have support for CCD (Continuous Collision Detection). There are some extra techniques you will have to use when you need to use bullets or something else that needs this.

As a good resource, there is a comunity driven documentation in github that seems to be pretty good. You can check the programmers guide here.

Friday, November 7, 2014

Adding physics to my game - Box2D

I wrote about Box2D before in previous post, but now i am going to go into detail and starting from scratch, writing about the key concepts and little details that help understand how things work.

For this entry i will work on creating a world, a body, a sprite and adding all that together to make a simple scene, where we see our sprite falling.

I will use PTM_RATIO, WORLD_TO_SCREEN and SCREEN_TO_WORLD macros, so make sure you read about them in my old post.

We start with our init method:


bool GameScene::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

 b2Vec2 gravity(0, -9.8f); //normal earth gravity, 9.8 m/s/s straight down!
 gameWorld = new b2World(gravity);//Set a new world with the given gravity

 auto player = Sprite::create("player.png");
 player->setPosition(Point(visibleSize.width/2, visibleSize.height/2));

 b2BodyDef playerBodyDef;
 playerBodyDef.type = b2_dynamicBody; //this will be a dynamic body
 playerBodyDef.position.Set(SCREEN_TO_WORLD(player->getPosition().x), SCREEN_TO_WORLD(player->getPosition().y)); //set the starting position
 playerBodyDef.angle = 0; //set the starting angle
 playerBodyDef.userData = player;

 b2Body *playerBody = gameWorld->CreateBody(&playerBodyDef);

 // Create shape definition and add to body

 //set each vertex of polygon in an array
 b2Vec2 vertices[5];
 vertices[0].Set(-1,  2);
 vertices[1].Set(-1,  0);
 vertices[2].Set( 0, -3);
 vertices[3].Set( 1,  0);
 vertices[4].Set( 1,  1);
  
 b2PolygonShape playerShape;
 playerShape.Set(vertices, 5); //pass array to the shape

 b2FixtureDef playerFixtureDef;

 playerFixtureDef.shape = &playerShape; //change the shape of the fixture
 playerFixtureDef.density = 10.0f;
        playerFixtureDef.friction = 0.4f;
        playerFixtureDef.restitution = 0.1f;

 b2Fixture *playerFixture = playerBody->CreateFixture(&playerFixtureDef);

 this->addChild(player);

 this->scheduleUpdate();
    return true;
}

For this to work properly, add a private member "gameWorld" to your GameScene.h (and #include "Box2D\Box2D.h").

For this exercise, all i do is create a world initialized with some value for gravity. You can change gravity variable to play with it and modify the physics output for your world.

To add bodies, you need to create some kind of template and then create an actual physics body with it. In the example the template is playerBodyDef. I add type (read Box2D docs on body types), position, angle and user data.
I Added a sprite, and then convert the position of my sprite into coordinates that my physics body can handle and position it there.
Remember to add userData, it is important to recognize the sprite attached to your body.

Once we are done setting the BodyDef, we create an actual body (playerBody).

The fixtures represent physical properties and just like bodies, we create a template first (playerFixtureDef) and then create an actual fixture, attached to a body. The fixture is the one that interacts with the physics world.

The last line in our method is this->scheduleUpdate(), which will start our game's main loop.

The void update(float dt) method can be overriden to go into the game's main loop, and we will, so add that to your GameScene.cpp.


void GameScene::update(float dt){
 int velocityIterations = 10;
 int positionIterations = 10;

 // Instruct the world to perform a single step of simulation. It is
 // generally best to keep the time step and iterations fixed.
 gameWorld->Step(dt, velocityIterations, positionIterations);

 bool blockFound = false;

 // Iterate over the bodies in the physics world
 for (b2Body* b = this->gameWorld->GetBodyList(); b; b = b->GetNext()){
  if(b->GetUserData() != NULL){
   Sprite *sprite = (Sprite*)b->GetUserData();
   sprite->setPosition(Point(b->GetPosition().x*PTM_RATIO, b->GetPosition().y*PTM_RATIO));
   sprite->setRotation(-1*CC_RADIANS_TO_DEGREES(b->GetAngle()));
  }
 }

}

velocityIterations and positionIterations are values that are used in the physics calculation, and the higher they are the more accurate our physics will be, but the longer it will take to calculate them.

The gameWorld->Step() is telling the world to update physics.

We need to manually update our sprite according to physics world, so we search in every body in the physics world for sprites (remember i told you to add userData in body?) and if some body has a sprite, we update it's position. Remember we need to convert from meters to pixels, and that's what PTM_RATIO is for.

With this you should be able to set up a body in a scene and see how it falls into it's death (not really). You can simulate several things, but without anything else to interact it is kinda plain (feel free to add more bodies and watch them collide). I will go into adding more things to the scene in a future entry.

Thursday, November 6, 2014

Creating a basic main menu for our game

When you create a new project you get a couple files named HelloWorldScene.cpp and HelloWorldScene.h.

Rename them and refactor them to look like this:

MainMenuScene.h:


#ifndef __MAINMENU_SCENE_H__
#define __MAINMENU_SCENE_H__

#include "cocos2d.h"

class MainMenu : public cocos2d::Layer
{
public:
    // there's no 'id' in cpp, so we recommend returning the class instance pointer
    static cocos2d::Scene* createScene();

    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
    virtual bool init();
    
    // implement the "static create()" method manually
    CREATE_FUNC(MainMenu);

 void goToGameScene(Ref *pSender);
};

#endif // __MAINMENU_SCENE_H__


MainMenuScene.cpp:


#include "MainMenuScene.h"

USING_NS_CC;

Scene* MainMenu::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = MainMenu::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool MainMenu::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
 auto *title = Label::createWithBMFont("myfont.fnt", "My Awesome Title", TextHAlignment::CENTER);
 auto menuTitle = MenuItemLabel::create(title);
 auto playItem = MenuItemImage::create("play.png", "play.png");
 auto menu = Menu::create(menuTitle, playItem, NULL);
 menu->alignItemsVerticallyWithPadding(visibleSize.height / 4);
 this->addChild(menu);

 auto background = Sprite::create("mainmenu-background.png");
 background->setPosition(Point(visibleSize.width/2, visibleSize.height/2));
 
 CCLOG("Characters: %f", background->getBoundingBox().size.width);
 CCLOG("Characters: %f", this->getBoundingBox().size.width);
 this->addChild(background, -1);
    
    return true;
}

What i did is pretty simple, i just created two menu items and added them to a menu. After that i loaded a background and positioned it in my scene.

The first menu item is created with Label::createWithBMFont, which takes a .fnt file and uses it to draw some text.
I created my files with BitMap Font Generator. All you need to do is tell it which font to use (you can download a ttf font from internet and load it too). Select the chars you want to load (that bar with checkboxes at the right side).
To make it work with cocos2dx you need it to generate only one page and to output a .png as texture. You can set both of those in export options (you need to play around with output sizes to find one that fits your desired chars and font size).
Once you are done with that, go to "Save bitmap as..." and place into a valid path.

The other menu item was easier...just load a sprite.

Once i have my items i create a menu with a padding of 1/4 of the height. If you have more items you may need to make this value lower.

When we are in the scene, we can reference it with "this", which i do to add the menu as a child.

Now i wanted to add a background, so i loaded a sprite. As the sprites have an anchor point at the middle of them and by default they are put at 0,0 in the parent, you need to move it to the center of the screen with setPosition(Point(visibleSize.width/2, visibleSize.height/2)). If you use visibleSize you can position the childs based on % instead of px. If you want to use px, make sure you use your designSizeResolution, as everything is based on it.

In this case my background was 480x320, the same size as my design resolution, so i don't need to resize it to fit anything that fits my "low-res" setting.

With that you should have a basic scene with a menu that contains a title and a play button. You even have a background now.

If you want to link your scene to another one, you can add something like to play item:
auto playItem = MenuItemImage::create("play.png", "play.png", CC_CALLBACK_1(MainMenu::goToGameScene, this));
and later in MainMenu.cpp:

void MainMenu::goToGameScene(Ref *pSender)
{
 auto scene = GameActionScene::createScene();
 
 Director::getInstance()->replaceScene(scene);
}
Don't forget to create another scene with the class GameActionScene (you can use HelloWorldScene as a template again).

Monday, November 3, 2014

Multi Resolution Support in Cocos2dx - Windows

For this entry i will explain the best way i found to work with multiple resolutions in Cocos2dx.

The page on the documentation gives you a code snipet:


 typedef struct tagResource
    {
        cocos2d::CCSize size;
        char directory[100];
    }Resource;

    static Resource smallResource  =  { cocos2d::CCSizeMake(480, 320),   "iphone" };
    static Resource mediumResource =  { cocos2d::CCSizeMake(1024, 768),  "ipad"   };
    static Resource largeResource  =  { cocos2d::CCSizeMake(2048, 1536), "ipadhd" };
    static cocos2d::CCSize designResolutionSize = cocos2d::CCSizeMake(480, 320);

    bool AppDelegate::applicationDidFinishLaunching() {
        // initialize director
        CCDirector* pDirector = CCDirector::sharedDirector();
        CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();

        pDirector->setOpenGLView(pEGLView);

        // Set the design resolution
        pEGLView->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::SHOW_ALL);

        CCSize frameSize = pEGLView->getFrameSize();

        std::vector<std::string> searchPath;

        // In this demo, we select resource according to the frame's height.
        // If the resource size is different from design resolution size, you need to set contentScaleFactor.
        // We use the ratio of resource's height to the height of design resolution,
        // this can make sure that the resource's height could fit for the height of design resolution.

        // if the frame's height is larger than the height of medium resource size, select large resource.
        if (frameSize.height > mediumResource.size.height)
        { 
            searchPath.push_back(largeResource.directory);
            pDirector->setContentScaleFactor(largeResource.size.height/designResolutionSize.height);
        }
        // if the frame's height is larger than the height of small resource size, select medium resource.
        else if (frameSize.height > smallResource.size.height)
        { 
            searchPath.push_back(mediumResource.directory);
            pDirector->setContentScaleFactor(mediumResource.size.height/designResolutionSize.height);
        }
        // if the frame's height is smaller than the height of medium resource size, select small resource.
        else
        { 
            searchPath.push_back(smallResource.directory);
            pDirector->setContentScaleFactor(smallResource.size.height/designResolutionSize.height);
        }
        ...................
        ...................
        auto fileUtils = FileUtils::getInstance();
 fileUtils->setSearchPaths(searchPath);
    }
You probably can tell already what the code is trying to do:

  1. Define a serie of resolutions for which you will design resources (low res, hd, etc).
  2. Set a DesignResolution
  3. Select a set of resources based on the frame size of the current device
  4. Send your director a content scale factor so it can know how to resize everything.
  5. Set your directory search path according to your resource selection.
The design resolution is the resolution for which you are designing your game natively and it is very important because everything will be based on it.

But there is another issue other than resolutions: the aspect ratio. There are a ton of device screen sizes and aspect ratios(4:3, 3:2, 16:10, etc). To solve this you have several options (i will be brief on this because on the documentation explain it in detail):
  • Exact fit: App will fill the screen, forgetting about the aspect ratio and stretching or compressing as needed (There can be a noticeable distorsion on your shapes)
  • No border: Your app will fill the screen but it will keep it's aspect ratio. It can crop at 2 of the 4 borders if the aspect ratio of the device is different than the aspect ratio of the design resolution.
  • Show all: This is my favorite. The app will be completely visible in the screen and will keep the same aspect ratio as design resolution. This can cause that 2 of the 4 borders have black borders. There is an easy workaround for this that i will explain in short.
  • Fixed height/Fixed width: As the name implies, it will keep either the height or the width constant and scale the other.
I found a very good explanation in here on using show all policy and fix your black borders (it is for another engine and uses another terms but you should read it, it has very good information).
If you know you can get black borders when you resize, design your backgrounds to be bigger than your current screen.
The recommendation is to work with an aspect ratio of 3:2. If you scale to 4:3 you will have your borders at the top/bottom and if you scale to 16:9 they will be left/right . Those will be your worst case scenarios and you will design your background image to fit it.
Knowing that all you have to do is calculate how much bigger your background will have to be in each direction. The calculations are simple, just do the following:

  • For X axis you have that your worst case is 16:9, that being an aspect ratio can be reduced to a fraction: 16/9 = 1.777777. Having that 3:2 = 3/2 = 1.5 we can conclude that 16:9 is 1.185185 times bigger than 3:2 ( 1.777777/1.5 ). What this implies is that for your supported resolution (ie. 480x320) your X axis will have to be 1.185185 times larger. This is something as simple as 480*1.185185=568.88 (round it to 570px). You will need a background with an X axis of 570px in the worst case, so create your regular 480px background and add it some pretty borders or something to cover the missing 90px (45px in each side). 
  • For the Y axis the procedure is exactly the same but with an aspect ratio of 4:3. In this case 4:3 aspect ratio is 1.125 times bigger than 3:2 (apply the same reasoning as X axis) so you will need to make your Y axis at least 320*1.125=360px
Try to round your calculations to the next higher pair number, so you can add the same number of pixels at both sides and avoid errors on your centering.

Repeat this for every resolution you are going to support (For obvious reasons it is better to start at a high resolution and then scale down because the distortion is not noticeable, but i am doing it this way for the sake of clarity).

If we used 16:9 instead of 3:2 as our design aspect ratio, we would have a worst case of 1.333 times when converting to 4:3, which is worse than using an intermediate aspect ratio as 3:2, so we would have more wasted space to fill.

If you have doubts about this you can check the linked explanation (again, very good and with pictures!!) or leave a comment.

We covered multisupport resolution in cocos2dx and the way to overcome the drawback of a good resolution policy for scaling our app into different aspect ratios.

All that is left is to explain how to test this in windows.

In windows, cocos2dx uses main.cpp to set it's entry point. You can create a frame size there with the desired resolution and the app will behave as it is running in a device with that resolution. You can even scale it to show bigger on your screen to avoid missing details because it is too little!!!

In your main.cpp (project/win32):

#include "main.h"
#include "AppDelegate.h"
#include "cocos2d.h"

USING_NS_CC;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // create the application instance
    AppDelegate app;
    auto director = Director::getInstance();
    director->setDisplayStats(true);
    auto glview = GLViewImpl::createWithRect("MyApp", Rect(0, 0, 480, 320), 2.0f);
    director->setOpenGLView(glview);

    return Application::getInstance()->run();
}



We create a GLView and set it our resolution and our zoom (the zoom won't change the resolution, just scale it in your window). Then just leave your appdelegate as we saw at the beggining and it should work. You can start making an app that will scale with no major drawbacks in any resolution and aspect ratio!!!

Sunday, April 27, 2014

Add sound to my Cocos2dx game

At some point you will want to add some awesome sounds to your game. The way to do it is very simple but before explaining it let's check a couple concepts:

Background Music: The background music (or BGM for short) are the sounds that are played in the background. Normally this music is played at low volume to keep it out of the focus of the player and avoid obscuring sounds that are more important. BGM is normally long and there is only one at a time (Except maybe at some points where you are going to a new level and there is a fade in and fade out).

Effects: This kind of sound is short and normally represents a game event (Walking, running, jumping, shooting, etc). There are tons of effects in a game and they are played constantly.

Depending on the platform you are going to run your game the format of the sounds should be different according to documentation.

The module of Cocos2dx that handles music is called CocosDenshion. To use it you must include a .h file in your game:

#include "SimpleAudioEngine.h"

You can use CocosDenshion namespace too:

using namespace CocosDenshion;

To use different formats depending on what platform you are in, you can use this comparison:

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    #define EFFECT_FILE        "effect2.ogg"
#elif( CC_TARGET_PLATFORM == CC_PLATFORM_MARMALADE)
    #define EFFECT_FILE        "effect1.raw"
#else
    #define EFFECT_FILE        "effect1.wav"
#endif // CC_PLATFOR_ANDROID

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
    #define MUSIC_FILE        "music.mid"
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    #define MUSIC_FILE        "background.wav"
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_BLACKBERRY || CC_TARGET_PLATFORM == CC_PLATFORM_LINUX )
    #define MUSIC_FILE        "background.ogg"
#elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    #define MUSIC_FILE        "background.caf"
#else
    #define MUSIC_FILE        "background.mp3"
#endif // CC_PLATFOR_WIN32

I took this fragment of code from the test project on Cocos2dx download. As you can see depending on the platform the #define directive is different, pointing to a file with another format. The files in this case should be located at the Resources folder in your project. If you want to have a folder for sounds you can create it and then reference the files with the relative path from the Resources folder (Something like "sounds/myFile.mp3").

In your init() methods you should preload the sounds you are going to use later. You can do that like this:

SimpleAudioEngine::getInstance()->preloadBackgroundMusic( MUSIC_FILE );
SimpleAudioEngine::getInstance()->preloadEffect( EFFECT_FILE );

Now you can play the sounds wherever you want with:

SimpleAudioEngine::getInstance()->playBackgroundMusic(MUSIC_FILE, true);
SimpleAudioEngine::getInstance()->playEffect(EFFECT_FILE, false, pitch, pan, gain);

There are also functions to stop the playback, pause, resume, etc. To control volume use:

SimpleAudioEngine::getInstance()->setEffectsVolume(volume);
SimpleAudioEngine::getInstance()->setBackgroundMusicVolume(volume);

The volume value must be between 0 and 1.

As a last hint remember to use bitrates that are enough for what you want. Use values around 128kbps - 256kbps, not something like 2822kbps (Yeah, happened to me once and took me a while to notice). You can find more information on supported formats for specific platforms in their respective documentations. In the case of android you can check this.

This should be enough to get you started with the music and effects on Cocos2dx. Good luck and rock on!



Thursday, April 17, 2014

Shooting bullets using Box2D

I got to the point where i wanted to shoot a bullet from some position toward my main character (its centroid).

To do this you have to make a little set up in the body definition:

b2BodyDef bulletBodyDef;
bulletBodyDef.type=b2_dynamicBody;
bulletBodyDef.bullet = true;

Additionally to the regular settings you have to set "isBullet" to true. This will tell Box2D to calculate collision continuosly for that body to avoid tunneling (passing through other bodies when moving too fast).

To actually shoot the bullet just apply a linear impulse to it. To calculate the direction in my case just get the vector from bullet to target, normalize it and multiply it by the magnitude of the impulse you are applying.

auto force = 10.0f;
auto hyp = target - bullet->GetPosition();
hyp.Normalize();
bullet->ApplyLinearImpulse(b2Vec2(force*hyp.x, force*hyp.y), bullet->GetWorldCenter(), true);

Once you have this you can shoot as many bodies as you want and if you are lucky enough your main character will have tons of holes :)