Android Open Source - ValentinesDayHearts Valentines Day Hearts






From Project

Back to project page ValentinesDayHearts.

License

The source code is released under:

Apache License

If you think the Android project ValentinesDayHearts listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.app;
// ww w  .  j a  v a2s .c o m
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.ParticleEffect;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.Filter;
import com.badlogic.gdx.physics.box2d.Fixture;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.QueryCallback;
import com.badlogic.gdx.physics.box2d.World;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.Input.Keys;

public class ValentinesDayHearts implements ApplicationListener, InputProcessor{
  final static float gravityCoef = 250f;
  final static int initialHeartCount = 10;
  final static float PhysWorldWidth = 80;
  final static float PhysWorldHeight = 48;
  
  final static float HeartMinSize = 12;
  final static float HeartMaxSize = 20;
  final static float HeartHeightWidthCoef = 1.0f;
  
  final static float[] HeartHues = new float[] 
      { 0, 8.0f / 360.0f, 16.0f / 360.0f, 320.0f / 360.0f, 333.0f / 360.0f  };
  
  private int WorldWidth;
  private int WorldHeight;
  private int RenderCoef = 20;
  
  List<String> heartWords;
  
  final static Vector2 CenterDisplacement = new Vector2(128.0f / 256.0f, 150.0f / 256.0f * HeartHeightWidthCoef);
  
  public final static Vector2 FontSizeHeartSizeCoef = new Vector2(0.046f, 0.05f);
      //new Vector2(0.13f, 0.13f);
  
  private OrthographicCamera camera;
  private World world;
  FixtureAtlas fixtureAtlas;
  private SpriteBatch spriteBatch;
  private BitmapFont font;
  
  private Texture backgroundTexture;
  
  private List<Texture> heartTextures;
  
  private Map<Body, Heart> hearts;
  
  private List<Heart> heartsToRemove;
  
  private Sound createHeartSound;
  private List<Sound> breakingSounds;
  
  private Vector3 testPoint = new Vector3();
  private Body hitBody = null;
  
  private Vector2 gravity;
  
  private int bgAngleStepCount;
  private int bgPosUpdateCount;
  
  private Vector2 bgPos;
  private float bgVel;
  private float bgDirCurrentAngle;  
  private float bgDirBeginAngle;  
  private float bgDirEndAngle;
  private int currentBgAngleStep;
  private int currentBgUpdate;
  
  private Vector2 tmpVector1 = new Vector2();
  private List<Heart> diedHearts = new ArrayList<Heart>();
  
  private int renderFreq = 0;
  
  @Override
  public void create() {    
    WorldWidth = Gdx.graphics.getWidth();
    WorldHeight = Gdx.graphics.getHeight();
    
    loadWords("data/words.txt");
    
    camera = new OrthographicCamera(PhysWorldWidth, PhysWorldHeight);
    camera.position.set(0, 0, 0);
  
    spriteBatch = new SpriteBatch();
    font = new BitmapFont(
        Gdx.files.internal("data/Jura-Medium.fnt"), 
        Gdx.files.internal("data/Jura-Medium.png"), false);
    font.setColor(Color.WHITE);
    
    fixtureAtlas = new FixtureAtlas(Gdx.files.internal("data/hearts.bin"));
    
    backgroundTexture = new Texture("data/background.png");

    hearts = new HashMap<Body, Heart>(initialHeartCount);
    
    prepareHeartsTextures(Gdx.files.internal("data/heart.png"));
    
    createHeartSound = Gdx.audio.newSound(Gdx.files.internal("data/beating.ogg"));
    breakingSounds = new ArrayList<Sound>();
    for (int i = 1; i <= 9; i++)
      breakingSounds.add(Gdx.audio.newSound(Gdx.files.internal("data/sound (" + i + ").ogg")));
    
    gravity = Gdx.app.getType() == ApplicationType.Android ? 
        new Vector2() : new Vector2(0, -gravityCoef / 2.5f);
    createPhysicsWorld();
    createHearts();

    heartsToRemove = new ArrayList<Heart>();
    
    bgPos = new Vector2(0.5f, 0.5f);
    currentBgAngleStep = 0;
    bgDirBeginAngle = (float)(Math.random() * Math.PI * 2);
    bgDirEndAngle = bgDirBeginAngle + (float)(Math.random() * Math.PI + Math.PI / 4); 
    bgDirCurrentAngle = bgDirBeginAngle;
    
    if (Gdx.app.getType() == ApplicationType.Android) {
      bgAngleStepCount = 20;
      bgPosUpdateCount = 4;
      bgVel = 0.003f;
    } else {
      bgAngleStepCount = 40;
      bgPosUpdateCount = 16;
      bgVel = 0.0005f;
    }
    
    Gdx.input.setInputProcessor(this);
  }
  
