How to flip a card with cocos2d-x - cocos2d-x

I need to flip a card to see its back, then side and then front gradually with animation like this: https://www.youtube.com/watch?v=te0Je0y4zU0 . How I can do this with cocos2d-x? I have took a look on OrbitCamera and RotateBy in 3D tests. They are very close to the one I want to, the only problem is that when the sprite turns around I see not the back (as at should be another texture, but the same spite from back camera). I understand that I should use 2 sprites to get the effect, but how I should do that, I don't know. Should I position 2 sprite with different Z order? Please advice.

Try this:
float duration = 2.0f;
auto actionBy = RotateBy::create(duration / 2, Vec3(0, 90, 0));
backCard->runAction(RotateBy::create(0, Vec3(0, 90, 0)));
frontCard->runAction( Sequence::create(
actionBy,
CallFunc::create([backCard, actionBy](){backCard->runAction(actionBy);}),
nullptr)

you need to simply do this :
CCRotateTo *act1= CCRotateTo::create(0.5, -180);
CCRotateTo *act2= CCRotateTo::create(1.0, 180);
CCSequence* act3 = CCSequence::create(act1,act2,NULL);
sprite->runAction(act3);

you should view https://www.youtube.com/watch?v=Q4fJrMvZVhI
same with cocos2dx using javascript.
reveal: function (pIsFaceUp) {
let self = this;
let timeFlip =0.5;
let callFunc = cc.callFunc(function () {
self.cardBG.spriteFrame = pIsFaceUp ? self.texFrontBG : self.texBackBG;
self.point.node.active = pIsFaceUp;
self.suit.node.active = pIsFaceUp;
self.mainPic.node.active = pIsFaceUp;
if(!pIsFaceUp)
self.node.skewY=135;
else
self.node.skewY=45;
});
if(!pIsFaceUp)
{
self.node.skewY=0;
let action = cc.skewBy(timeFlip/2,0,45);
let action2 = cc.skewTo(timeFlip/2,0,180);
self.node.runAction(cc.sequence(action,callFunc,action2));
}else
{
self.node.skewY=180;
let action = cc.skewBy(timeFlip/2,0,-45);
let action2 = cc.skewTo(timeFlip/2,0,0);
self.node.runAction(cc.sequence(action,callFunc,action2));
}
},

Use 2 Sprites: spriteFront, spriteBack
init (show spriteBack at first):
scale spriteFront to (0, 1) (scale X to 0, while keeping scale Y to 1)
scale spriteBack to (1, 1)
flip animation:
scale spriteBack to (0, 1)
after the animation,
scale spriteFront to (1, 1)
float fDuration = 0.8f;
CCArray* pArray = CCArray::create();
pArray->addObject(CCScaleTo::create(fDuration/2, 0, 1));
pArray->addObject(CCTargetedAction::create(spriteFront, CCScaleTo::create(fDuration/2, 1)));
CCFiniteTimeAction* flipCardAnimation = CCSequence::create(pArray);
spriteBack->runAction(flipCardAnimation);

Related

Cocos2dx: Sprite3D won't render to texture

I use RenderTexture to render a layer with all its nodes to a texture then apply an OpenGL shader on that texture to create post-process effects. It works all fine except with Sprite3D and Billboard nodes. It has been asked on their forums a few times without any response. I wonder if anyone got this to work.
Here is an example:
Layer* gameLayer = Layer::create();
this->addChild(gameLayer, 0);
auto dir = Director::getInstance()->getWinSize();
Camera *camera = Camera::createPerspective(60, (GLfloat)dir.width / dir.height, 1, 1000);
camera->setPosition3D(Vec3(0, 100, 100));
camera->lookAt(Vec3(0, 0, 0), Vec3(0, 1, 0));
gameLayer->addChild(camera); //add camera to the scene
// You'll get a NULL camera inside BillBoard::calculateBillbaordTransform() function
// if you call visit()
/*auto billboard = BillBoard::create("cocos2d-x.png", BillBoard::Mode::VIEW_POINT_ORIENTED);
billboard->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(billboard, 100);*/
// This one won't render into the texture
Sprite3D* sprite3D = Sprite3D::create("blend_test/character_3_animations_test.c3b");
sprite3D->setScale(5.0f); //sets the object scale in float
sprite3D->setRotation3D(Vec3(0.0f, 0.0f, 0.0f));
//sprite3D->setPosition3D(Vec3(VisibleRect::center().x, VisibleRect::center().y, 0.0f)); //sets sprite position
sprite3D->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(sprite3D, 1); //adds sprite to scene, z-index: 1
// This one works just fine and appears black and white as expected
// in the resulting texture
Sprite* sprite2D = Sprite::create("cocos2d-x.png");
sprite2D->setPosition(Vec2(VisibleRect::center().x, VisibleRect::center().y));
gameLayer->addChild(sprite2D);
// Black and white OpenGL shader
GLProgram* glProgram = GLProgram::createWithFilenames("shaders/gray.vert", "shaders/gray.frag");
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_POSITION);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_COLOR);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD1, GLProgram::VERTEX_ATTRIB_TEX_COORD1);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD2, GLProgram::VERTEX_ATTRIB_TEX_COORD2);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD3, GLProgram::VERTEX_ATTRIB_TEX_COORD3);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_NORMAL, GLProgram::VERTEX_ATTRIB_NORMAL);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_BLEND_WEIGHT, GLProgram::VERTEX_ATTRIB_BLEND_WEIGHT);
glProgram->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_BLEND_INDEX, GLProgram::VERTEX_ATTRIB_BLEND_INDEX);
glProgram->link();
glProgram->updateUniforms();
RenderTexture* renderTexture = RenderTexture::create(VisibleRect::width(), VisibleRect::height());
renderTexture->retain();
Sprite* ppSprite = Sprite::createWithTexture(renderTexture->getSprite()->getTexture());
ppSprite->setTextureRect(Rect(0, 0, ppSprite->getTexture()->getContentSize().width,
ppSprite->getTexture()->getContentSize().height));
ppSprite->setAnchorPoint(Point::ZERO);
ppSprite->setPosition(Point::ZERO);
ppSprite->setFlippedY(true);
ppSprite->setGLProgram(glProgram);
this->addChild(ppSprite, 100);
renderTexture->beginWithClear(0.0f, 0.0f, 0.0f, 0.0f);
auto renderer = _director->getRenderer();
auto& parentTransform = _director->getMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
gameLayer->visit(renderer, parentTransform, true);
//gameLayer->visit();
renderTexture->end();
ppSprite->setTexture(renderTexture->getSprite()->getTexture());
Cocos2d-x v3.11.1 (current as of this post) and below don't properly support RenderTextures with Sprite3D because of a clear depth buffer bug.
There is a GitHub issue on the bug. But a workaround now exists:
...
sprite3D->setForce2DQueue(true); // puts your Sprite3D on same render queue as the RenderTexture. More info below.
...
auto rt = RenderTexture::create(1280, 720, Texture2D::PixelFormat::RGBA8888, GL_DEPTH24_STENCIL8); // By default a depth buffer isn't created
rt->setKeepMatrix(true); // required
...
...
rt->beginWithClear(0, 0, 0, 0, 1); // required, clears the depth buffer
Also, changes need to be made to RenderTexture.cpp. This fixes the clear depth buffer bug in Cocos2d-x.
void RenderTexture::onClear()
{
// save clear color
GLfloat oldClearColor[4] = {0.0f};
GLfloat oldDepthClearValue = 0.0f;
GLint oldStencilClearValue = 0;
GLboolean oldDepthWrite = GL_FALSE;
// backup and set
if (_clearFlags & GL_COLOR_BUFFER_BIT)
{
glGetFloatv(GL_COLOR_CLEAR_VALUE, oldClearColor);
glClearColor(_clearColor.r, _clearColor.g, _clearColor.b, _clearColor.a);
}
if (_clearFlags & GL_DEPTH_BUFFER_BIT)
{
glGetFloatv(GL_DEPTH_CLEAR_VALUE, &oldDepthClearValue);
glClearDepth(_clearDepth);
glGetBooleanv(GL_DEPTH_WRITEMASK, &oldDepthWrite);
glDepthMask(true);
}
if (_clearFlags & GL_STENCIL_BUFFER_BIT)
{
glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &oldStencilClearValue);
glClearStencil(_clearStencil);
}
// clear
glClear(_clearFlags);
// restore
if (_clearFlags & GL_COLOR_BUFFER_BIT)
{
glClearColor(oldClearColor[0], oldClearColor[1], oldClearColor[2], oldClearColor[3]);
}
if (_clearFlags & GL_DEPTH_BUFFER_BIT)
{
glClearDepth(oldDepthClearValue);
glDepthMask(oldDepthWrite);
}
if (_clearFlags & GL_STENCIL_BUFFER_BIT)
{
glClearStencil(oldStencilClearValue);
}
}
See the issue for more details. I also made an example gist of the workaround. Screenshot below.
I'm not sure about billboards, but this workaround might fix it too.
Info on Cocos2d-x render queues:
The Sprite3D needs to be on the same render queue as the RenderTexture. Cocos2d-x (as of v3.7 or so) now has 5 render queues:
Global Z Order < 0
3D Opaque
3D Transparent
Global Z Order == 0 (default for 2D)
Global Z Order > 0
You can put the Sprite3D and the RenderTexture on the last queue with setGlobalZOrder(1) or just put the Sprite3D in the 2D queue with sprite3D->setForce2DQueue(true).
unlike cocos2d RenderTexture the following worked fine for 3D screen capture (or anything i imagine)!
Sprite * CcGlobal::getScreenAsSprite(void) {
Size screenSize = Director::getInstance()->getWinSize();
int width = screenSize.width;
int height = screenSize.height;
std::shared_ptr<GLubyte> buffer(new GLubyte[width * height * 4], [](GLubyte* p) { CC_SAFE_DELETE_ARRAY(p); });
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer.get());
Image* image = new (std::nothrow) Image;
image->initWithRawData(buffer.get(), width * height * 4, width, height, 8);
Texture2D *texture = new (std::nothrow) Texture2D();
texture->initWithImage(image);
SpriteFrame *spriteFrame = SpriteFrame::createWithTexture(texture, Rect(Vec2(0, 0), screenSize));
Sprite *sprite = Sprite::createWithSpriteFrame(spriteFrame);
sprite->setFlippedY(true);
delete image;
return sprite;
}
===================================================

