I was developing tower of defence game for a long time.
It was about to complete but I have to create ripple effect for one of the weapon that I have used.

First question I want to ask is that is it feasible to use ripple effect for such purpose?
Because suddenly fps goes down.

I have created ripple effect for live wallpaper. But I don't know how to create it for normal game activity.

I was not able to understand many things from it. If any one has some knowledge on this then please provide.

Edit :
I include the image for ripple effect that I think I have to use for weapon fire.

Also I include code that I use to create ripple effect. Similar code with some changes I used to create live wallpaper and it work perfectly. But in normal game activity I don't able to find my mistake. So please guide on this

@Override
public Engine onCreateEngine(EngineOptions pEngineOptions) {

    return new org.andengine.engine.Engine(pEngineOptions) {
        private boolean mRenderTextureInitialized;

        private RenderTexture mRenderTexture;
        private UncoloredSprite mRenderTextureSprite;

        @Override
        public void onDrawFrame(final GLState pGLState)
                throws InterruptedException {
            final boolean firstFrame = !this.mRenderTextureInitialized;

            if (firstFrame) {
                this.initRenderTextures(pGLState);
                this.mRenderTextureInitialized = true;
            }

            final int surfaceWidth = this.mCamera.getSurfaceWidth();
            final int surfaceHeight = this.mCamera.getSurfaceHeight();

            this.mRenderTexture.begin(pGLState);
            {
                /* Draw current frame. */
                super.onDrawFrame(pGLState);
            }
            this.mRenderTexture.end(pGLState);

            /* Draw rendered texture with custom shader. */
            {
                pGLState.pushProjectionGLMatrix();
                pGLState.orthoProjectionGLMatrixf(0, surfaceWidth, 0,
                        surfaceHeight, -1, 1);
                {
                    this.mRenderTextureSprite
                            .onDraw(pGLState, this.mCamera);
                }
                pGLState.popProjectionGLMatrix();
            }
        }

        private void initRenderTextures(final GLState pGLState) {
            final int surfaceWidth = this.mCamera.getSurfaceWidth();
            final int surfaceHeight = this.mCamera.getSurfaceHeight();

            this.mRenderTexture = new RenderTexture(RippleEffectDemo.this
                    .getEngine().getTextureManager(), surfaceWidth,
                    surfaceHeight);
            this.mRenderTexture.init(pGLState);

            final ITextureRegion renderTextureTextureRegion = TextureRegionFactory
                    .extractFromTexture(this.mRenderTexture);
            this.mRenderTextureSprite = new UncoloredSprite(0, 0,
                    renderTextureTextureRegion, RippleEffectDemo.this
                            .getEngine().getVertexBufferObjectManager()) {

                @Override
                protected void preDraw(final GLState pGLState,
                        final Camera pCamera) {

                    this.setShaderProgram(RippleShaderProgram.getInstance());

                    super.preDraw(pGLState, pCamera);

                    if (mCurrentWaveLength &gt; 25)
                        mCurrentWaveLength -= 25;
                    else
                        mCurrentWaveLength = 0;

                    float currentTime = -0.001f * mCurrentWaveLength;
                    float aliveTimer = (float) mCurrentWaveLength
                            / (float) mWaveLength;

                    GLES20.glUniform4f(
                            RippleShaderProgram.sUniformResolution,
                            (float) SCREEN_WIDTH, (float) SCREEN_HEIGHT,
                            RippleEffectDemo.this.mDropCenterX,
                            RippleEffectDemo.this.mDropCenterY);
                    GLES20.glUniform2f(RippleShaderProgram.sUniformTime,
                            currentTime, aliveTimer);
                }
            };
        }
    };
}

@Override
public void onCreateResources() {
    this.mBitmapTextureAtlas = new BitmapTextureAtlas(
            this.getTextureManager(), 512, 512);
    this.mBackgroundTextureRegion = BitmapTextureAtlasTextureRegionFactory
            .createFromAsset(this.mBitmapTextureAtlas, this,
                    "badge_large.png", 0, 0);
    this.mBitmapTextureAtlas.load();

    this.getShaderProgramManager().loadShaderProgram(
            RippleShaderProgram.getInstance());
}

@Override
public Scene onCreateScene() {
    final Scene scene = new Scene();

    final int centerX = (int) ((CAMERA_WIDTH - this.mBackgroundTextureRegion
            .getWidth()) / 2);
    final int centerY = (int) ((CAMERA_HEIGHT - this.mBackgroundTextureRegion
            .getHeight()) / 2);

    backgroundSprite = new Sprite(centerX, centerY,
            this.mBackgroundTextureRegion, this.getEngine()
                    .getVertexBufferObjectManager());
    scene.attachChild(backgroundSprite);

    scene.setOnSceneTouchListener(this);

    return scene;
}