  protected void loadWords(String fileName)  {
    if ((fileName == null) || (fileName == ""))
            throw new IllegalArgumentException();
        
        String line;
        heartWords = new ArrayList<String>();
        try
        {    
            BufferedReader in = new BufferedReader(
                new InputStreamReader(Gdx.files.internal(fileName).read()));
            if (!in.ready())
                throw new IOException();
            while ((line = in.readLine()) != null) 
              heartWords.add(line);
            in.close();
        }
        catch (IOException e)
        {
            System.out.println(e);
        }
  }

  protected void prepareHeartsTextures(FileHandle fileHandle) {
    heartTextures = new ArrayList<Texture>(1);
    heartTextures.add(new Texture(fileHandle));
    float[][][] hslData = prepareHslData(fileHandle);
    heartTextures = new ArrayList<Texture>(HeartHues.length);
    
    for (int i = 0; i < HeartHues.length; i++) {
      heartTextures.add(generateHeartTexture(hslData, HeartHues[i]));
      heartTextures.get(i).setFilter(TextureFilter.Linear, TextureFilter.Linear);    
    }
  }
  
  protected float[][][] prepareHslData(FileHandle fileHandle) {
    Pixmap pixmap = new Pixmap(fileHandle);
    float[][][] result = new float[pixmap.getWidth()][pixmap.getHeight()][4];
    
    for (int i = 0; i < pixmap.getWidth(); i++)
      for (int j = 0; j < pixmap.getHeight(); j++) {
        int color = pixmap.getPixel(i, j);
        float r = (float)((color >> 24) & 0xFF) / 255.0f;
        float g = (float)((color >> 16) & 0xFF) / 255.0f;
        float b = (float)((color >> 8) & 0xFF) / 255.0f;
        float a = (float)(color & 0xFF) / 255.0f;
        result[i][j] = ColorUtils.RgbToHsl(r, g, b, a);
      }
    
    return result;
  }
  
  protected Texture generateHeartTexture(float[][][] hslData, float newHue) {
    Pixmap pixmap = new Pixmap(hslData.length, hslData[0].length, Format.RGBA8888);

    float[] rgba;
    for (int i = 0; i < hslData.length; i++)
      for (int j = 0; j < hslData[0].length; j++) {
        rgba = ColorUtils.HslToRgb(newHue, hslData[i][j][1], hslData[i][j][2], hslData[i][j][3]);
        pixmap.setColor(rgba[0], rgba[1], rgba[2], rgba[3]);
        pixmap.drawPixel(i, j);
      }
    
    Texture result = new Texture(pixmap);
    return result;
  }
  
  @Override
  public void dispose() {
    for (int i = 0; i < heartTextures.size(); i++)
      heartTextures.get(i).dispose();
    spriteBatch.dispose();
    world.dispose();
  }

  @Override
  public void pause() {

  }  
  
  @Override
  public void render() {
    updatePhysics();
    updateBackground();    
    updateSprites();      
    updateParticles();
    if (renderFreq++ % RenderCoef == 0) {
      refresh();    
      renderBackground();
      renderHearts();    
      renderFonts();
      renderParticles();
      renderFreq = 0;
    }
  }
  
