com.intel.gameworkload.GameLayer.java Source code

Java tutorial

Introduction

Here is the source code for com.intel.gameworkload.GameLayer.java

Source

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.intel.gameworkload;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Stack;
import java.util.Vector;

import org.cocos2d.actions.instant.CCCallFuncN;
import org.cocos2d.actions.interval.CCFadeOut;
import org.cocos2d.actions.interval.CCMoveTo;
import org.cocos2d.actions.interval.CCSequence;
import org.cocos2d.actions.interval.CCSpawn;
import org.cocos2d.layers.CCLayer;
import org.cocos2d.config.ccMacros;
import org.cocos2d.events.CCTouchDispatcher;
import org.cocos2d.layers.*;
import org.cocos2d.nodes.*;
import org.cocos2d.types.*;
import org.jbox2d.callbacks.ContactImpulse;
import org.jbox2d.callbacks.ContactListener;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.*;
import org.jbox2d.collision.Manifold;
import org.jbox2d.collision.WorldManifold;
import org.jbox2d.collision.shapes.ChainShape;
import org.jbox2d.collision.shapes.CircleShape;
import org.jbox2d.collision.shapes.EdgeShape;
import org.jbox2d.dynamics.contacts.Contact;
import org.jbox2d.dynamics.joints.*;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Handler;
import android.util.Log;
import android.view.MotionEvent;
import aurelienribon.bodyeditor.BodyEditorLoader;

public class GameLayer extends CCColorLayer implements ContactListener {

    private static final float factor = 32.0f;
    private final float gravityFactor = 9.8f;

    private static final int sceneWidth = 1368;
    private static final int sceneHeight = 720;

    private static final int groundWidth = 1368;
    private static final int groundHeight = 20;

    private static final int rockWidth = 50;
    private static final int rockHeight = 100;
    private static final int nRocksPerRow = 20;

    private static final int snowWidth = 6;

    private static final int fanX = groundWidth / 2;
    private static final int fanSize = 150;
    private static int fanY = fanSize / 2 + 55;
    private static int fanSpeed = 10;

    private static final int MountainWidth = 600;
    private static final int MountainHeight = 384;

    private static final int ejectorHeight = MountainHeight;
    private static final int ejectorLength = 75;

    private static final int snowmanWidth = 90;
    private static final int snowmanHeight = 100;

    private static final int rockLabelWidth = 130;
    private static final int rockLabelHeight = 50;
    private static final int rockNumberWidth = 30;

    private static final int leftWallTag = -2;
    private static final int particleTag = -1;
    private static final int ejectorbaseTag = 2;
    private static final int ejectorarmTag = 3;
    private static final int snowTag = 4;

    private static final int rockTag = 5;
    private static final int fanTag = 7;
    private static final int stoneTag = 8;
    private static final int disableStoneTag = 9;
    private static final int groundTag = 10;
    private static final int leftMTag = 11;
    private static final int rightMTag = 12;
    private static final int snowmanTag = 13;

    private boolean useCircleSnow = true;
    private boolean mScored = false;

    private static AssetManager mAm;
    private static MainActivity mActivity;
    private World world = null;
    private BodyEditorLoader loader = null;

    private int mNSnows = 0;
    private int mNRocks = 0;
    private int mSnowStep = 0;
    private int mRockStep = 0;

    private Stack<AutoScaleSprite> mScoreSprites = new Stack<AutoScaleSprite>();
    private Stack<AutoScaleSprite> mParticleSprites = new Stack<AutoScaleSprite>();
    private Stack<AutoScaleSprite> mStoneSprites = new Stack<AutoScaleSprite>();

    private AutoScaleSprite mRockLabel = null;
    private AutoScaleSprite mN1 = null;
    private AutoScaleSprite mN2 = null;
    private AutoScaleSprite mN3 = null;