rotating sprite on touch libgdx

I am trying to rotate my sprite when i push to go left. right now my character is idling and running to the right. but im having trouble rotating to the left.
here is my chunk of code. if anyone could help me, that would be awesome.
public void draw(SpriteBatch spriteBatch) {
stateTime += Gdx.graphics.getDeltaTime();
//continue to keep looping
if(Gdx.input.isTouched()){
int xTouch = Gdx.input.getX();
int yTouch = Gdx.input.getY();
//System.out.println(Gdx.graphics.getWidth()/4);
//go left
if(xTouch < (width/4) && yTouch > height - (height/6)){
currentRunFrame = runAnimation.getKeyFrame(stateTime, true);
spriteBatch.draw(currentRunFrame, runSprite.getX() - 32, runSprite.getY() + 150, 128, 128);
RealGame.leftButton = new Texture(Gdx.files.internal("leftButtonOver.png"));
moveLeft();
}
if(xTouch > (width/4) && xTouch < (width/4)*2 && yTouch > height - (height/6)){
currentRunFrame = runAnimation.getKeyFrame(stateTime, true);
spriteBatch.draw(currentRunFrame, runSprite.getX() - 32, runSprite.getY() + 150, 128, 128);
RealGame.rightButton = new Texture(Gdx.files.internal("rightButtonOver.png"));
moveRight();
}
if(xTouch > (width/4) * 2 && xTouch < (width/4) * 3 && yTouch > height - (height/6)){
RealGame.shootButton = new Texture(Gdx.files.internal("shootButtonOver.png"));
}
if(xTouch > (width/4) * 3 && xTouch < (width/4) * 4 && yTouch > height - (height/6)){
RealGame.jumpButton = new Texture(Gdx.files.internal("jumpButtonOver.png"));
}
}
if(!Gdx.input.isTouched()){
currentIdleFrame = idleAnimation.getKeyFrame(stateTime, true);
spriteBatch.draw(currentIdleFrame, idleSprite.getX() - 32, idleSprite.getY() + 150, 128, 128);
RealGame.leftButton = new Texture(Gdx.files.internal("leftButton.png"));
RealGame.rightButton = new Texture(Gdx.files.internal("rightButton.png"));
RealGame.shootButton = new Texture(Gdx.files.internal("shootButton.png"));
RealGame.jumpButton = new Texture(Gdx.files.internal("jumpButton.png"));
moveStop();
}
}
thank you in advance, and let me know if you need more info.
I assume your currentIdleFrame is a Texture or TextureRegion, not a Sprite. One of SpriteBatchs draw methode with Texture supports a flipX and flipY. Using this you can flip him, for example if you are walking to the left, but your Texture is facing to the right. Also this supports rotation, which should be the rotation in degrees.
Verry important note: You create new Texture every render loop. Don't do this. Instead load all your Textures in a Texture[] frames and draw the right one, depending on stateTime. Also take a look at Animations class, which will help you with this.
Hope i could help
Use a SpriteBatch draw method that takes boolean flipX parameter or call flip on the Sprite.
Oh, and if this is your main loop, stop loading the textures like you are. Load them in the beginning and just swap them as needed.