  protected void updatePhysics() {
    Body heartBody;
    
    for (Heart heart : hearts.values())   {
      heartBody = heart.Body;
      
      if (Gdx.input.isKeyPressed(Keys.UP))
        heartBody.applyForceToCenter(0, 20000);
      
      if (Gdx.input.isKeyPressed(Keys.DOWN))
        heartBody.applyForceToCenter(0, -20000);
      
      if (Gdx.input.isKeyPressed(Keys.LEFT))
        heartBody.applyForceToCenter(-20000, 0);
      
      if (Gdx.input.isKeyPressed(Keys.RIGHT))
        heartBody.applyForceToCenter(20000, 0);
      
      if (Gdx.input.isKeyPressed(Keys.Q))
        heartBody.applyTorque(10000);
      
      if (Gdx.input.isKeyPressed(Keys.W))
        heartBody.applyTorque(-10000);
      
      break;
    }
    
    if (Gdx.app.getType() == ApplicationType.Android) {
      gravity.x = -Gdx.input.getPitch() / 90.0f;
      gravity.y = Gdx.input.getRoll() / 90.0f;
      gravity.mul(gravityCoef);
      world.setGravity(gravity);      
    }
    world.step(Gdx.app.getGraphics().getDeltaTime(), 6, 6);
    //world.step(Gdx.app.getGraphics().getDeltaTime(), 8, 3);
  }
  
  protected void updateBackground() {
    if (currentBgUpdate++ == bgPosUpdateCount) {
      
      bgPos.add(
        bgVel * (float)Math.cos(bgDirCurrentAngle), 
        bgVel * (float)Math.sin(bgDirCurrentAngle));
      
      bgDirCurrentAngle += (bgDirEndAngle - bgDirBeginAngle) / bgAngleStepCount;
      currentBgAngleStep++;
      
      if (currentBgAngleStep == bgAngleStepCount) {
        currentBgAngleStep = 0;
        bgDirBeginAngle = (float)(Math.random() * Math.PI * 2);
        bgDirEndAngle = bgDirBeginAngle + (float)(Math.random() * Math.PI * 2 - Math.PI); 
      }
      
      float textureWidth = (float)WorldWidth / backgroundTexture.getWidth();
      float textureHeight = (float)WorldHeight / backgroundTexture.getHeight();
      
      if (bgPos.x - textureWidth / 2 < 0 || bgPos.y - textureHeight / 2 < 0 ||
        bgPos.x + textureWidth / 2 > 1 || bgPos.y + textureHeight / 2 > 1)
          bgDirCurrentAngle -= (float)Math.PI;
      
      currentBgUpdate = 0;
    }
  }
  
  protected void updateSprites() {
    Body heartBody;
    
    for (Heart heart : hearts.values())   {
      heartBody = heart.Body;
      Vector2 pos = heartBody.getPosition();
      float angleDeg = heartBody.getAngle() * MathUtils.radiansToDegrees;
  
      heart.Sprite.setPosition(pos.x, pos.y);
      heart.Sprite.setRotation(angleDeg);
    }
  }

  protected void updateParticles() {  
    for (Heart heart : heartsToRemove) {
      if (heart.ParticleEffect.isComplete())
        diedHearts.add(heart);
    }
    for (Heart heart : diedHearts)
      heartsToRemove.remove(heart);
    diedHearts.clear();
  }
  
  protected void refresh() {
    GL10 gl = Gdx.app.getGraphics().getGL10();
    gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
  }
  