    void init(int nRocks, int nSnows, int runtime) {
        mNSnows = nSnows;
        mNRocks = nRocks;
        CCDirector.sharedDirector().setnRocks(nRocks);
        CCDirector.sharedDirector().setnSnows(nSnows);

        CGSize winSize = CCDirector.sharedDirector().displaySize();
        Log.d("GameWorkload", "window " + winSize.width + "x" + winSize.height);
        AutoScaleSprite.setAutoScale(winSize.width / sceneWidth, winSize.height / sceneHeight);
        this.setIsTouchEnabled(true);

        bodiesToDel = new Vector<Body>();
        bodiesToReplace = new Vector<Body>();
        particleBodies = new LinkedList<ParticleRecord>();

        // create physics world
        Vec2 gravity = new Vec2(0.0f, -1 * gravityFactor);
        world = new World(gravity);

        // create box of the world
        BodyDef sideBodyDef = new BodyDef();
        sideBodyDef.position.set(0, 0);
        Body sideBody = world.createBody(sideBodyDef);

        ChainShape sideShape = new ChainShape();
        Vec2[] vertices = new Vec2[4];
        vertices[0] = new Vec2(0, groundHeight / factor);
        vertices[1] = new Vec2(sceneWidth / factor, groundHeight / factor);
        vertices[2] = new Vec2(sceneWidth / factor, sceneHeight / factor);
        vertices[3] = new Vec2(0, sceneHeight / factor);

        sideShape.createChain(vertices, 4);
        Fixture sideFixture = sideBody.createFixture(sideShape, 1);
        sideFixture.setFriction(0.01f);

        // draw ground. The sprite has some problem to deal with long
        AutoScaleSprite ground = AutoScaleSprite.sprite("images/ground.png");

        ground.setPosition(groundWidth / 2, groundHeight / 2);
        ground.setTag(groundTag);
        addChild(ground);
        sideBody.setUserData(ground);

        //create left wall
        BodyDef wallBodyDef = new BodyDef();
        wallBodyDef.position.set(0, 0);
        Body wallBody = world.createBody(sideBodyDef);

        EdgeShape wallShape = new EdgeShape();
        wallShape.set(new Vec2(0, sceneHeight / factor), new Vec2(0, groundHeight / factor));
        Fixture wallFixture = wallBody.createFixture(wallShape, 1);
        wallFixture.setFriction(0.01f);

        AutoScaleSprite fakeSprite = AutoScaleSprite.sprite("images/snow.png");
        fakeSprite.setTag(leftWallTag);
        wallBody.setUserData(fakeSprite);

        String jsonString = readFileToString("snowbodies.json");
        loader = new BodyEditorLoader(jsonString);

        Body leftM = createSpriteAndStaticBody("leftM", leftMTag, MountainWidth / 2, MountainHeight / 2, 2.0f);
        Body rightM = createSpriteAndStaticBody("rightM", rightMTag, sceneWidth - MountainWidth / 2,
                MountainHeight / 2, 2.0f);

        AutoScaleSprite logo = AutoScaleSprite.sprite("intel.png");
        logo.setPosition(sceneWidth - logo.getContentSize().width / 2 - 1, logo.getContentSize().height / 2);
        addChild(logo);

        if (runtime < 4 && runtime >= 0) {
            AutoScaleSprite runtimeLogo = AutoScaleSprite.sprite("runtime" + runtime + ".png");
            runtimeLogo.setPosition(runtimeLogo.getContentSize().width / 2 + 10,
                    sceneHeight - runtimeLogo.getContentSize().height / 2);
            addChild(runtimeLogo);
        }

        updateRockNumber();

        Body snowMan = createSpriteAndDynamicBody("snowman", snowmanTag, sceneWidth - snowmanWidth / 2,
                MountainHeight + snowmanHeight / 2, 50.0f, null);
        snowMan.setBullet(true);

        WeldJointDef snowmanJointDef = new WeldJointDef();
        snowmanJointDef.initialize(snowMan, rightM,
                snowMan.getPosition().sub(new Vec2(0, snowmanHeight / 2 / factor)));
        snowmanJointDef.collideConnected = true;
        world.createJoint(snowmanJointDef);

        // create snow
        for (int i = 0; i < mNSnows; i++) {
            this.createSnow((i * snowWidth + snowWidth / 2),
                    (int) (sceneHeight - 10 - sceneHeight / 3 * ccMacros.CCRANDOM_0_1()), null);
        }

        for (int i = 0; i < mNRocks; i++) {
            String name = "rock" + (i % 5 + 1);
            createRock(name, rockWidth * (i % nRocksPerRow + 1) + ejectorLength,
                    sceneHeight - rockHeight * (i / nRocksPerRow + 1), null);
        }

        // create Fan
        Body fanBody = this.createSpriteAndDynamicBody("fan", fanTag, fanX, fanY, 1.0f, null);
        fanBody.setBullet(true);

        // create fan joint
        RevoluteJointDef fanJointDef = new RevoluteJointDef();
        fanJointDef.initialize(sideBody, fanBody, fanBody.getPosition());
        fanJointDef.enableMotor = true;
        fanJointDef.motorSpeed = fanSpeed;
        fanJointDef.maxMotorTorque = 6000;
        RevoluteJoint fanJoint = (RevoluteJoint) world.createJoint(fanJointDef);

        // create ejector base and arm.
        Body baseBody = this.createSpriteAndStaticBody("ejectorbase", ejectorbaseTag, ejectorLength / 2,
                ejectorHeight, 1.0f);
        armBody = this.createSpriteAndDynamicBody("ejectorarm", ejectorarmTag, ejectorLength,
                ejectorHeight + ejectorLength / 2, 1.0f, null);

        RevoluteJointDef armJointDef = new RevoluteJointDef();
        Vec2 anchor = new Vec2(ejectorLength / factor, ejectorHeight / factor);
        armJointDef.initialize(baseBody, armBody, anchor);
        armJointDef.enableMotor = true;
        armJointDef.motorSpeed = -15;
        armJointDef.maxMotorTorque = 700;
        armJointDef.enableLimit = true;
        armJointDef.lowerAngle = ccMacros.CC_DEGREES_TO_RADIANS(15);
        armJointDef.upperAngle = ccMacros.CC_DEGREES_TO_RADIANS(45);
        armJoint = (RevoluteJoint) world.createJoint(armJointDef);

        world.setContactListener(this);

        Log.w("GameWorkload", "before run scheduleupdate get class:" + this.getClass());
        this.scheduleUpdate();
    }

