Skip to content
shottedsheriff edited this page Oct 22, 2014 · 6 revisions

Setup

On this page you will learn how to setup this extension so you can use it in your own project.

NOTE: I only tested this extension with Windows and Android, so I only explain what you have to do for this two platforms in detail. But if you use an other platform you should be able transfer this wiki to it.

Step 1: Remove Box2D

This extension already includes all the code for Box2D. So if you are currently using Box2D in your project you have to remove this dependencies to keep everything clean. If you're not using Box2D you can skip this and continue with step 2.

To remove the Box2D dependecies you have to open up the build.gradle file in your root directory. This are the dependencies you have to remove:

Core Dependency:

compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"

Desktop Dependency:

compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"

Android Dependency:

compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-armeabi"
natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-armeabi-v7a"
natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-x86"

iOS Dependency:

compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
natives "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-ios"

HTML Dependency:

compile "com.badlogicgames.gdx:gdx-box2d-gwt:$gdxVersion:sources"
compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion:sources"

Step 2: Adding new dependecies

Now you have to add the new dependencies for the new files. This are the lines you have to add:

Core Dependency:

compile fileTree(dir: 'libs', include: '*.jar')

Desktop Dependency:

compile fileTree(dir: 'libs', include: '*.jar')

Android Dependency:

compile fileTree(dir: 'libs', include: '*.so')

If you are using other platforms it should work the same way, you just have to adjust the ending of the files to iunclude.

Step 3: Adding the files

Now you are going to add the actual files. You can find them inside the libs folder of this repository.

NOTE: I only compiled the code for Windows 32/64 bit, Android and Linux 32 bit. If you want to use this extension with other platforms like MacOS or iOS you need to compile the code yourself. All you need for this is inside the jni folder of this repository. HTML5 isn't supported at all at the moment because you can't use native code with this platform.