  protected void renderBackground() {
    spriteBatch.getProjectionMatrix().setToOrtho2D(0, 0,
        WorldWidth, WorldHeight);
    spriteBatch.begin();
    
    float textureWidth = (float)WorldWidth / backgroundTexture.getWidth();
    float textureHeight = (float)WorldHeight / backgroundTexture.getHeight();
    
    spriteBatch.draw(backgroundTexture, 0, 0, WorldWidth, WorldHeight, 
        bgPos.x - textureWidth / 2, bgPos.y - textureHeight / 2,
        bgPos.x + textureWidth / 2, bgPos.y + textureHeight / 2);
    spriteBatch.end();
    /*
    spriteBatch.getProjectionMatrix().setToOrtho2D(0, 0,
        WorldWidth, WorldHeight);
    spriteBatch.begin();
    spriteBatch.draw(backgroundTexture, 0, 0, 0, 0, 800, 480);
    spriteBatch.end();*/
  }
  
  protected void renderHearts() {
    spriteBatch.setProjectionMatrix(camera.combined);
    spriteBatch.begin();    
    for (Heart heart : hearts.values())      
      heart.Sprite.draw(spriteBatch);
    spriteBatch.end();
  }
  
  protected void renderFonts() {  
    for (Heart heart : hearts.values())   {
      Body body = heart.Body;
      Vector2 position = body.getPosition();
      tmpVector1.set((position.x / PhysWorldWidth + 0.5f) * WorldWidth, 
               (position.y / PhysWorldHeight + 0.5f) * WorldHeight);
      
      Matrix4 projection = spriteBatch.getProjectionMatrix();
      projection.setToOrtho2D(0, 0, WorldWidth, WorldHeight);
      projection.translate(tmpVector1.x, tmpVector1.y, 0);
      projection.rotate(0, 0, 1, body.getAngle() / (float)Math.PI * 180);
      projection.translate(-tmpVector1.x, -tmpVector1.y, 0);
      
      Vector2 stringSize = heart.getStringSize();
      tmpVector1.add(heart.Size.x / PhysWorldWidth * WorldWidth * CenterDisplacement.x 
                 - stringSize.x * 0.5f, 
                 heart.Size.y /  PhysWorldHeight * WorldHeight * CenterDisplacement.y
                 + stringSize.y);  
      
      spriteBatch.begin();
      font.setScale(heart.Size.x * FontSizeHeartSizeCoef.x, heart.Size.y * FontSizeHeartSizeCoef.y);
      font.draw(spriteBatch, heart.String, tmpVector1.x, tmpVector1.y);
      font.setScale(1, 1);
      spriteBatch.end();
    }
  }

  protected void renderParticles() {  
    float delta = Gdx.graphics.getDeltaTime();
    for (Heart heart : heartsToRemove) {
      
      tmpVector1.set(heart.ParticleEffect.getEmitters().get(0).getX(), 
               heart.ParticleEffect.getEmitters().get(0).getY());
      
      tmpVector1.sub(heart.Size.x / PhysWorldWidth * WorldWidth * CenterDisplacement.x,
                 heart.Size.y / PhysWorldHeight * WorldHeight * CenterDisplacement.y);
      
      Matrix4 projection = spriteBatch.getProjectionMatrix();
      projection.setToOrtho2D(0, 0, WorldWidth, WorldHeight);
      
      projection.translate(tmpVector1.x, tmpVector1.y, 0);
      projection.rotate(0, 0, 1, heart.getDeathAngle()  / (float)Math.PI * 180);
      projection.translate(-tmpVector1.x, -tmpVector1.y, 0);
      
      spriteBatch.begin();
      
      heart.ParticleEffect.draw(spriteBatch, delta);  
      
      spriteBatch.end();
    }
  }
  
  @Override
  public void resize(int arg0, int arg1) {
    // TODO Auto-generated method stub
  }

  @Override
  public void resume() {
    // TODO Auto-generated method stub
    
  }
  
  private void createHearts() {    
    for (int i = 0; i < initialHeartCount; i++)
      addHeart();
  }
  