    protected GameLayer(ccColor4B color, int nRocks, int nSnows, int rockStep, int snowStep, int runtime,
            int mode) {
        // TODO Auto-generated constructor stub
        super(color);
        mRockStep = rockStep;
        mSnowStep = snowStep;
        init(nRocks, nSnows, runtime);
        benchmarkMode = (mode == 1);
    }

    public static CCScene scene(MainActivity mainActivity, AssetManager am, int nRocks, int nSnows, int rockStep,
            int snowStep, int runtime, int mode) {
        CCScene scene = CCScene.node();
        mAm = am;
        mActivity = mainActivity;
        CCLayer layer = new GameLayer(ccColor4B.ccc4(122, 228, 255, 255), nRocks, nSnows, rockStep, snowStep,
                runtime, mode);
        scene.addChild(layer);
        return scene;
    }

    private class ParticleRecord {
        public Vector<Body> bodies;
        public float life;

        public ParticleRecord(Vector<Body> particles, float time) {
            bodies = particles;
            life = time;
        }
    }

    private LinkedList<ParticleRecord> particleBodies = null;

    /*
     * unit is in pixels
     * number means #particles for each side
     */
    private Vector<Body> createParticles(float x, float y, int width, int number, Vec2 initVelocity,
            String imagePath) {
        Vector<Body> bodies = new Vector<Body>();
        //calculate the rectangle for particles
        float length = width * number;
        float ix = x - length / 2 + width / 2;
        float iy = y - length / 2 + width / 2;
        float maxForce = 0.1f;
        Vec2 center = new Vec2(x / factor, y / factor);
        for (int i = 0; i < number; i++) {
            for (int j = 0; j < number; j++) {
                AutoScaleSprite sprite;
                if (mParticleSprites.isEmpty()) {
                    sprite = AutoScaleSprite.sprite(imagePath);
                    if (sprite.getContentSize().width != width) {
                        sprite.setScale(width / sprite.getContentSize().width);
                    }
                    addChild(sprite);
                } else {
                    sprite = mParticleSprites.pop();
                    sprite.setVisible(true);
                }
                sprite.setPosition(ix, iy);
                sprite.setTag(particleTag);

                BodyDef bodyDef = new BodyDef();
                bodyDef.position.set(sprite.getPosition().x / factor, sprite.getPosition().y / factor);
                bodyDef.type = BodyType.DYNAMIC;
                bodyDef.userData = sprite;

                FixtureDef fixtureDef = new FixtureDef();
                fixtureDef.density = 0.5f;
                fixtureDef.friction = 0.7f;
                fixtureDef.restitution = 0.7f;

                Body body = world.createBody(bodyDef);
                CircleShape shape = new CircleShape();
                shape.setRadius(width / (factor * 2));
                fixtureDef.shape = shape;
                body.createFixture(fixtureDef);

                body.setLinearDamping(0.8f);
                Vec2 force = body.getPosition().sub(center);
                float distance = force.normalize();
                body.setLinearVelocity(initVelocity);
                body.applyLinearImpulse(force.mul(maxForce), body.getPosition());
                bodies.add(body);
                ix = ix + width;
            }
            ix = x - length / 2;
            iy = iy + width;
        }
        return bodies;
    }

