Creeng

Saturday, 7 December 2013

cocos2d-x CCClippingNode (triple-C)

Hey blog readers,

Had some hard time getting the CCClippingNode work in our upcoming game and thought a quick info might be helpful and fun to share.

CCClippingNode is a wrapper to OpenGL stencil buffer. Brief description: CCClippingNode node defines, which area of the CCClippingNode:s sub nodes are drawn or not drawn (=draw only stencil area or if inverted clip the stencil area). And the code to achieve something as cool as this :)



1. CCClippingNode code
    CCSize s = CCDirector::sharedDirector()->getWinSize(); 
     
    // Create simple triangle from stencil buffer 
    CCDrawNode *stencil = CCDrawNode::create(); 
    static CCPoint triangle[3];
    triangle[0] = ccp(-100, -100); 
    triangle[1] = ccp(100, -100); 
    triangle[2] = ccp(0, 100);
     
    static ccColor4F mask = {0, 0, 0, 1};
    stencil->drawPolygon(triangle, 3, mask, 0, mask);
    stencil->setPosition(ccp(50, 50));
     
    // Create the clipping node with stencil buffer 
    CCClippingNode *clipper = CCClippingNode::create(); 
    clipper->setAnchorPoint(ccp(0.5, 0.5)); 
    clipper->setPosition(ccp(s.width / 2, s.height / 2));
    clipper->setInverted(false); 
    clipper->setStencil(stencil); 
    addChild(clipper); 

    // Add things to clipping node, these will be clipped by stencil buffer
    CCSprite* content = CCSprite::create("HelloWorld.png"); 
    content->setPosition( ccp(50, 50) );
    clipper->addChild(content); 

    CCLabelTTF* tf = CCLabelTTF::create("Testing long text", "Thonburi", 35); 
    tf->setPosition(ccp(50, 50));
    clipper->addChild(tf); 

2. Remember to enable OpenGL stencil buffer, by changing depthFormat to GL_DEPTH24_STENCIL8_OES!!! On iOS modify the AppController.mm
EAGLView *__glView = [EAGLView viewWithFrame: [window bounds] 
                                         pixelFormat: kEAGLColorFormatRGBA8 
                                         depthFormat: GL_DEPTH24_STENCIL8_OES   // Enable stencil! 
                                         preserveBackbuffer: NO 
                                         sharegroup: nil 
                                         multiSampling: NO 
                                         numberOfSamples: 0];

Sunday, 29 September 2013

cocos2d-x and "scrolling numbers in label"

Here's a short post (after a long time) for implementing scrolling numbers for a label in cocos2d-x. Scrolling numbers in a label is a common requirement for a typical game. You see it everywhere, increasing coins, points... in huds, level complete scenes etc. I just implemented an action class for cocos2d-x and thought I could share it here.

The implementation is really simple, derive own action from CCActionInterval and implement the update() method. Here's the header, cpp and usage example. As it is implemented as action it can be used with CCLabelTTF and CCLabelBMFont (derived classes from CCLabelProtocol). Have fun!

Header:
class ScrollingNumberAction : public cocos2d::CCActionInterval
{
public:
    
    static ScrollingNumberAction* create(int from, int to, float duration);

    ScrollingNumberAction(int _from, int _to);
    virtual ~ScrollingNumberAction();
    
protected:
    
    virtual void update(float time);
    
    int from;
    int to;
};

Source:
ScrollingNumberAction* ScrollingNumberAction::create(int from, int to, float duration)
{
    ScrollingNumberAction *pAction = new ScrollingNumberAction(from, to);
    pAction->initWithDuration(duration);
    pAction->autorelease();
    return pAction;
}

ScrollingNumberAction:: ScrollingNumberAction(int _from, int _to)
: from(_from), to(_to)
{
}

ScrollingNumberAction::~ ScrollingNumberAction()
{
}

