In my previous blog post, I was talking about how I failed to extend the Rectangle class, and had to step back and learn a few more basics. Now approximately 8 hours later, a lot of pages, videos and other things (completely unrelated to Java), I’ve managed to get it working.

For me this a huge step in the right direction, and when the penny finally hit, it didn’t take more than a few minutes to actually change the code to reflect what I wanted to do.

Again, the original spawnRaindrop() code:

1
2
3
4
5
6
7
8
9
private void spawnRaindrop() {
    Rectangle raindrop = new Rectangle();
    raindrop.x = MathUtils.random(0, 800-48);
    raindrop.y = 480;
    raindrop.width = 48;
    raindrop.height = 48;
    raindrops.add(raindrop)
    lastDropTime = TimeUtils.nanoTime();
}
private void spawnRaindrop() {
	Rectangle raindrop = new Rectangle();
	raindrop.x = MathUtils.random(0, 800-48);
	raindrop.y = 480;
	raindrop.width = 48;
	raindrop.height = 48;
	raindrops.add(raindrop)
	lastDropTime = TimeUtils.nanoTime();
}

 

And the modified version

1
2
3
4
5
6
7
8
9
10
private void spawnRaindrop() {
    Raindrop raindrop = new Raindrop();
    raindrop.x = MathUtils.random(0, 800-48);
    raindrop.y = 480;
    raindrop.width = 48;
    raindrop.height = 48;
    raindrop.speed = MathUtils.random(50,200);
    raindrops.add(raindrop);
    lastDropTime = TimeUtils.nanoTime();
}
private void spawnRaindrop() {
	Raindrop raindrop = new Raindrop();
	raindrop.x = MathUtils.random(0, 800-48);
	raindrop.y = 480;
	raindrop.width = 48;
	raindrop.height = 48;
	raindrop.speed = MathUtils.random(50,200);
	raindrops.add(raindrop);
	lastDropTime = TimeUtils.nanoTime();
}

 

The Raindrop class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.badlogic.drop;
 
import com.badlogic.gdx.math.Rectangle;
 
public class Raindrop extends Rectangle {
    float speed;
 
    public Raindrop() {
        super();
    }
 
    public Raindrop(float x, float y, float width, float height, float speed) {
        super(x, y, width, height);
        this.speed = speed;
    }
}
package com.badlogic.drop;

import com.badlogic.gdx.math.Rectangle;

public class Raindrop extends Rectangle {
	float speed;

	public Raindrop() {
		super();
	}

	public Raindrop(float x, float y, float width, float height, float speed) {
		super(x, y, width, height);
		this.speed = speed;
	}
}

 

And the entire Drop.java code with the new shiny Raindrop class in action (I took the liberty of copying directly from the tutorial page since I had mangled the original code somewhat awful)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package com.badlogic.drop;
 
import java.util.Iterator;
 
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;
 
public class Drop implements ApplicationListener {
   Texture dropImage;
   Texture bucketImage;
   Sound dropSound;
   Music rainMusic;
   SpriteBatch batch;
   OrthographicCamera camera;
   Rectangle bucket;
   Array<Raindrop> raindrops;
   long lastDropTime;
 
   @Override
   public void create() {
      // load the images for the droplet and the bucket, 48x48 pixels each
      dropImage = new Texture(Gdx.files.internal("droplet.png"));
      bucketImage = new Texture(Gdx.files.internal("bucket.png"));
 
      // load the drop sound effect and the rain background "music"
      dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
      rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
 
      // start the playback of the background music immediately
      rainMusic.setLooping(true);
      rainMusic.play();
 
      // create the camera and the SpriteBatch
      camera = new OrthographicCamera();
      camera.setToOrtho(false, 800, 480);
      batch = new SpriteBatch();
 
      // create a Rectangle to logically represent the bucket
      bucket = new Rectangle();
      bucket.x = 800 / 2 - 48 / 2; // center the bucket horizontally
      bucket.y = 20; // bottom left corner of the bucket is 20 pixels above the bottom screen edge
      bucket.width = 48;
      bucket.height = 48;
 
      // create the raindrops array and spawn the first raindrop
      raindrops = new Array<Raindrop>();
      spawnRaindrop();
   }
 
   private void spawnRaindrop() {
      Raindrop raindrop = new Raindrop();
      raindrop.x = MathUtils.random(0, 800-48);
      raindrop.y = 480;
      raindrop.width = 48;
      raindrop.height = 48;
      raindrop.speed = MathUtils.random(0,50);
      raindrops.add(raindrop);
      lastDropTime = TimeUtils.nanoTime();
   }
 