Place the files as follows: (If you don't have a libs folder in one of your projects just create one)

gdx-liquidfun.jar into core/libs

gdx-liquidfun-natives.jar into desktop/libs

libgdx-liquidfun.so from libs/armeabi into android/libs/armeabi

libgdx-liquidfun.so from libs/armeabi-v7a into android/libs/armeabi-v7a

libgdx-liquidfun.so from libs/x86 into android/libs/x86

After adding all necessary files you have to refresh your project by right-clicking the single projects inside Eclipse and click on Gradle -> refresh all. If you don't have gdx-liquidfun.jar in Android Private Libraries it is necessary to edit build path of android project. To do so you should right click on android project and select Properties -> Java Build Path -> Add external Jar and pick core/libs/gdx-liquidfun.jar. Last step is to check this library in Order and export.

Step 4: Testing (Optional)

Now you can test liquidfun to see if anything went wrong. To do this you can use this simple class:

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;

import finnstr.libgdx.liquidfun.ParticleDebugRenderer;
import finnstr.libgdx.liquidfun.ParticleDef.ParticleType;
import finnstr.libgdx.liquidfun.ParticleGroupDef;
import finnstr.libgdx.liquidfun.ParticleSystem;
import finnstr.libgdx.liquidfun.ParticleSystemDef;

public class LiquidfunTest extends ApplicationAdapter implements InputProcessor {

	private final static float BOX_TO_WORLD = 120.0f;
	private final static float WORLD_TO_BOX = 1f / BOX_TO_WORLD;

	private OrthographicCamera camera;
	private SpriteBatch batch;
	private Texture texture;
	private Sprite sprite;

	private World mWorld;
	private ParticleSystem mParticleSystem;
	private ParticleDebugRenderer mParticleDebugRenderer;
	private Box2DDebugRenderer mDebugRenderer;

	private ParticleGroupDef mParticleGroupDef1;
	private ParticleGroupDef mParticleGroupDef2;

	@Override
	public void create() {
		float width = Gdx.graphics.getWidth();
		float height = Gdx.graphics.getHeight();

		camera = new OrthographicCamera(width, height);
		camera.position.set(width / 2, height / 2, 0);
		camera.update();
		Gdx.input.setInputProcessor(this);

		batch = new SpriteBatch();
		texture = new Texture(Gdx.files.internal("badlogic.jpg"));
		texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
		TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);

		sprite = new Sprite(region);
		sprite.setSize(width, height);
		sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
		sprite.setPosition(0, 0);

		createBox2DWorld(width, height);
		createParticleStuff(width, height);

		/* Render stuff */
		mDebugRenderer = new Box2DDebugRenderer();
		mParticleDebugRenderer = new ParticleDebugRenderer(new Color(0, 1, 0, 1), mParticleSystem.getParticleCount());

		/* Version */
		Gdx.app.log("Running LiquidFun version", mParticleSystem.getVersionString());
		updateLog();
	}

	private void createBox2DWorld(float width, float height) {
		mWorld = new World(new Vector2(0, -9.8f), false);

		/* Bottom */
		BodyDef bodyDef = new BodyDef();
		bodyDef.type = BodyType.StaticBody;
		bodyDef.position.set(width * WORLD_TO_BOX / 2f, height * (2f / 100f) * WORLD_TO_BOX / 2f);
		//bodyDef.angle = (float) Math.toRadians(-30);
		Body ground = mWorld.createBody(bodyDef);

		PolygonShape shape = new PolygonShape();
		shape.setAsBox(width * WORLD_TO_BOX / 2, height * (2f / 100f) * WORLD_TO_BOX / 2f);

		FixtureDef fixDef = new FixtureDef();
		fixDef.friction = 0.2f;
		fixDef.shape = shape;
		ground.createFixture(fixDef);

		shape.dispose();

		/* Walls */
		BodyDef bodyDef1 = new BodyDef();
		bodyDef1.type = BodyType.StaticBody;
		bodyDef1.position.set(width * (2f / 100f) * WORLD_TO_BOX / 2f, height * WORLD_TO_BOX / 2);
		Body left = mWorld.createBody(bodyDef1);

		bodyDef1.position.set(width * WORLD_TO_BOX - width * (2f / 100f) * WORLD_TO_BOX / 2f, height * WORLD_TO_BOX / 2);
		Body right = mWorld.createBody(bodyDef1);

		shape = new PolygonShape();
		shape.setAsBox(width * (2f / 100f) * WORLD_TO_BOX / 2f, height * WORLD_TO_BOX / 2);
		fixDef.shape = shape;

		left.createFixture(fixDef);
		right.createFixture(fixDef);
		shape.dispose();
	}
	
	private void createParticleStuff(float width, float height) {
		//First we create a new particlesystem and 
		//set the radius of each particle to 6 / 120 m (5 cm)
		ParticleSystemDef systemDef = new ParticleSystemDef();
		systemDef.radius = 6f * WORLD_TO_BOX;
		systemDef.dampingStrength = 0.2f;

		mParticleSystem = new ParticleSystem(mWorld, systemDef);
		mParticleSystem.setParticleDensity(1.3f);

		//Create a new particlegroupdefinition and set some properties
		//For the flags you can set more than only one
		mParticleGroupDef1 = new ParticleGroupDef();
		mParticleGroupDef1.color.set(1f, 0, 0, 1);
		mParticleGroupDef1.flags.add(ParticleType.b2_waterParticle);
		mParticleGroupDef1.position.set(width * (30f / 100f) * WORLD_TO_BOX, height * (80f / 100f) * WORLD_TO_BOX);

		//Create a shape, give it to the definition and
		//create the particlegroup in the particlesystem.
		//This will return you a ParticleGroup instance, but
		//we don't need it here, so we drop that.
		//The shape defines where the particles are created exactly
		//and how much are created
		PolygonShape parShape = new PolygonShape();
		parShape.setAsBox(width * (20f / 100f) * WORLD_TO_BOX / 2f, width * (20f / 100f) * WORLD_TO_BOX / 2f);
		mParticleGroupDef1.shape = parShape;
		mParticleSystem.createParticleGroup(mParticleGroupDef1);

		//Exactly the same! This is the second group with a different
		//color and shifted on the x-Axis
		mParticleGroupDef2 = new ParticleGroupDef();
		mParticleGroupDef2.shape = mParticleGroupDef1.shape;
		mParticleGroupDef2.flags = mParticleGroupDef1.flags;
		mParticleGroupDef2.groupFlags = mParticleGroupDef1.groupFlags;
		mParticleGroupDef2.position.set(width * (70f / 100f) * WORLD_TO_BOX, height * (80f / 100f) * WORLD_TO_BOX);
		mParticleGroupDef2.color.set(0.2f, 1f, 0.3f, 1);
		mParticleSystem.createParticleGroup(mParticleGroupDef2);

		//Here we create a new shape and we set a
		//linear velocity. This is used in createParticles1()
		//and createParticles2()
		CircleShape partShape = new CircleShape();
		partShape.setRadius(18.5f * WORLD_TO_BOX);

		mParticleGroupDef1.shape = partShape;
		mParticleGroupDef2.shape = partShape;

		mParticleGroupDef1.linearVelocity.set(new Vector2(0, -10f));
		mParticleGroupDef2.linearVelocity.set(new Vector2(0, -10f));
	}

	@Override
	public void render() {
		//First update our InputProcessor
		this.inputUpdate(Gdx.graphics.getDeltaTime());

		//Now do the same as every year...
		Gdx.gl.glClearColor(0, 0, 0, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);

		mWorld.step(Gdx.graphics.getDeltaTime(), 10, 6, mParticleSystem.calculateReasonableParticleIterations(Gdx.graphics.getDeltaTime()));

		batch.setProjectionMatrix(camera.combined);
		batch.begin();
		sprite.draw(batch);
		batch.end();

		//Get the combined matrix and scale it down to
		//our Box2D size
		Matrix4 cameraCombined = camera.combined.cpy();
		cameraCombined.scale(BOX_TO_WORLD, BOX_TO_WORLD, 1);

		//First render the particles and then the Box2D world
		mParticleDebugRenderer.render(mParticleSystem, BOX_TO_WORLD, cameraCombined);
		mDebugRenderer.render(mWorld, cameraCombined);
	}

	@Override
	public void dispose() {
		batch.dispose();
		texture.dispose();
		mParticleGroupDef1.shape.dispose();
		mWorld.dispose();
		mDebugRenderer.dispose();
	}

	public void createParticles1(float pX, float pY) {
		mParticleGroupDef1.position.set(pX * WORLD_TO_BOX, pY * WORLD_TO_BOX);
		mParticleSystem.createParticleGroup(mParticleGroupDef1);
		updateParticleCount();
		updateLog();
	}

	private void createParticles2(float pX, float pY) {
		mParticleGroupDef2.position.set(pX * WORLD_TO_BOX, pY * WORLD_TO_BOX);
		mParticleSystem.createParticleGroup(mParticleGroupDef2);
		updateParticleCount();
		updateLog();
	}

	private void updateParticleCount() {
		if(mParticleSystem.getParticleCount() > mParticleDebugRenderer.getMaxParticleNumber()) {
			mParticleDebugRenderer.setMaxParticleNumber(mParticleSystem.getParticleCount() + 1000);
		}
	}

	public void updateLog() {
		//Here we log the total particle count and the f/s
		Gdx.app.log("", "Total particles: " + mParticleSystem.getParticleCount() + " FPS: " + Gdx.graphics.getFramesPerSecond());
	}

	public void createCircleBody(float pX, float pY, float pRadius) {
		BodyDef bodyDef = new BodyDef();
		bodyDef.type = BodyType.DynamicBody;
		bodyDef.position.set(pX * WORLD_TO_BOX, pY * WORLD_TO_BOX);
		Body body = mWorld.createBody(bodyDef);

		CircleShape shape = new CircleShape();
		shape.setRadius(pRadius * WORLD_TO_BOX);

		FixtureDef fixDef = new FixtureDef();
		fixDef.density = 0.5f;
		fixDef.friction = 0.2f;
		fixDef.shape = shape;
		fixDef.restitution = 0.3f;

		body.createFixture(fixDef);
	}

	/* +++ Input +++ */ 

	private final float CREATE_PARTICLE_FREQUENCY = 50f;
	private float mTotDelta = 0;

	private boolean mCreateParticles = false;
	private float mPointerPosX = 0;
	private float mPointerPosY = 0;
	private int mCurrentButton = -1;

	@Override
	public boolean touchDown(int screenX, int screenY, int pointer, int button) {
		if(button != Input.Buttons.LEFT && button != Input.Buttons.RIGHT && button != Input.Buttons.MIDDLE) return false;

		if(button == Input.Buttons.MIDDLE) {
			this.createCircleBody(screenX, Gdx.graphics.getHeight() - screenY, MathUtils.random(10, 80));
			return true;
		}

		mCreateParticles = true;
		mCurrentButton = button;
		mTotDelta = 0;

		mPointerPosX = screenX;
		mPointerPosY = screenY;

		return true;
	}

	@Override
	public boolean touchUp(int screenX, int screenY, int pointer, int button) {
		if(button != Input.Buttons.LEFT && button != Input.Buttons.RIGHT && button != Input.Buttons.MIDDLE) return false;

		mCreateParticles = false;
		return true;
	}

	@Override
	public boolean touchDragged(int screenX, int screenY, int pointer) {
		if(!mCreateParticles) return false;

		mPointerPosX = screenX;
		mPointerPosY = screenY;

		return true;
	}

	@Override
	public boolean mouseMoved(int screenX, int screenY) {
		return false;
	}

	@Override
	public boolean scrolled(int amount) {
		return false;
	}

	public void inputUpdate(float pDelta) {
		if(!mCreateParticles) return;
		mTotDelta += pDelta;

		if(mTotDelta >= 1f / CREATE_PARTICLE_FREQUENCY) {
			mTotDelta -= 1 / CREATE_PARTICLE_FREQUENCY;
		} else return;

		float x = mPointerPosX + MathUtils.random(-Gdx.graphics.getWidth() * (1.5f/100f), Gdx.graphics.getWidth() * (1.5f/100f));
		float y = mPointerPosY + MathUtils.random(-Gdx.graphics.getHeight() * (1.5f/100f), Gdx.graphics.getHeight() * (1.5f/100f));

		if(mCurrentButton == Input.Buttons.LEFT) {
			this.createParticles1(x, Gdx.graphics.getHeight() - y);
		} else if(mCurrentButton == Input.Buttons.RIGHT) {
			this.createParticles2(x, Gdx.graphics.getHeight() - y);
		}
	}

	@Override
	public boolean keyDown(int keycode) {return false;}

	@Override
	public boolean keyUp(int keycode) {return false;}

	@Override
	public boolean keyTyped(char character) {return false;}

}

Clone this wiki locally