    private void createScoreAnimation(float seconds) {
        AutoScaleSprite scoreSprite;
        if (mScoreSprites.isEmpty()) {
            scoreSprite = AutoScaleSprite.sprite("images/score.png");
            scoreSprite.setPosition(sceneWidth / 2, 0 - scoreSprite.getContentSize().height / 2);
            addChild(scoreSprite);
        } else {
            scoreSprite = mScoreSprites.pop();
            scoreSprite.setOpacity(255);
            scoreSprite.setPosition(sceneWidth / 2, 0 - scoreSprite.getContentSize().height / 2);
            scoreSprite.setVisible(true);
        }

        CCMoveTo actionMove = CCMoveTo.action(seconds,
                CGPoint.ccp(sceneWidth / 2, sceneHeight + scoreSprite.getContentSize().height / 2));
        CCFadeOut actionFadeOut = CCFadeOut.action(seconds);
        CCSpawn spawnActions = CCSpawn.actions(actionMove, actionFadeOut);
        CCCallFuncN actionMoveDone = CCCallFuncN.action(this, "scoreFinished");
        CCSequence actions = CCSequence.actions(spawnActions, actionMoveDone);
        scoreSprite.runAction(actions);
    }

    public void scoreFinished(Object sender) {
        AutoScaleSprite sprite = (AutoScaleSprite) sender;
        sprite.setVisible(false);
        mScoreSprites.push(sprite);
    }

    private final float LogInterval = 5f;
    private float timeToLog = LogInterval;
    private boolean logEnabled = false;

    private final float fireInterval = 3f;
    private float timeToFire = fireInterval;

    private final float windInterval = 0.1f;
    private float timeToWind = windInterval;
    private Vector<Body> bodiesToDel;
    private Vector<Body> bodiesToReplace;

    private final float benchmarkInterval = 20f;
    private final int benchmarkRockStep = 20;
    private final int benchmarkSnowStep = 50;
    private final int benchmarkMaxSteps = 6;
    private int benchmarkSteps = 0;
    private boolean benchmarkMode = false;
    private float benchmarkLastUpdateTime = 0;
    private long benchmarkLastContacts = 0;
    private float benchmarkTotalUpdateTime = 0;
    private long benchmarkTotalContacts = 0;
    private float benchmarkTotalFrames = 0;
    private double benchmarkAFPS = 1;
    private double benchmarkAUPF = 1;
    private double benchmarkAAPS = 1;
    private double benchmarkJPS = 0;

    private String benchmarkdetails = "";

    private void outputresult(String s) {
        benchmarkdetails += s + "\n";
        Log.d("GameWorkload", s);
    }

    private void appenddetails(String s) {
        benchmarkdetails += s + "\n";
    }

    private void printdetaiils(float afps, double aut, double aaps, float jps, float acps) {
        appenddetails("Calculating avg fps @" + afps);
        appenddetails("Calculating Average update time @" + aut);
        if (aaps > 0)
            appenddetails("Calculating Average animation/second @" + aaps);
        appenddetails("Calculating Average JPS @" + jps);
        appenddetails("Calculating Average Contacts/frame @" + acps);

        String s = String.format("%d %d %.4f %.4f %.4f %.4f %.4f", mNRocks, mNSnows, afps, aut, aaps, jps, acps);
        Log.d("GameWorkload", s);
        benchmarkAFPS *= afps;
        benchmarkAUPF *= aut;
        if (aaps > 0)
            benchmarkAAPS *= aaps;
        benchmarkJPS += jps;
    }

    private class MyTask implements Runnable {
        String mResult;
        String mDetails;

        public MyTask(String result, String details) {
            mResult = result;
            mDetails = details;
        }

        @Override
        public void run() {
            Intent resultData = new Intent();
            resultData.putExtra("benchmarkdetails", mDetails);
            resultData.putExtra("benchmarkresult", mResult);
            mActivity.setResult(Activity.RESULT_OK, resultData);
        }
    }

    String[] resultDescriptions = { "Animation/s", "FPS", "UpdateTimeInSec/frame", "JANK/s" };
    double[] resultValues = { 0, 0, 0, 0 };
    int[] precision = { 0, 1, 4, 4 };