   @Override
   public void render() {
      // clear the screen with a dark blue color. The
      // arguments to glClearColor are the red, green
      // blue and alpha component in the range [0,1]
      // of the color to be used to clear the screen.
      Gdx.gl.glClearColor(0, 0, 0.2f, 1);
      Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
 
      // tell the camera to update its matrices.
      camera.update();
 
      // tell the SpriteBatch to render in the
      // coordinate system specified by the camera.
      batch.setProjectionMatrix(camera.combined);
 
      // begin a new batch and draw the bucket and
      // all drops
      batch.begin();
      batch.draw(bucketImage, bucket.x, bucket.y);
      for(Rectangle raindrop: raindrops) {
         batch.draw(dropImage, raindrop.x, raindrop.y);
      }
      batch.end();
 
      // process user input
      if(Gdx.input.isTouched()) {
         Vector3 touchPos = new Vector3();
         touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
         camera.unproject(touchPos);
         bucket.x = touchPos.x - 48 / 2;
      }
      if(Gdx.input.isKeyPressed(Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime();
      if(Gdx.input.isKeyPressed(Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.getDeltaTime();
 
      // make sure the bucket stays within the screen bounds
      if(bucket.x < 0) bucket.x = 0;
      if(bucket.x > 800 - 48) bucket.x = 800 - 48;
 
      // check if we need to create a new raindrop
      if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRaindrop();
 
      // move the raindrops, remove any that are beneath the bottom edge of
      // the screen or that hit the bucket. In the later case we play back
      // a sound effect as well.
      Iterator<Raindrop> iter = raindrops.iterator();
      while(iter.hasNext()) {
         Raindrop raindrop = iter.next();
         raindrop.y -= raindrop.speed * Gdx.graphics.getDeltaTime();
         if(raindrop.y + 48 < 0) iter.remove();
         if(raindrop.overlaps(bucket)) {
            dropSound.play();
            iter.remove();
         }
      }
   }
 
   @Override
   public void dispose() {
      // dispose of all the native resources
      dropImage.dispose();
      bucketImage.dispose();
      dropSound.dispose();
      rainMusic.dispose();
      batch.dispose();
   }
 
   @Override
   public void resize(int width, int height) {
   }
 
   @Override
   public void pause() {
   }
 
   @Override
   public void resume() {
   }
}
package com.badlogic.drop;

import java.util.Iterator;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.TimeUtils;

public class Drop implements ApplicationListener {
   Texture dropImage;
   Texture bucketImage;
   Sound dropSound;
   Music rainMusic;
   SpriteBatch batch;
   OrthographicCamera camera;
   Rectangle bucket;
   Array<Raindrop> raindrops;
   long lastDropTime;

   @Override
   public void create() {
      // load the images for the droplet and the bucket, 48x48 pixels each
      dropImage = new Texture(Gdx.files.internal("droplet.png"));
      bucketImage = new Texture(Gdx.files.internal("bucket.png"));

      // load the drop sound effect and the rain background "music"
      dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
      rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));

      // start the playback of the background music immediately
      rainMusic.setLooping(true);
      rainMusic.play();

      // create the camera and the SpriteBatch
      camera = new OrthographicCamera();
      camera.setToOrtho(false, 800, 480);
      batch = new SpriteBatch();

      // create a Rectangle to logically represent the bucket
      bucket = new Rectangle();
      bucket.x = 800 / 2 - 48 / 2; // center the bucket horizontally
      bucket.y = 20; // bottom left corner of the bucket is 20 pixels above the bottom screen edge
      bucket.width = 48;
      bucket.height = 48;

      // create the raindrops array and spawn the first raindrop
      raindrops = new Array<Raindrop>();
      spawnRaindrop();
   }

   private void spawnRaindrop() {
      Raindrop raindrop = new Raindrop();
      raindrop.x = MathUtils.random(0, 800-48);
      raindrop.y = 480;
      raindrop.width = 48;
      raindrop.height = 48;
      raindrop.speed = MathUtils.random(0,50);
      raindrops.add(raindrop);
      lastDropTime = TimeUtils.nanoTime();
   }

   @Override
   public void render() {
      // clear the screen with a dark blue color. The
      // arguments to glClearColor are the red, green
      // blue and alpha component in the range [0,1]
      // of the color to be used to clear the screen.
      Gdx.gl.glClearColor(0, 0, 0.2f, 1);
      Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);

      // tell the camera to update its matrices.
      camera.update();

      // tell the SpriteBatch to render in the
      // coordinate system specified by the camera.
      batch.setProjectionMatrix(camera.combined);

      // begin a new batch and draw the bucket and
      // all drops
      batch.begin();
      batch.draw(bucketImage, bucket.x, bucket.y);
      for(Rectangle raindrop: raindrops) {
         batch.draw(dropImage, raindrop.x, raindrop.y);
      }
      batch.end();

      // process user input
      if(Gdx.input.isTouched()) {
         Vector3 touchPos = new Vector3();
         touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
         camera.unproject(touchPos);
         bucket.x = touchPos.x - 48 / 2;
      }
      if(Gdx.input.isKeyPressed(Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime();
      if(Gdx.input.isKeyPressed(Keys.RIGHT)) bucket.x += 200 * Gdx.graphics.getDeltaTime();

      // make sure the bucket stays within the screen bounds
      if(bucket.x < 0) bucket.x = 0;
      if(bucket.x > 800 - 48) bucket.x = 800 - 48;

      // check if we need to create a new raindrop
      if(TimeUtils.nanoTime() - lastDropTime > 1000000000) spawnRaindrop();

      // move the raindrops, remove any that are beneath the bottom edge of
      // the screen or that hit the bucket. In the later case we play back
      // a sound effect as well.
      Iterator<Raindrop> iter = raindrops.iterator();
      while(iter.hasNext()) {
         Raindrop raindrop = iter.next();
         raindrop.y -= raindrop.speed * Gdx.graphics.getDeltaTime();
         if(raindrop.y + 48 < 0) iter.remove();
         if(raindrop.overlaps(bucket)) {
            dropSound.play();
            iter.remove();
         }
      }
   }

   @Override
   public void dispose() {
      // dispose of all the native resources
      dropImage.dispose();
      bucketImage.dispose();
      dropSound.dispose();
      rainMusic.dispose();
      batch.dispose();
   }

   @Override
   public void resize(int width, int height) {
   }

   @Override
   public void pause() {
   }

   @Override
   public void resume() {
   }
}

I hope this is helpful to someone else, if not at least I have some place to find this when I wake up and have forgotten all about it.

Drop 1
Me 1

Your move Java!

On another note, I really need to add a couple of things to this wordpress blog. Color coded syntax and an easier way to add the code would be a start. Also I’ve been asked to add some syndication for the entire site, so I’ll look into that sometime soon as well.