void ScrollingNumberAction::update(float time)
{
    if (m_pTarget) {
        CCLabelProtocol* label = dynamic_cast<CCLabelProtocol*>(m_pTarget);
        int increment = (to - from) * time;
        label->setString(IntToString(from + increment).c_str());
    }

Usage:
CCLabelTTF* label = CCLabelTTF::create();
NumberAction* action = NumberAction::create(from, to, 1.0f);
label->runAction(action);

Thursday, 17 January 2013

Cocos2D-x and Windows Phone 8

Hello blog, here is a quick post including few tips for running a Cocos2d-x game on actual Windows Phone 8 device. We have been porting Hockey MVP game in Creeng for WP8 and today we finally got our hands on a WP8 device to test our port. Here are some magic steps that needs to be taken care of.

Check this great blog post for how to do the actual port http://www.developer.nokia.com/Community/Wiki/Porting_Cocos2d-x_Games_for_Windows_Phone_8

  1. Remember to set the shader types for DirectX shaders in Assets/Textures folder (pixel or vertex). See picture at the end of the post.
  2. The sound effects doesn't work on device => set the volume explicitly when initializing effects: (CocosDenshion::SimpleAudioEngine::sharedEngine()->setEffectsVolume(1.0f)) - works on emulator without this
  3. The Cocos2d-x WP8 port in cocos2d-x page is missing some ARM libraries like libpng. You need these libs and you can find them from the github repo at https://github.com/cocos2d-x/cocos2dx-win8/tree/wp8/cocos2dx/platform/third_party/win8_metro/libraries/arm  - just copy them in
  4. If you are using the accelerometer, remember to swap the x- and y-axis and multiple them by -1.
  5. Run and enjoy the sweet taste of victory!
Note: The WP8 port has beta mark on it, but at least we got our game working.


Sunday, 12 August 2012

Glow effect using cocos2d-x

Cheers! Final day of Olympics... good day to write a short post.

Here is a sample code for how to create a nice and simple animated 2D glow effect for a sprite with cocos2d-x. No need for any fancy things, just render a glow sprite with blending under the sprite and animate that a little by scaling it up and down using CCScaleTo action. The particle-fire.png sprite from the cocos2d-x samples works pretty well for generating the effect.



Here's the function
void addGlowEffect(CCSprite* sprite, 

                   const ccColor3B& colour, const CCSize& size)

{

    CCPoint pos = ccp(sprite->getContentSize().width / 2, 

        sprite->getContentSize().height / 2);

    

    CCSprite* glowSprite = CCSprite::spriteWithSpriteFrameName("particle-fire.png");

    glowSprite->setColor(colour);

    glowSprite->setPosition(pos);

    glowSprite->setRotation(sprite->getRotation());

    

    _ccBlendFunc f = {GL_ONE, GL_ONE};

    glowSprite->setBlendFunc(f);

    sprite->addChild(glowSprite, -1);

    

    // Run some animation which scales a bit the glow

    CCSequence* s1 = CCSequence::actionOneTwo(

                     CCScaleTo::actionWithDuration(0.9f, size.width, size.height),

                     CCScaleTo::actionWithDuration(0.9f, size.width*0.75f, size.height*0.75f));

    

    CCRepeatForever* r = CCRepeatForever::actionWithAction(s1);

    glowSprite->runAction(r);

}

Use it like this
addGlowEffect(sprite, ccc3(255,255,255), CCSizeMake(2.5f, 2.5f));

Thursday, 12 July 2012

VRope for cocos2d-x

Time to blog again something after a while. I have been working big hours with our new start-up Creeng. Currently working with 2 game projects for iOS and Android, the first one coming up during the next month (hopefully). Anyway it has been great and yesterday I ported the vrope from cocos2d to cocos2d-x for our second game and released it in the github. Thought I would share it here too.

The box2d has a great joint, called b2RopeJoint, which connects two bodies with a rope. The VRope then draws the rope, which acts like a real rope using the verlet integration for calculating approximations for the points in the rope joint (for theory check wikipedia). Check the cocos2d blog for great tutorial

The ported code and simple user guide is in the github

The demo cocos2d-x code: here

Here's a demo video


Tuesday, 15 May 2012

Cocos2d-x experiences

Long time no blogging... I have been busy with setting up a new startup; happy times :) We are coming out of the closet very soon... Anyway I have been testing the Cocos2d-x open-source (MIT licence) game framework and it has been a positive surprise. It advertises itself as being a cross-platform version of the famous Cocos2d framework for iOS. So I thought I write some experiences about the framework encountered so far. No code this time...

The greatest thing about the Cocos2d-x is that it actually REALLY provides a cross-platform experience for mobile devices (and I mean really!). The whole framework is implemented in C++ and it has support for iOS, Android, Win32, Linux... It is actually a port of the Cocos2d iOS/Objective-C implementation and provides the same features as the original first-born. The APIs in some cases are pretty non C++, because it borrows the idioms from the Cocos2d Objective-C APIs, but who cares... You just need to know the twists :) I have been so far able to run it on Win32 with Visual Studio and on Android using NDK. I have also witnessed with my own eyes that it also works with iOS & xCode like a charm.

The best setup for developing game simultaneously for iOS and Android (cross-platform style) seems to be getting a Mac and using it to build on both iOS and Android. Well... I'm still a Windows user, so I'm using Win32 and Visual Studio for implementing the actual game and then just building and running it on Android using the NDK. The reason I'm not using directly the Android to actually implement the game is that the NDK debugging features sucks!

The Cocos2d-x uses native OS APIs through C++ wrappers to utilize e.g. sounds. It also automatically generates for each platform all the boilerplate code for creating the actual application with OpenGL view and the other necessary stuff. You just implement the actual game using C++ APIs. All JNI and other fun stuff in Android and other platforms are automatically generated.

So far this setup has been working quite well. Some problems that I can foresee is for example using the threads and synchronization from OS (as we all know C++ doesn't support these yet) => might blow up the code. But let's see what happens, so far I'm more than happy with cocox2d-x. And one more thing, now I'm able to implement a game to iOS without swimming in the Objective-C vomit ;)

Sunday, 22 April 2012

WebGL shaders

Hello and welcome back!