HTML5: Inverse text-color on canvas

I want to draw text on a canvas in the inverse color of the background (to make sure the text is readible no matter the background color). I believe in oldskool bitblt-ing, this was an XOR operation.
How to do this?
Update: most of the newer browsers now support the blending mode "difference" which can achieve the same result.
context.globalCompositeOperation = "difference";
Updated demo.
Old answer:
One should think that the XOR mode for composition would do this, but unfortunately canvas' XOR only XORs the alpha bits.
By applying the following code we can however receive a result such as this:
You can make an extension to the canvas like this:
CanvasRenderingContext2D.prototype.fillInversedText =
function(txt, x, y) {
//code - see below
}
Now you can call it on the context as the normal fillText, but with a slight change:
ctx.fillInversedText(txt, x, y);
For this to work we do the following first - measure text. Currently we can only calculate width of text and then assume the height. This may or may not work well as fonts can be very tall and so forth. Luckily this will change in the future, but for now:
var tw = this.measureText(txt).width;
var th = parseInt(ctx.font, '10');
th = (th === 0) ? 10 : th; //assume default if no font and size is set
Next thing we need to do is to setup an off-screen canvas to draw the text we want ot invert:
var co = document.createElement('canvas');
co.width = tw;
co.height = th;
Then draw the actual text. Color does not matter as we are only interested in the alpha channel for this canvas:
var octx = co.getContext('2d');
octx.font = this.font;
octx.textBaseline = 'top';
octx.fillText(txt, 0, 0);
Then we extract the pixel buffers for the area we want to draw the inverted text as well as all the pixels for the off-screen canvas which now contains our text:
var ddata = this.getImageData(x, y, tw, th);
var sdata = octx.getImageData(0, 0, tw, th);
var dd = ddata.data; //cache for increased speed
var ds = sdata.data;
var len = ds.length;
And then we invert each pixel where alpha channel for pixel is greater than 0.
for (var i = 0; i < len; i += 4) {
if (ds[i + 3] > 0) {
dd[i] = 255 - dd[i];
dd[i + 1] = 255 - dd[i + 1];
dd[i + 2] = 255 - dd[i + 2];
}
}
Finally put back the inverted image:
this.putImageData(ddata, x, y);
This may seem as a lot of operations, but it goes pretty fast.
Demo (warning if you are sensitive to flicker)
(the psychedelic background is just to have some variations as fiddle needs external images and most are prevented by CORS when we use pixel manipulation).
I've removed my old answer, as it did not solve the question. As of recently, there are new globalCompositeOperations that do all kinds of great things. I've created an example that shows how to obtain inverted text. In case that link breaks, the method is essentially this:
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "white";
//draw inverted things here
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation

as3 wipe fade alpha using tween

Is it possible to fade the alpha with a soft wipe transition using as3 tween?
I thought maybe http://www.greensock.com might have the answer but I have found nothing. I would like the image to slowly fade away from one side to the other. A soft dissolve.
I thought maybe its possible using a mask but I don't think masks accept alphas otherwise it could be done that way.
Actually masks allow alphas. It's kind of a hack. You should try writing this in code:
maskMC.cacheAsBitmap = true;
objMC.cacheAsBitmap = true;
objMC.mask = maskMC;
Where objMC is your animated MovieClip and maskMC is your Mask that contains a gradient shape with transparency. See an example here: Link
You can also achieve this effect using Greensock. Code would look like this:
TweenLite.to(objMC, 1, {"alpha":0, "x":objMC.x + 10});
When using TweenLite, you need to provide object to animate, duration of animation and an instance of an Object class (that's stuff we write between curly braces). This instance contains all the values we want to change gradually.
You can accomplish this by using the ALPHA blendmode.
Make a shape that has a gradient whose alpha goes from full to nothing, then make it's blend mode ALPHA, put it in the same container as your item you wish to mask.
Then set the container blendmode to LAYER
Psuedo Code:
container.blendMode = BlendMode.LAYER; //container holds both the mask and the thing you want masked
maskObj.blendMode = BlendMode.ALPHA;
drawMaskGradients();
Here is a function I've used in the past to create said mask via code: (itemContainer is the object I'm masking)
You could however do this all in the Flash IDE using design tools.
softMaskSprite = new Sprite();
this.blendMode = BlendMode.LAYER;
softMaskSprite.blendMode = BlendMode.ALPHA;
softMaskSprite.mouseChildren = false;
softMaskSprite.mouseEnabled = false;
this.addChildAt(softMaskSprite,this.getChildIndex(itemContainer)+1);
//Create Sides
var top:Shape = new Shape();
var matr:Matrix = new Matrix();
matr.createGradientBox(areaWidth + (softMaskWidth * 2), softMaskWidth, 90 * (Math.PI / 180), 0, 0);
top.graphics.beginGradientFill(GradientType.LINEAR, [0xFF0000, 0x0000FF], [0,1], [0,255], matr, SpreadMethod.PAD);
top.graphics.drawRect(0, 0, areaWidth + (softMaskWidth * 2), softMaskWidth);
top.graphics.endFill();
top.x = softMaskWidth * -1;
top.y = softMaskWidth * -1;
softMaskSprite.addChild(top);
//BOTTOM
var bottom:Shape = new Shape();
matr = new Matrix();
matr.createGradientBox(areaWidth + (softMaskWidth * 2), softMaskWidth, 90 * (Math.PI / 180), 0, 0);
bottom.graphics.beginGradientFill(GradientType.LINEAR, [0xFF0000, 0x0000FF], [1,0], [0,255], matr, SpreadMethod.PAD);
bottom.graphics.drawRect(0, 0, areaWidth + (softMaskWidth * 2), softMaskWidth);
bottom.graphics.endFill();
bottom.y = areaHeight;
bottom.x = softMaskWidth * -1;
softMaskSprite.addChild(bottom);