  private void createPhysicsWorld () {
    world = new World(gravity, true);
    
    float wallWidth = 10;
    
    PolygonShape groundPoly;
    BodyDef groundBodyDef;
    Body groundBody;
    
    groundPoly = new PolygonShape();
    groundPoly.setAsBox(PhysWorldWidth + wallWidth, wallWidth, new Vector2(0, -PhysWorldHeight / 2 - wallWidth), 0);
    groundBodyDef = new BodyDef();
    groundBodyDef.type = BodyType.StaticBody;
    groundBody = world.createBody(groundBodyDef);    
    groundBody.createFixture(groundPoly, 10);
    groundPoly.dispose();
    
    groundPoly = new PolygonShape();
    groundPoly.setAsBox(PhysWorldWidth + wallWidth, wallWidth, new Vector2(0, +PhysWorldHeight / 2 + wallWidth), 0);
    groundBodyDef = new BodyDef();
    groundBodyDef.type = BodyType.StaticBody;
    groundBody = world.createBody(groundBodyDef);    
    groundBody.createFixture(groundPoly, 10);
    groundPoly.dispose();
    
    groundPoly = new PolygonShape();
    groundPoly.setAsBox(wallWidth, PhysWorldHeight + wallWidth, new Vector2(-PhysWorldWidth / 2 - wallWidth, 0), 0);
    groundBodyDef = new BodyDef();
    groundBodyDef.type = BodyType.StaticBody;
    groundBody = world.createBody(groundBodyDef);    
    groundBody.createFixture(groundPoly, 10);
    groundPoly.dispose();
    
    groundPoly = new PolygonShape();
    groundPoly.setAsBox(wallWidth, PhysWorldHeight + wallWidth, new Vector2(+PhysWorldWidth / 2 + wallWidth, 0), 0);
    groundBodyDef = new BodyDef();
    groundBodyDef.type = BodyType.StaticBody;
    groundBody = world.createBody(groundBodyDef);    
    groundBody.createFixture(groundPoly, 10);
    groundPoly.dispose();
  }
  
  @Override
  public boolean keyDown(int arg0) {
    return false;
  }

  @Override
  public boolean keyTyped(char arg0) {
    // TODO Auto-generated method stub
    return false;
  }

  @Override
  public boolean keyUp(int arg0) {
    // TODO Auto-generated method stub
    return false;
  }

  @Override
  public boolean scrolled(int arg0) {
    // TODO Auto-generated method stub
    return false;
  }

  QueryCallback callback = new QueryCallback() {
    @Override
    public boolean reportFixture (Fixture fixture) {
      // if the hit point is inside the fixture of the body
      // we report it
      if (fixture.testPoint(testPoint.x, testPoint.y)) {
        hitBody = fixture.getBody();
        return false;
      } else
        return true;
    }
  };
  
  @Override
  public boolean touchDown(int x, int y, int pointer, int button) {
    camera.unproject(testPoint.set(x, y, 0));
    
    hitBody = null;
    world.QueryAABB(callback, testPoint.x - 0.0001f, testPoint.y - 0.0001f, testPoint.x + 0.0001f, testPoint.y + 0.0001f);

    // TODO: Repair;
    // if (hitBody == groundBody) hitBody = null;

    if (hitBody != null && hitBody.getType() == BodyType.KinematicBody) 
      return false;

    if (hitBody != null) {
      // Remove heart.
      if (hearts.containsKey(hitBody)) {
        Heart heart = hearts.get(hitBody);
        heart.destroy();
        
        heart.BreakSound.play(1);
        
        ParticleEffect effect = heart.ParticleEffect;
        Vector2 bodyPosition = heart.getDeathPos();
        effect.setPosition(((bodyPosition.x + heart.Size.x * 0.5f) / PhysWorldWidth  + 0.5f) * WorldWidth, 
                   ((bodyPosition.y + heart.Size.y * 0.5f) / PhysWorldHeight + 0.5f) * WorldHeight);
        effect.start();
        heartsToRemove.add(heart);
        
        world.destroyBody(hitBody);        
        hearts.remove(hitBody);    

        Gdx.input.vibrate(200);

      }
    } else {
      // Add heart.      
      addHeart(((float)x / WorldWidth - 0.5f) * PhysWorldWidth, 
          (-(float)y / WorldHeight + 0.5f) * PhysWorldHeight);
      
      createHeartSound.play(1);
    }
    
    return false;
  }
  