Finally had time to experiment with WebGL a bit. Implemented a nice effect on grass moving in the wind using WebGL and a fragment shader. I found the effect and the graphics from the excellent RayWenderlich iOS+Cocos2D tutorial (check it here). The effect is pretty cool, it bends the grass back and forward so that the top of the grass bends more than the root.



The implementation just loads the image and renders it as a 2D texture to the HTML5 canvas and applies the shaders. It uses a sin wave to calculate the movement/bending factor where the input is a uniform variable incremented from the javascript for each frame. Well you can find the theory from the tutorial. I had to made some minor modifications to the original fragment shader to get it working in WebGL.

Here is the fragment shader implementation.
<script id="2d-fragment-shader" type="x-shader/x-fragment">

    precision mediump float;

    varying vec2 v_texCoord;

    uniform sampler2D u_texture;

    uniform float u_time;



    const float speed = 2.0;

    const float bendFactor = 0.2;

    void main()

    {

        float height = 1.0 - v_texCoord.y;

        float offset = pow(height, 2.5);

        

        //  multiply by sin since it gives us nice bending

        offset *= (sin(u_time * speed) * bendFactor);

        gl_FragColor = texture2D(u_texture, fract(vec2(v_texCoord.x + offset, 

                                 v_texCoord.y)));

    }

</script>

And the javascript...
var totalTime = 0.0;



window.onload = function(){



    window.requestAnimFrame = (function(callback) {

        return window.requestAnimationFrame || window.webkitRequestAnimationFrame || 

        window.mozRequestAnimationFrame || window.oRequestAnimationFrame || 

        window.msRequestAnimationFrame ||

        function(callback) {

            window.setTimeout(callback, 1000 / 60);

        };

    })();



    var image = new Image();

    image.src = "grass.png";

    image.onload = function() {

        initShaders();

        display();

    }  



    // Get A WebGL context

    var canvas = document.getElementById("grasscanvas");

    var gl = canvas.getContext("experimental-webgl");

    

    var createShaderFromScript = function(scriptId) {

        var shaderSource = "";

        var shaderType;

        var shaderScript = document.getElementById(scriptId);

        shaderSource = shaderScript.text;



        if (shaderScript.type == "x-shader/x-vertex") {

            shaderType = gl.VERTEX_SHADER;

        } 

        else if (shaderScript.type == "x-shader/x-fragment") {

            shaderType = gl.FRAGMENT_SHADER;

        } 

        

        var shader = gl.createShader(shaderType);

        gl.shaderSource(shader, shaderSource);

        gl.compileShader(shader);

        return shader;

    };



    function rectVertex(x, y, width, height) 

    {

      var x1 = x;

      var x2 = x + width;

      var y1 = y;

      var y2 = y + height;

      gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([

         x1, y1,

         x2, y1,

         x1, y2,

         x1, y2,

         x2, y1,

         x2, y2]), gl.STATIC_DRAW);

    }

    

    function initShaders()

    {

        // setup GLSL program

        vertexShader = createShaderFromScript("2d-vertex-shader");

        fragmentShader = createShaderFromScript("2d-fragment-shader");

        

        program = gl.createProgram();

        gl.attachShader(program, vertexShader);

        gl.attachShader(program, fragmentShader);

        gl.linkProgram(program);

        gl.useProgram(program);    

 

        // Create a texture.

        var texture = gl.createTexture();

        gl.bindTexture(gl.TEXTURE_2D, texture);



        // Set the parameters so we can render any size image.

        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);

        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);

        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);



        // Upload the image into the texture.

        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);

    }

    

    function render()    

    {

        // look up where the vertex data needs to go.

        var texCoordLocation = gl.getAttribLocation(program, "a_texCoord");

        var positionLocation = gl.getAttribLocation(program, "a_position");

  

        // provide texture coordinates for the rectangle.

        var texCoordBuffer = gl.createBuffer();

        gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);

        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([

            0.0,  0.0,

            1.0,  0.0,

            0.0,  1.0,

            0.0,  1.0,

            1.0,  0.0,

            1.0,  1.0]), gl.STATIC_DRAW);

        gl.enableVertexAttribArray(texCoordLocation);

        gl.vertexAttribPointer(texCoordLocation, 2, gl.FLOAT, false, 0, 0);



        // Set uniform variables for shader

        var resolutionLocation = gl.getUniformLocation(program, "u_resolution");

        var timeLocation = gl.getUniformLocation(program, "u_time");

        gl.uniform2f(resolutionLocation, canvas.width, canvas.height);

        gl.uniform1f(timeLocation, totalTime);



        // Create a buffer for the position of the rectangle corners.

        var buffer = gl.createBuffer();

        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);

        gl.enableVertexAttribArray(positionLocation);

        gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);



        // Set a rectangle the same size as the image.

        rectVertex(0, 0, image.width, image.height);

        

        // Draw the rectangle.

        gl.drawArrays(gl.TRIANGLES, 0, 6);

    }

    

    function display()

    {

        totalTime += 0.01;

        render(image);

        requestAnimFrame(display);

    }

}