how to turn an existing color image into a black-white image?

What diffrent approaches exists out there in actionscript 3.0 ?
I found this library: mojocolors
But this is more for graphics-color I guess…
Thx
var n:Number = 1/3;
var matrix:Array = [n,n,n,0,0,
n,n,n,0,0,
n,n,n,0,0,
0,0,0,1,0];
var cmf:ColorMatrixFilter = new ColorMatrixFilter(matrix);
bitmap.filters = [cmf];
In general, assuming that you start with an RGB image, you convert it to HSI color space and use the I (intensity) component.
To convert an image to grayscale, you need to iterate over each pixel in the image buffer and average the R, G and B component into one entity, then duplicate it three times to get the new color. pseudo-code (assuming 8bit color):
for each pixel in buffer:
pixel.rgb = ((pixel.red + pixel.green + pixel.blue) / 3) * 0x010101;
I am sure you can do something with Adobe's PixelBender to achieve this faster.
Apply the filter created by this method to the DisplayObject that contains the image you want in black and white:
public static function createBlackAndWhiteFilter():ColorMatrixFilter {
var rLum:Number = 0.2225;
var gLum:Number = 0.7169;
var bLum:Number = 0.0606;
var bwMatrix:Array = [rLum, gLum, bLum, 0, 0, rLum, gLum, bLum, 0, 0, rLum, gLum, bLum, 0, 0, 0, 0, 0, 1, 0];
return new ColorMatrixFilter(bwMatrix);
}//createBlackAndWhiteFilter