BUG: CCAnimation from sprite sheet wiggles if rotation and/or trim are used ON DEVICE ONLY

I got a comment from a QA tester about an animation wiggling a bit when it shouldn’t, and when I investigated I found that it was in fact shifting slightly, but only when run on a iOS device; in the iOS simulator the animation looks fine. I’ve attached a sample project build in Cocos2d-x 2.2 (using the project-creator tool) to illustrate the issue. There are 4 sprite sheets included for the different combinations of rotation/trim, all made with TexturePacker version 3.1.2. To tell if a animation is wiggling when it shouldn’t look at the character’s shoes as compared to the background. When run on the simulator, the shoes never move regardless of the sprite sheet used. Conversely, when run on device all sprite sheets but “noRnoT” (no rotation, no trim) wiggle a little. It seems to correspond to the height of the visual pixels, but that doesn’t really explain why “RnoT” sprite sheet also wiggles.

I verified the issue on an iPad 1 and iPhone 5. Someone please verify that they are seeing this bug too.

NOTE: I tried to post the project (only 3.6 MB) but kept getting “413 Request Entity Too Large”, I will try again later.

I’ve tried uploading the project in parts, and still get the error. Something is wrong with the forum.

Project only.

Resource folder.

Additional sprite sheets.

Hey Justin,

I’m seeing the same issue, except using cocos2d-iphone and Texture Packer 3.2. For me it’s only noticeable on a Retina Display iPad (iPad 3 in this case), and only on device (there’s absolutely no wiggle on the simulator). The character’s frame appears to shift slightly during some of the animations in the same way you described. I’ve checked the original PNG frames that go into the sprite sheet, and there’s no such wiggle in the source graphics.

I tried various Texture Packer settings, including disabling trim and rotation as you suggested. Interestingly enough, I noticed that each time the Texture Packer sheet was altered and republished, the wiggles did change slightly but they still occurred no matter what settings I tried.

Then I resorted to logging all my animation frame sizes and positions, and everything looks OK there too - there’s no sub-pixel rendering or other issues that I can see. Next thing I’m going to try is animating the frames by loading them directly as individual PNGs and not using a texture atlas to see if the problem still occurs.

Did you figure anything out on your end?

To eliminate TexturePacker as a factor, I just replaced one of the problem animations (that was wiggling) that was previously referencing frames in a texture atlas with one that uses CCSpriteFrames created directly from the PNG source frames. It still uses a CCAnimate action. Without the texture atlas, there’s no wiggle. The other animations that still use the texture atlas, do wiggle.

My guess is there’s something wrong in the atlas settings. What values are you using for padding / spacing etc.?

Border and shape padding are both 4 (@4x), Inner padding 0, Scale 1, common divisor 1x1.

But I don’t think it’s how the sprite sheet / texture atlas is made because we have both seen that on the simulator there is no wiggle. On top of that, I just tested on an Android device (Nexus 7) and there is no wiggle either, so it really seems that the issue is with how an actual iOS device regenerates those frames.

Thanks for the reply, I agree with your assessment that it may not be the atlas itself causing the issue. Found some relevant Cocos2D issues online that are discussing the same problem:

https://code.google.com/p/cocos2d-iphone/issues/detail?id=1345
https://code.google.com/p/cocos2d-iphone/issues/detail?id=1378

Sounds like people found a workaround that involved tweaking a shader, but it has a detrimental affect on performance. Let me know if you make any more progress.

Good to know. As for not wiggling on Android, apparently it is device specific, as it is wiggling on the Amazon Kindle HD 7, which implies to me that it is a deeper issue of how each device implements… OpenGL? That might explain why on the other forums you mentioned that someone could not find a difference between the iOS and Mac code yet the wiggle is on one and not the other.

This doesn’t really get us programmers to a solution though. :frowning:

So the issue we are both seeing is due to a floating point rounding error caused by the use of low-precision floating point numbers in the vertex and fragment shader that CCSprites use by default. The solution I’ve applied is to make a custom variant of the original shader that uses higher precision, and then apply it judiciously to only the sprites that need it / exhibit a problem (the reason being that there is a performance hit that scales with the size of the sprite, and you wouldn’t want to use the more expensive shaders on all sprites). To override a CCSprite’s shader, you could either create a CCSprite subclass, or simply set the shaderProgram property for a one-off change. As for the actual shaders you’ll need, they are the fragment shader:

"                                           \n\
#ifdef GL_ES                                \n\
precision highp float;                      \n\
#endif                                      \n\
                                            \n\
varying vec4 v_fragmentColor;               \n\
varying vec2 v_texCoord;                    \n\
uniform sampler2D CC_Texture0;              \n\
                                            \n\
void main()                                 \n\
{                                           \n\
    gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);            \n\
}                                           \n\
";

and then the vertex shader:

"                                                   \n\
attribute vec4 a_position;                          \n\
attribute vec2 a_texCoord;                          \n\
attribute vec4 a_color;                             \n\
                                                    \n\
#ifdef GL_ES                                        \n\
varying lowp vec4 v_fragmentColor;                  \n\
varying highp vec2 v_texCoord;                  \n\
#else                                               \n\
varying vec4 v_fragmentColor;                       \n\
varying vec2 v_texCoord;                            \n\
#endif                                              \n\
                                                    \n\
void main()                                         \n\
{                                                   \n\
    gl_Position = CC_MVPMatrix * a_position;        \n\
    v_fragmentColor = a_color;                      \n\
    v_texCoord = a_texCoord;                        \n\
}                                                   \n\
";

Worked for me, with no noticeable performance hit when I applied it only to my character sprites. Good luck!

I went the setShaderProgram() route to the sprites that will frame animate and it works well without a performance hit, but I only have one or two sprites at a time. And it fixed the wiggle on the Android side also. Thanks.

I’m new to shaders. Can you show me a code fragment with the call to set the shader in a class derived from CCSprite? It seems like you just call setShaderProgram() with a single shader, but you have two above (frag and vertex) so I am unclear how to join them into a single CCGLProgram.

Thanks

In the CPP file where I use them, I defined the files at the top:

const GLchar * twPositionTextureColor_frag =
#include "twShader_PositionTextureColor_frag.h"
const GLchar * twPositionTextureColor_vert =
#include "twShader_PositionTextureColor_vert.h"

Then in the appropriate function I implement:

GLProgram* glp = CCShaderCache::getInstance()->getGLProgram("TWShader_PositionTextureColor"); 

if (!glp) {
    glp = new GLProgram();
    // load custom program for animation
    glp->initWithVertexShaderByteArray(twPositionTextureColor_vert, twPositionTextureColor_frag);
				
    glp->addAttribute(kCCAttributeNamePosition, kCCVertexAttrib_Position);
    glp->addAttribute(kCCAttributeNameColor, kCCVertexAttrib_Color);
    glp->addAttribute(kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords);
    glp->link();
    glp->updateUniforms();
				
    CHECK_GL_ERROR_DEBUG();
				
    CCShaderCache::sharedShaderCache()->addProgram(glp, "TWShader_PositionTextureColor");
    glp->autorelease();
}
			
sprite->setShaderProgram(glp);

I haven’t checked if this still works/is necessary in Cocos v.3.x.

Thanks! I appreciate the quick response.

I am using cocos2dx 2.2.3, and got what you had compiled in, and verified that the program shader actually compiles properly (passes the CHECK_GL_ERROR_DEBUB() macro), but I still see my texture sheet sprites wiggle. I am only calling

sprite->setShaderProgram(glp);

right after I create the sprite and before I add it to the scene I call setShaderProgram(). However, I am still seeing the sprites wiggle as they animate.

Any ideas?

I’ve converted a project that had this issue to Cocos 3.3 and this solution no longer works, meaning the wiggle has returned. Additionally, it is regardless of whether the sprite sheet allows rotated sprites or not.

… sigh …

@hawkwood did you manage to find a solution? This is a really annoying bug

Hi, did someone found a solution for this? we are experiment a vert similar issue.

So, changing the shader resolution from lowp / mediump to highp worked for @lito88

What we can do in cocos2d-x is to have a collection of high-quality shaders, so users who trigger this bug can enable them… it could be a per-sprite basis (or global)…