protected void onTap(final int pX, final int pY) {
    // we do not support multiple wave
    if (0 == mCurrentWaveLength) {
        // so skip wave, if it is active
        mCurrentWaveLength = mWaveLength;

        this.mDropCenterX = (float) pX;
        this.mDropCenterY = (float) pY;
    }
}

@Override
public void onClick(final ClickDetector pClickDetector,
        final int pPointerID, final float pSceneX, final float pSceneY) {
}

@Override
public boolean onSceneTouchEvent(final Scene pScene,
        final TouchEvent pSceneTouchEvent) {

    if (pSceneTouchEvent.isActionDown()) {
        onTap((int) pSceneTouchEvent.getX(), (int) pSceneTouchEvent.getY());
        return true;
    }

    this.mDropCenterX = pSceneTouchEvent.getMotionEvent().getX()
            / this.mCamera.getSurfaceWidth();
    this.mDropCenterY = pSceneTouchEvent.getMotionEvent().getY()
            / this.mCamera.getSurfaceHeight();

    return true;
}

public static class RippleShaderProgram extends ShaderProgram {
    private static RippleShaderProgram INSTANCE;

    public static final String VERTEXSHADER = "uniform mat4 "
            + ShaderProgramConstants.UNIFORM_MODELVIEWPROJECTIONMATRIX
            + ";\n" + "attribute vec4 "
            + ShaderProgramConstants.ATTRIBUTE_POSITION + ";\n"
            + "attribute vec2 "
            + ShaderProgramConstants.ATTRIBUTE_TEXTURECOORDINATES + ";\n"
            + "varying vec2 "
            + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + ";\n"
            + "void main() {\n" + " "
            + ShaderProgramConstants.VARYING_TEXTURECOORDINATES + " = "
            + ShaderProgramConstants.ATTRIBUTE_TEXTURECOORDINATES + ";\n"
            + " gl_Position = "
            + ShaderProgramConstants.UNIFORM_MODELVIEWPROJECTIONMATRIX
            + " * " + ShaderProgramConstants.ATTRIBUTE_POSITION + ";\n"
            + "}";

    // private static final String UNIFORM_TOUCH_COORDS = "touchCoords";
    private static final String UNIFORM_RESOLUTION = "resolution";
    private static final String UNIFORM_TIME = "time";

    public static final String FRAGMENTSHADER = "precision mediump float;\n"
            +

            "uniform sampler2D "
            + ShaderProgramConstants.UNIFORM_TEXTURE_0
            + ";\n"
            + "varying mediump vec2 "
            + ShaderProgramConstants.VARYING_TEXTURECOORDINATES
            + ";\n"
            + "uniform vec4 "
            + RippleShaderProgram.UNIFORM_RESOLUTION
            + ";\n"
            + "uniform vec2 "
            + RippleShaderProgram.UNIFORM_TIME
            + ";\n"
            +

            "void main() {\n"
            + " vec2 tap = "
            + RippleShaderProgram.UNIFORM_RESOLUTION
            + ".zw;\n"
            + " tap.x = "
            + RippleShaderProgram.UNIFORM_RESOLUTION
            + ".x - tap.x;\n"
            + " vec2 tPos = -1.0 + 2.0 * tap.xy / "
            + RippleShaderProgram.UNIFORM_RESOLUTION
            + ".xy;\n"
            + " vec2 cPos = -1.0 + 2.0 * gl_FragCoord.xy / "
            + RippleShaderProgram.UNIFORM_RESOLUTION
            + ".xy;\n"
            + " cPos = cPos + tPos;\n"
            + " float cLength = length(cPos);\n"
            + " float radius = 18.0 * "
            + RippleShaderProgram.UNIFORM_TIME
            + ".y;\n"
            + " float amplitude = 0.05 * "
            + RippleShaderProgram.UNIFORM_TIME
            + ".y;\n"
            + " vec2 uv = gl_FragCoord.xy/"
            + RippleShaderProgram.UNIFORM_RESOLUTION
            + ".xy"
            + "+(cPos/cLength)*cos(cLength*radius-"
            + RippleShaderProgram.UNIFORM_TIME
            + ".x *4.0)*amplitude;\n"
            + " vec3 col = texture2D("
            + ShaderProgramConstants.UNIFORM_TEXTURE_0
            + ",uv).xyz;\n"
            + " gl_FragColor = vec4(col,1.0); \n" + // color