  private void addHeart() {
    addHeart(-1, -1);
  }
  
  private void addHeart(float x, float y) {
    BodyDef heartBodyDef = new BodyDef();
    heartBodyDef.type = BodyType.DynamicBody;
    
    Filter filter = new Filter();
    filter.groupIndex = 0;
    
    float newWidth = (float)Math.random() * (HeartMaxSize - HeartMinSize) + HeartMinSize;
    float newHeight = newWidth * HeartHeightWidthCoef;
      
    Vector2 size = new Vector2(newWidth, newHeight);
    
    heartBodyDef.angle = (float)(Math.atan2(gravity.y, gravity.x) + Math.PI / 2);
    float cosa = (float)Math.cos(heartBodyDef.angle);
    float sina = (float)Math.sin(heartBodyDef.angle);
    float dx = newWidth / 2;
    float dy = newHeight / 2;
    float x1 = dx * cosa - dy * sina;
    float y1 = dx * sina + dy * cosa;
    
    if (x == -1) {
      heartBodyDef.position.x = 
        (float)Math.random() * (PhysWorldWidth - newWidth) - (PhysWorldWidth / 2 - newHeight / 2);
      heartBodyDef.position.y = 
        (float)Math.random() * (PhysWorldHeight - newHeight) - (PhysWorldHeight / 2 - newHeight / 2);
    } else {
      heartBodyDef.position.x = x;
      heartBodyDef.position.y = y;
    }    
    
    heartBodyDef.position.x -= x1;
    heartBodyDef.position.y -= y1;
    
    Body body = world.createBody(heartBodyDef);
    
    fixtureAtlas.createFixtures(body, "heart.png", newWidth, newHeight);
      
    for (int j = 0; j < body.getFixtureList().size(); j++) {
      Fixture fixture = body.getFixtureList().get(j);
        
      fixture.setFilterData(filter);
      fixture.setFriction(0.75f);
      fixture.setDensity(1.0f);
      fixture.setRestitution(0.4f);
    }
      
    body.resetMassData();
    
    Sprite sprite = new Sprite(heartTextures.get((int)(Math.random() * heartTextures.size())));      
    sprite.setSize(newWidth, newHeight);
    sprite.setOrigin(0, 0);
    Vector2 heartPosition = body.getPosition();
    sprite.setPosition(heartPosition.x, heartPosition.y);
    float angleDeg = body.getAngle() * MathUtils.radiansToDegrees;
    sprite.setRotation(angleDeg);
    
    ParticleEffect effect = new ParticleEffect();
    effect.load(Gdx.files.internal("data/destroy.p"), Gdx.files.internal("data"));
    
    Sound breakingSound = breakingSounds.get((int)(Math.random() * breakingSounds.size()));
    
    hearts.put(body, new Heart(body, sprite, size, heartWords.get((int)(Math.random() * heartWords.size())), 
        font, effect, breakingSound));
  }
  
  @Override
  public boolean touchDragged(int arg0, int arg1, int arg2) {
    // TODO Auto-generated method stub
    return false;
  }

  @Override
  public boolean touchMoved(int arg0, int arg1) {
    // TODO Auto-generated method stub
    return false;
  }

  @Override
  public boolean touchUp(int arg0, int arg1, int arg2, int arg3) {
    // TODO Auto-generated method stub
    return false;
  }

}




Java Source Code List

com.app.ColorUtils.java
com.app.DesktopStarter.java
com.app.FixtureAtlas.java
com.app.Heart.java
com.app.PosAndAngle.java
com.app.ValentinesDayHeartsActivity.java
com.app.ValentinesDayHearts.java