    private String getBenchmarkResult() {
        if (benchmarkSteps > 1)
            resultValues[0] = Math.pow(benchmarkAAPS, 1 / (double) (benchmarkSteps - 1));
        resultValues[1] = Math.pow(benchmarkAFPS, 1 / (double) benchmarkSteps);
        resultValues[2] = Math.pow(benchmarkAUPF, 1 / (double) benchmarkSteps);
        resultValues[3] = benchmarkJPS / (float) benchmarkSteps;

        JSONObject result = new JSONObject();
        try {
            for (int i = 0; i < 4; i++) {
                JSONObject content = new JSONObject();
                content.put("description", resultDescriptions[i]);
                content.put("value", resultValues[i]);
                content.put("precision", precision[i]);
                result.put(Integer.toString(i), content);
            }
        } catch (JSONException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        String benchmarkresult = result.toString();
        return benchmarkresult;
    }

    private void updateActivityResult() {
        Handler handler = new Handler(mActivity.getMainLooper());
        handler.post(new MyTask(getBenchmarkResult(), benchmarkdetails));
    }

    private void exitActivity() {
        Handler handler = new Handler(mActivity.getMainLooper());
        handler.post(new Runnable() {
            public void run() {
                mActivity.finish();
            }
        });
    }

    public void update(float dt) {

        if (benchmarkMode) {
            if (benchmarkTotalFrames == 1 && benchmarkSteps == 0) {
                //print out first frame hint
                appenddetails("Running settings @" + mNRocks + " rocks @" + mNSnows + " snows");
            }
            float totalTime = CCDirector.sharedDirector().gettotalTime();
            if (totalTime > 0) {
                benchmarkTotalUpdateTime += benchmarkLastUpdateTime;
                benchmarkTotalFrames++;
                benchmarkTotalContacts += benchmarkLastContacts;
            }
            if (totalTime >= benchmarkInterval) {
                this.benchmarkSteps++;
                appenddetails("Ended settings @" + mNRocks + " rocks @" + mNSnows + " snows");
                CCDirector d = CCDirector.sharedDirector();
                printdetaiils(d.getAFPS(), d.getAUTPF(), d.getAAPS(), d.getJPS(),
                        benchmarkTotalContacts / (float) benchmarkTotalFrames);

                updateActivityResult();
                if (benchmarkSteps >= benchmarkMaxSteps) {
                    outputresult("Benchmark ended");
                    //outputresult(getBenchmarkResult());
                    updateActivityResult();
                    CCDirector.sharedDirector().end();
                    exitActivity();

                } else {
                    benchmarkTotalUpdateTime = 0;
                    benchmarkTotalFrames = 0;
                    benchmarkTotalContacts = 0;
                    appenddetails("Running settings @" + mNRocks + " rocks @" + mNSnows + " snows");
                    addObjects(benchmarkRockStep, benchmarkSnowStep);
                }
            }
        }

        long t0 = System.currentTimeMillis();
        world.step(1.0f / 60, 10, 10);
        int nContacts = world.getContactCount();
        benchmarkLastContacts = nContacts;
        long t1 = System.currentTimeMillis();
        if (mScored) {
            createScoreAnimation(2);
            mScored = false;
        }

        timeToLog -= dt;
        timeToFire -= dt;
        timeToWind -= dt;

        if (timeToFire < 0) {
            fire();
            timeToFire = fireInterval;
        }

        if (timeToLog < 0 && logEnabled) {
            float AFPS = CCDirector.sharedDirector().getAFPS();
            float Time = CCDirector.sharedDirector().gettotalTime();
            Log.d("GameWorkload", "TotalTime=" + Time + " AFPS=" + AFPS + " Rock=" + mNRocks + " Snow=" + mNSnows
                    + " Sprites=" + this.getChildren().size() + " particleSprites=" + mParticleSprites.size());
            timeToLog = LogInterval;
        }

        float xForce = 0;
        if (timeToWind < 0)
            xForce = ccMacros.CCRANDOM_0_1();

        Body body = world.getBodyList();
        bodiesToDel.clear();
        bodiesToReplace.clear();

        for (Iterator<ParticleRecord> i = particleBodies.iterator(); i.hasNext();) {
            ParticleRecord r = i.next();
            r.life -= dt;
            if (r.life < 0) {
                for (Body b : r.bodies) {
                    //this.removeChild((CCNode) b.getUserData(), true);
                    AutoScaleSprite sprite = (AutoScaleSprite) b.getUserData();
                    sprite.setVisible(false);
                    mParticleSprites.push(sprite);
                    world.destroyBody(b);
                }
                r.bodies.clear();
                i.remove();
            } else {
                for (Body b : r.bodies) {
                    AutoScaleSprite s = (AutoScaleSprite) b.getUserData();
                    s.setOpacity(Math.round(255 * r.life));
                }
            }
        }

        int nBodiesOut = 0;

        // check stoneJoint
        if (stoneJoint != null) {
            if (stoneJoint.getBodyA().getLinearVelocity().x > 0) {
                if (armJoint.getJointAngle() <= ccMacros.CC_DEGREES_TO_RADIANS(20)) {
                    world.destroyJoint(stoneJoint);
                    stoneJoint = null;
                }
            }
        }

        while (body != null) {

            if (body.getType() == BodyType.STATIC) {
                body = body.getNext();
                continue;
            }
            AutoScaleSprite sprite = (AutoScaleSprite) body.m_userData;

            if (sprite.getTag() == disableStoneTag) {
                // apply exploiting
                Vec2 initVelocity = body.getLinearVelocity().mul(0.1f);
                particleBodies.add(new ParticleRecord(this.createParticles(sprite.getPosition().x,
                        sprite.getPosition().y, 6, 4, initVelocity, "images/particle.png"), 1));
                bodiesToDel.add(body);

                if (stoneJoint != null && stoneJoint.getBodyA() == body) {
                    // remove joint
                    world.destroyJoint(stoneJoint);
                    stoneJoint = null;
                }
            } else if (sprite.getTag() != groundTag && sprite.getTag() != leftWallTag) {
                sprite.setPosition(body.getPosition().x * factor, body.getPosition().y * factor);
                sprite.setRotation(-1 * ccMacros.CC_RADIANS_TO_DEGREES(body.getAngle()));
            }

            // apply wind force

            if (timeToWind < 0 && sprite.getTag() == snowTag) {
                if (body.getPosition().x * factor < ejectorLength * 2) {
                    body.applyLinearImpulse(new Vec2(xForce * body.getMass(), 0), body.getPosition());

                } else if (body.getPosition().x * factor > sceneWidth - snowmanWidth * 2) {
                    body.applyLinearImpulse(new Vec2(-1 * xForce * body.getMass(), 0), body.getPosition());
                }
            }

            float bx = body.getPosition().x * factor;
            float by = body.getPosition().y * factor;
            if ((bx < 0 || bx > sceneWidth || by < groundHeight - 2 || by > sceneHeight)
                    && sprite.getTag() != particleTag) {

                //Log.d("GameWorkload", "Body out of box, tag "+sprite.getTag()+ " x "+body.getPosition().x+ " y "+body.getPosition().y);
                bodiesToReplace.add(body);
            }

            body = body.getNext();
        }

        for (int i = 0; i < bodiesToDel.size(); i++) {
            Body delBody = bodiesToDel.get(i);
            AutoScaleSprite sprite = (AutoScaleSprite) delBody.getUserData();
            sprite.setVisible(false);
            if (sprite.getTag() == this.particleTag) {
                mParticleSprites.push(sprite);
            } else if (sprite.getTag() == this.disableStoneTag) {
                mStoneSprites.push(sprite);
            }
            //this.removeChild(sprite, true);
            world.destroyBody(delBody);
        }

        int x = sceneWidth / 2;
        for (int i = 0; i < bodiesToReplace.size(); i++) {
            Body replaceBody = bodiesToReplace.get(i);
            AutoScaleSprite sprite = (AutoScaleSprite) replaceBody.getUserData();
            if (sprite.getTag() == rockTag) {
                this.createRock((String) sprite.getUserData(), x,
                        (int) (sceneHeight - sprite.getContentSize().height), sprite);
            } else if (sprite.getTag() == snowTag) {
                this.createSnow(x, (int) (sceneHeight - sprite.getContentSize().height), sprite);
            }
            x = x + (int) sprite.getContentSize().width;
            world.destroyBody(replaceBody);
            //this.removeChild(sprite, true);
        }
        bodiesToReplace.clear();

        if (timeToWind < 0)
            timeToWind = windInterval;
        CCDirector.sharedDirector().setObjNumber(world.getBodyCount());
        bodiesToDel.clear();
        long t2 = System.currentTimeMillis();
        benchmarkLastUpdateTime = (float) (t2 - t0) / 1000.f;
        //Log.d("GameWorkload", "contacts="+nContacts+" lastTotalTime "+dt*1000+" updateTime="+(t2-t0)+" stepTime="+(t1-t0)+" otherTime="+(t2-t1)+"benchmarkUpdateTime "+this.benchmarkLastUpdateTime);
        CCDirector.sharedDirector().setUpdateTime(benchmarkLastUpdateTime);
    }

    private void addObjects(int nRocks, int nSnows) {
        mNSnows += nSnows;
        mNRocks += nRocks;
        for (int i = 0; i < nSnows; i++) {
            this.createSnow((i * snowWidth + snowWidth / 2),
                    (int) (sceneHeight - 10 - sceneHeight / 3 * ccMacros.CCRANDOM_0_1()), null);
        }
        for (int i = 0; i < nRocks; i++) {
            String name = "rock" + (i % 5 + 1);
            createRock(name, rockWidth * (i % nRocksPerRow + 1) + ejectorLength,
                    sceneHeight - rockHeight * (i / nRocksPerRow + 1), null);
        }
        CCDirector.sharedDirector().resetAFPS();
        CCDirector.sharedDirector().setnRocks(mNRocks);
        CCDirector.sharedDirector().setnSnows(mNSnows);
        updateRockNumber();
    }

    private Body createRock(String name, int x, int y, AutoScaleSprite oldSprite) {
        return createSpriteAndBody(name, rockTag, x, y, 0.5f, 0.1f, 0.8f, BodyType.DYNAMIC, oldSprite, false);
    }

    private Body createSnow(int x, int y, AutoScaleSprite oldSprite) {
        Body body = createSpriteAndBody("snow", snowTag, x, y, 0.5f, 0.7f, 0.001f, BodyType.DYNAMIC, oldSprite,
                this.useCircleSnow);
        body.setGravityScale(0.05f);
        body.setLinearDamping(1);
        return body;
    }

    private Body createSpriteAndDynamicBody(String name, int tag, int x, int y, float density,
            AutoScaleSprite oldSprite) {
        return createSpriteAndBody(name, tag, x, y, density, 0.2f, 0.8f, BodyType.DYNAMIC, oldSprite, false);
    }

    private Body createSpriteAndStaticBody(String name, int tag, int x, int y, float density) {
        return createSpriteAndBody(name, tag, x, y, density, 0.2f, 0.8f, BodyType.STATIC, null, false);
    }

    private Body createSpriteAndBody(String name, int tag, int x, int y, float density, float friction,
            float restitution, BodyType type, AutoScaleSprite oldSprite, boolean useCircle) {

        AutoScaleSprite sprite;
        if (oldSprite != null) {
            sprite = oldSprite;
            sprite.setVisible(true);
        } else {
            sprite = AutoScaleSprite.sprite(loader.getImagePath(name));
            addChild(sprite);
        }
        sprite.setPosition(x, y);
        sprite.setTag(tag);

        BodyDef bodyDef = new BodyDef();
        bodyDef.position.set(sprite.getPosition().x / factor, sprite.getPosition().y / factor);
        bodyDef.type = type;
        bodyDef.userData = sprite;

        FixtureDef fixtureDef = new FixtureDef();
        fixtureDef.density = density;
        fixtureDef.friction = friction;
        fixtureDef.restitution = restitution;

        Body body = world.createBody(bodyDef);

        if (useCircle) {
            CircleShape shape = new CircleShape();
            shape.setRadius(snowWidth / (factor * 2));
            fixtureDef.shape = shape;
            body.createFixture(fixtureDef);
        } else {
            loader.attachFixture(body, name, fixtureDef, sprite.getContentSize().width / factor);
        }
        // 4. Create the body fixture automatically by using the loader.
        sprite.setUserData(name);
        return body;
    }

    private Body armBody = null;
    //private Body baseBody = null;
    private WeldJoint stoneJoint = null;
    private RevoluteJoint armJoint = null;

    public void fire() {
        // create magic stone
        if (stoneJoint == null) {
            Body stoneBody;
            if (mStoneSprites.isEmpty()) {
                stoneBody = this.createSpriteAndDynamicBody("stone", stoneTag, ejectorLength,
                        ejectorHeight + ejectorLength, 1.5f, null);
            } else {
                stoneBody = this.createSpriteAndDynamicBody("stone", stoneTag, ejectorLength,
                        ejectorHeight + ejectorLength, 1.5f, mStoneSprites.pop());
            }
            stoneBody.setBullet(true);
            WeldJointDef stoneJointDef = new WeldJointDef();
            stoneJointDef.initialize(stoneBody, armBody, stoneBody.getPosition());
            stoneJointDef.collideConnected = false;
            stoneJoint = (WeldJoint) world.createJoint(stoneJointDef);

            // apply force to arm
            armBody.applyAngularImpulse(1000);
        }
    }

    private String readFileToString(String fname) {
        try {
            StringBuilder builder = new StringBuilder(1024);
            BufferedReader br = new BufferedReader(new InputStreamReader(mAm.open(fname)));
            String line;
            while ((line = br.readLine()) != null) {
                builder.append(line).append("\n");
            }
            br.close();
            return builder.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean ccTouchesBegan(MotionEvent event) {
        return CCTouchDispatcher.kEventHandled;
    }

    @Override
    public boolean ccTouchesEnded(MotionEvent event) {
        Log.d("GameWorkload", "touch end");
        if (!benchmarkMode) {
            addObjects(mRockStep, mSnowStep);
        }
        return super.ccTouchesEnded(event);

    }

    @Override
    public boolean ccTouchesMoved(MotionEvent event) {
        return super.ccTouchesMoved(event);
    }

    @Override
    public boolean ccTouchesCancelled(MotionEvent event) {
        return super.ccTouchesCancelled(event);
    }

    @Override
    public void beginContact(Contact contact) {
        // TODO Auto-generated method stub
    }

    @Override
    public void endContact(Contact contact) {
        // TODO Auto-generated method stub

    }

    @Override
    public void preSolve(Contact contact, Manifold oldManifold) {
        // TODO Auto-generated method stub
        // get obj of contact
    }

    @Override
    public void postSolve(Contact contact, ContactImpulse impulse) {
        AutoScaleSprite spriteA = (AutoScaleSprite) contact.getFixtureA().getBody().getUserData();
        AutoScaleSprite spriteB = (AutoScaleSprite) contact.getFixtureB().getBody().getUserData();

        if (spriteA.getTag() <= snowTag || spriteB.getTag() <= snowTag) {
            return;
        }

        if (spriteB.getTag() == stoneTag) {
            AutoScaleSprite tmp = spriteA;
            spriteA = spriteB;
            spriteB = tmp;
        }

        if (spriteA.getTag() == stoneTag) {
            //            Log.d("GameWorkload", "special hit tagA " + spriteA.getTag() + " tagB " + spriteB.getTag());
            WorldManifold worldManifold = new WorldManifold();
            contact.getWorldManifold(worldManifold);
            spriteA.setTag(disableStoneTag);
            spriteA.setPosition(worldManifold.points[0].x * factor, worldManifold.points[0].y * factor);

            if (spriteB.getTag() == snowmanTag) {
                mScored = true;
            }
        }
    }

    private void updateRockNumber() {
        if (mRockLabel == null) {
            mRockLabel = AutoScaleSprite.sprite("rocklabel.png");
            this.addChild(mRockLabel);
        }
        if (mNRocks > 999)
            return;
        //calculate numbers for 1x, 10x, 100x
        int d1 = mNRocks % 10;
        int d10 = mNRocks % 100 / 10;
        int d100 = mNRocks / 100;

        float x = sceneWidth - 3 * rockNumberWidth - rockLabelWidth / 2;
        float y = sceneHeight - rockLabelHeight / 2;
        mRockLabel.setPosition(x, y);

        x = sceneWidth - 2.5f * rockNumberWidth;
        if (d100 > 0) {
            AutoScaleSprite n = AutoScaleSprite.sprite(d100 + ".png");
            n.setPosition(x, y);
            x += rockNumberWidth;
            if (mN1 != null)
                this.removeChild(mN1, true);
            mN1 = n;
            this.addChild(n);
        }
        if (d100 > 0 || d10 > 0) {
            AutoScaleSprite n = AutoScaleSprite.sprite(d10 + ".png");
            n.setPosition(x, y);
            x += rockNumberWidth;
            if (mN2 != null)
                this.removeChild(mN2, true);
            mN2 = n;
            this.addChild(n);
        }
        {
            AutoScaleSprite n = AutoScaleSprite.sprite(d1 + ".png");
            n.setPosition(x, y);
            x += rockNumberWidth;
            if (mN3 != null)
                this.removeChild(mN3, true);
            mN3 = n;
            this.addChild(n);
        }
    }

}