            "}";

    // ===========================================================
    // Fields
    // ===========================================================

    public static int sUniformModelViewPositionMatrixLocation = ShaderProgramConstants.LOCATION_INVALID;
    public static int sUniformTexture0Location = ShaderProgramConstants.LOCATION_INVALID;
    public static int sUniformResolution = ShaderProgramConstants.LOCATION_INVALID;
    public static int sUniformTime = ShaderProgramConstants.LOCATION_INVALID;

    // ===========================================================
    // Constructors
    // ===========================================================

    private RippleShaderProgram() {
        super(RippleShaderProgram.VERTEXSHADER,
                RippleShaderProgram.FRAGMENTSHADER);
    }

    public static RippleShaderProgram getInstance() {
        if (RippleShaderProgram.INSTANCE == null) {
            RippleShaderProgram.INSTANCE = new RippleShaderProgram();
        }
        return RippleShaderProgram.INSTANCE;
    }

    @Override
    protected void link(final GLState pGLState)
            throws ShaderProgramLinkException {
        GLES20.glBindAttribLocation(this.mProgramID,
                ShaderProgramConstants.ATTRIBUTE_POSITION_LOCATION,
                ShaderProgramConstants.ATTRIBUTE_POSITION);
        GLES20.glBindAttribLocation(
                this.mProgramID,
                ShaderProgramConstants.ATTRIBUTE_TEXTURECOORDINATES_LOCATION,
                ShaderProgramConstants.ATTRIBUTE_TEXTURECOORDINATES);

        super.link(pGLState);

        RippleShaderProgram.sUniformModelViewPositionMatrixLocation = this
                .getUniformLocation(ShaderProgramConstants.UNIFORM_MODELVIEWPROJECTIONMATRIX);
        RippleShaderProgram.sUniformTexture0Location = this
                .getUniformLocation(ShaderProgramConstants.UNIFORM_TEXTURE_0);

        RippleShaderProgram.sUniformResolution = this
                .getUniformLocation(RippleShaderProgram.UNIFORM_RESOLUTION);
        RippleShaderProgram.sUniformTime = this
                .getUniformLocation(RippleShaderProgram.UNIFORM_TIME);
    }

    @Override
    public void bind(final GLState pGLState,
            final VertexBufferObjectAttributes pVertexBufferObjectAttributes) {
        GLES20.glDisableVertexAttribArray(ShaderProgramConstants.ATTRIBUTE_COLOR_LOCATION);

        super.bind(pGLState, pVertexBufferObjectAttributes);

        GLES20.glUniformMatrix4fv(
                RippleShaderProgram.sUniformModelViewPositionMatrixLocation,
                1, false, pGLState.getModelViewProjectionGLMatrix(), 0);
        GLES20.glUniform1i(RippleShaderProgram.sUniformTexture0Location, 0);
    }

    @Override
    public void unbind(final GLState pGLState)
            throws ShaderProgramException {
        GLES20.glEnableVertexAttribArray(ShaderProgramConstants.ATTRIBUTE_COLOR_LOCATION);

        super.unbind(pGLState);
    }
}



Question: Didn't Say Uno Before Running Out of Cards
Answer: From the rules, the hand is over. 

http://www.wonkavator.com/uno/unorules.html


  When you have one card left, you must yell "UNO" (meaning one).
  Failure to do this results in you having to pick two cards from the
  DRAW pile. That is, of course if you get caught by the other players.
  Once a player has no cards left, the hand is over. Points are scored
  (see scoring section) and you start over again. That's UNO in a
  nutshell.


Further, you only have until the next player starts playing a card before it is to late to call them out. In this scenario once Player 2 started his 3rd turn (by starting the motion to play his last card), it was too late to call him out.


  A player who forgets to say UNO before his card touches the discard
  pile, but "catches" himself before any other player catches him, is
  safe and is not subject to the penalty. You may not catch a player for
  failure to say UNO until his second to last card touches the DISCARD
  pile. Also, you may not catch a player for failure to say UNO after
  the next player begins his turn. "Beginning a turn is defines as
  either drawing a card from the DRAW pile or drawing a card from your
  hand to play. If the last card played in a hand is a Draw Two or Wild
  Draw Four card, the next player must draw the two or four cards. These
  cards are counted when points are totaled. If no one is out of cards
  by the time the DRAW pile is depleted, reshuffle and continue play.


In order to call out Player2 and force him to draw cards for not saying 'UNO', Player 1 would have had to say it directly after his second to last card was played (touched the discard pile) but before he started to play his last card. Incredibly short, but possible.