I am trying to make a Model from a set tiles with libgdx's MeshPartBuilder and I noticed that when I'm generating the Model the memory usage goes up by ~200MB and doesn't come down, but when I'm doing a Thread.sleep(), It stays at a normal level.
Here is the method I call to prepare the model.
public void updateModel() {
System.out.println("Updating model for chuck " + chunkUUID.toString());
ModelBuilder mdlBuilder = new ModelBuilder();
mdlBuilder.begin();
MeshPartBuilder meshBuilder = null;
for(int y = 0; y < size[1]; y++) {
for(int x = 0; x < size[0]; x++) {
if(tiles[x][y] == null) {
System.out.println(x+":"+y+" is null");
continue;
}
tiles[x][y].getModel(chunkUUID.toString() + "." + x + ":" + y, this, mdlBuilder, meshBuilder, modelOffset[0] + x, modelOffset[1],
modelOffset[2] + y);
}
System.out.println(y);
try {
Thread.sleep(250L);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
model = mdlBuilder.end();
iModel = new ModelInstance(model);
}
And here is the getModel method that is called in the loop.
public void getModel(String partId, Chunk chunk, ModelBuilder mdlBuilder, MeshPartBuilder mshBuilder, float offsetX, float offsetY, float offsetZ) {
int counter = 0;
mshBuilder = mdlBuilder.part(partId+"."+counter, GL20.GL_TRIANGLES, vrtxAttr, material);
mshBuilder.rect(
offsetX, offsetY+cornersHeight[0], offsetZ,
offsetX, offsetY+cornersHeight[1], offsetZ+1F,
offsetX+1F, offsetY+cornersHeight[2], offsetZ+1F,
offsetX+1F, offsetY+cornersHeight[3], offsetZ,
0,1,0);
}
Changing the material doesn't seem to help and if I reduce the sleep length to 10ms, the memory usage goes up again.
However replacing the Thread.sleep part with System.gc() helps but doesn't have the same effect (20-40MB more).
Does anyone know why this is hapenning and if there is a solution ?,
Related
I am writing Java program which allows me to get the geometric center of random-generated 2-dimensional points. I want the calculations to be done by custom number of threads. I want to suspend/continue calculations at any time. Unfortunately, my code doesn't work, It seems like run() is never executed. Here is what I got:
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
System.out.println("running... " + Thread.currentThread().getName());
PointInterface p = pg.getPoint(); // getting random point(x,y)
pointCount++;
int[] pos = p.getPositions(); // getting (x,y)
System.out.println(pos[0] + ", " + pos[1] + " k");
sumxy[0] += pos[0];
sumxy[1] += pos[1];
geometricCenter[0] = (double) sumxy[0] / pointCount;
geometricCenter[1] = (double) sumxy[1] / pointCount;
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("exception caught in run()");
return;
}
}
}
Setting number of threads:
public void setNumberOfThreads(int threads) {
threadsList.clear();
for (int i = 0; i < threads; i++) {
threadsList.add(new Thread());
}
}
Starting the calculations:
public void start() {
try {
for (Thread t : threadsList) {
t.start();
}
} catch (Exception e) {
System.out.println("start() exception caught");
}
}
Suspending calculations:
public void suspendCalculations() {
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
System.out.println("SuspendCalculations() exception caught");
}
}
Resuming calculations:
I don't exactly know what I'm supposed to do here. Should I create new set of Threads like that?
public void continueCalculations() {
int x = threadsList.size();
threadsList.clear();
for (int i = 0; i < x; i++) {
threadsList.add(new Thread());
threadsList.get(i).start();
}
}
How I run my program:
public static void main(String[] args) {
ParallelCalculations pc = new ParallelCalculations(); // My main class object where run() and all the methods above are declared
PointGenerator g = new PointGenerator(); // class that allows me to generate points
PointInterface a = g.getPoint(); // getting random point
pc.setNumberOfThreads(3);
pc.setPointGenerator(g);
pc.start();
pc.suspendCalculations();
System.out.println(pc.getGeometricCenter()[0] + ", " + pc.getGeometricCenter()[1]);
pc.continueCalculations();
pc.suspendCalculations();
System.out.println(pc.getGeometricCenter()[0] + ", " + pc.getGeometricCenter()[1]);
}
If needed:
Point:
class Point {
public static final int MAX_POSITION = 16;
private int[] positions = new int[2];
Point(int _x, int _y) {
this.positions[0] = _x;
this.positions[1] = _y;
}
public int[] getPositions() {
return positions;
}
}
Point Generator:
class PointGenerator {
private int x = (int) (Math.random() * (Point.MAX_POSITION + 1));
private int y = (int) (Math.random() * (Point.MAX_POSITION + 1));
public PointInterface getPoint() { // Can be called by many Threads at the same time.
return new Point(x, y);
}
}
Your run() should be executed and should do it's thing.
Though there is a far bigger cause for random behaviour in this code: All the threads write to sumxy, geometricCenter and pointCount at he same time without any syncronisation-locks, causing more or less random behaviour. You at least need to implement some kind of synchronisation to prevent simultanious writes.
Maybe start here (Java synchonized tutorial by Oracle) if you have no clue.
But simply adding synchronisation to everything will probably just make it slower than a single thread, you will need some kind of buffer for each thread to work independently and than collect the results when they are suspended.
And now general problems:
A) Your suspendCalculations() doesn't do anything (for 1200ms to be percise). To break the calcualtion you would need to interrupt all the worker-threads since they terminate upon interruption. Call threadsList.get(i).Interrupt() to do so.
B) If you want to to be able to change the number of threads while suspended, this is a way to go. If this is not necessarry, it would be more efficient to create a constant
public static final Object LOCK = new Object();
make all the threads LOCK.wait() on that object, so resuming them is just a call to LOCK.notifyAll().
C) Instead of using your own implementaion of Point you can use java.awt.Point.
D) Returning the coordinates of a point simply in an array is really bad for readability of your code, java.awt.Point has getX() and getY() functions.
I am asking this today for the purpose of comparing the two.
What I am trying to do is make a bot that is able to click the space-bar at a certain time.
Anyway, let me show what I am trying to compare.
Robot robot = new Robot();
This will be out Robot. (java.awt.Robot;)
My question is, which is faster for the purpose of try to constantly read one (or more) pixels on the screen at once?
My current (in progress) programs that are the same with the exception of one using
Robot.getPixelColor(int, int)
and the other uses
BufferedImage image Robot.createScreenCapture(Rectangle)
imaage.getRGB
These are being run at many times a second, and I am simply trying to figure out which is more consistently faster. They both seem random at the speed at which they operate, so any advice is appreciated.
In case you wish for an example to put these into, here is the setup for it.
Here is the example using Robot.getPixelColor(int, int)
Robot robot = new Robot();
Rectangle rect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
Point prompt = new Point(913, 506, robot);
BufferedImage screenImage = robot.createScreenCapture(rect);
for(int i = 0; i < 360; i++) {
zones.add(new Point(radius * Math.cos(Math.toRadians(i)) + centerX, radius * Math.sin(Math.toRadians(i)) + centerY, robot));
}
System.out.println("Resolution: " + width + " * " + height/* + " " + robot.getPixelColor(20, 20)/* + " " + screenImage.getRGB((int) prompt.getX(), (int) prompt.getY())*/);
while(true) {
tempZone = null;
while(prompt.hasColor(promptColor)) {
System.out.println("Found prompt, trying to find zone.");
int count = 0;
for(int i = 0; i < zones.size(); i++) {
if(zones.get(i).hasColor(zoneColor)){
tempZone = zones.get(i);
break;
}
}
if(tempZone != null) {
System.out.println("Found zone at: " + tempZone.getX() + ", " + tempZone.getY() + ". Looking for ticker...");
tempZone.whenHasColor(zoneColor);
}else{
System.out.println("Unable to find zone.");
}
while(prompt.hasColor(promptColor)) {}
}
}
With its Point Class:
public class Point {
private double x;
private double y;
private final Robot robot;
public Point(double x, double y, Robot rob) {
this.x = x;
this.y = y;
this.robot = rob;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public boolean hasColor(Color color) {
return (robot.getPixelColor((int )x, (int) y).equals(color));
}
public void whenHasColor(Color color) throws IOException {
Rectangle rect = new Rectangle(889 + (int) x, 449 + (int) y, 1, 1);
while(hasColor(color)) {
//Nothing
}
BufferedImage cap = robot.createScreenCapture(rect);
if(Math.abs(Math.abs(cap.getRGB(0, 0)) - 15657182) < 350000) {
System.out.println("Missed Zone, failure.");
}else {
robot.keyPress(Main.promptKey);
System.out.println("Found ticker pressing key. ");
}
}
}
For anyone wondering where could something this specific come from, I am using a modified version of this website as my medium for making this bot.
Now for the example with using Robot.createScreenCapture(Rectangle) and BufferedImage.getRGB(int, int)
Robot robot = new Robot();
Rectangle rect = new Rectangle(889, 449, 149, 149);
Point prompt = new Point(24, 57, robot);
String format = "png";
String fileName = "fullDebug1." + format;
BufferedImage screenImage = robot.createScreenCapture(rect);
for(int i = 0; i < 360; i++) {
zones.add(new Point(radius * Math.cos(Math.toRadians(i)) + centerX, radius * Math.sin(Math.toRadians(i)) + centerY, robot));
}
System.out.println("Resolution: " + width + " * " + height/* + " " + screenImage.getRGB((int) prompt.getX(), (int) prompt.getY())*/);
while(true) {
tempZone = null;
int zoneFinal = 0;
while(prompt.hasColor(promptColor)) {
System.out.println("Found prompt, trying to find zone.");
BufferedImage zoneImage = robot.createScreenCapture(rect);
int count = 0;
for(int i = 0; i < zones.size(); i++) {
if(zones.get(i).hasColorZones(zoneColor, zoneImage)){
tempZone = zones.get(i);
break;
}
}
if(tempZone != null) {
System.out.println("Found zone at: " + tempZone.getX() + ", " + tempZone.getY() + ". Looking for ticker...");
tempZone.whenHasColor(zoneColor);
}else{
System.out.println("Unable to find zone.");
}
while(prompt.hasColor(promptColor)) {}
}
and its Point class:
public class Point {
private double x;
private double y;
private final Robot robot;
public Point(double x, double y, Robot rob) {
this.x = x;
this.y = y;
this.robot = rob;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public boolean hasColor(int color) {
Rectangle rect = new Rectangle(889, 449, 149, 149);
BufferedImage screenImage = robot.createScreenCapture(rect);
return (Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(color))
}
public boolean hasColorPixel(int color, Rectangle rect) throws IOException {
BufferedImage cap = robot.createScreenCapture(rect);
return Math.abs(cap.getRGB(0, 0)) == Math.abs(color);
}
public boolean hasColorZones(int color, BufferedImage screenImage) {
return Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(color);
}
public boolean hasColorList(int[] colors, BufferedImage screenImage) {
for(int i : colors) {
if(Math.abs(screenImage.getRGB((int) x, (int) y)) == Math.abs(i)) {
return true;
}
}
return false;
}
public void whenHasColor(int color) throws IOException {
Rectangle rect = new Rectangle(889 + (int) x, 449 + (int) y, 1, 1);
while(hasColorPixel(color, rect)) {
//Nothing
}
BufferedImage cap = robot.createScreenCapture(rect);
if(Math.abs(Math.abs(cap.getRGB(0, 0)) - 15657182) < 350000) {
System.out.println("Missed Zone, failure.");
}else {
robot.keyPress(Main.promptKey);
System.out.println("Found ticker pressing key. ");
}
}
}
So my question is, which will run faster? Sometimes the latter option (which I more thoroughly debugged) gets 10 in a row, but then will fail randomly too. At first I thought it was that my laptop could not handle it, but upon running it on my desktop I had the same results. I would rather not have the bot be inconsistent with working properly or not.
I do hope I was able to give enough information for anyone to be able to help me. If there is anything I need to add or change, please tell me! This is my first question and I would like to learn to be better with this site.
Another apologies to if 1) the pixels are not the same on your screen, therefore you would not be able to run this to simulate my situation, and 2) if this post was long. I wanted to make a good "Minimal, Complete, and Verifiable example," but I wasn't sure how to shorten it any more.
EDIT
To make the code put in more relevant, as Ben pointed out, I could have cut it. But I thought of something that makes it more useful.
What can be done to that code to optimize it to run faster, so the bot will be 100% consistently working, or at least closer to 100% than it is now (~60%)
That sounds extremely philosophical doesn't it?
Anyways, I have a rather complex problem.
My main_activity class gathers all of the Zombies like so:
//Testing Runnable (used to compare the first zombie with the player)
private Runnable updateLocations = new Runnable(){
#Override
public void run(){
try {
while(true) {
image_player.getLocationInWindow(pLoc);
Zombie zoms = zombieCollection.next();
if(!zoms.equals(null)){
zoms.getZombieImage().getLocationInWindow(zLoc);
}
System.out.println("Zombie: x = " + zLoc[0] + "; y = " + zLoc[1]);
System.out.println("Player: x = " + pLoc[0] + "; y = " + pLoc[1]);
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
My zombie class gathers information like so:
public class Zombie{
float X, Y;
int Width, Height;
Direction fdirc;
ImageView zImage;
Player player;
boolean dead;
int[] zLoc;
public Zombie(ImageView zImage, Player player){
zLoc = new int[2];
zImage.getLocationOnScreen(zLoc);
this.zImage = zImage;
this.X = zLoc[0];
this.Y = zLoc[1];
this.Width = zImage.getWidth();
this.Height = zImage.getHeight();
this.fdirc = Direction.EAST;
this.player = player;
this.dead = false;
Thread thread = new Thread(this.startZombieChase);
thread.start();
}
public ImageView getZombieImage(){
return zImage;
}
private Runnable startZombieChase = new Runnable() {
#Override
public void run() {
try {
while(!dead) {
moveTowardsPlayer();
Thread.sleep(10);
updateZombie.sendEmptyMessage(0);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
private Handler updateZombie = new Handler(Looper.getMainLooper()) {
public void handleMessage(android.os.Message msg) {
/** Because the zombie should always be on top! **/
zImage.getLocationOnScreen(zLoc);
zImage.bringToFront();
zImage.setX(X);
zImage.setY(Y);
}
};
private void moveTowardsPlayer(){
int player_x = player.getPosition()[0];
int player_y = player.getPosition()[1];
l("Where is it in zombie class : player - " + player_x + " " + player_y + "zombie - " + X + " " + Y);
float compareX = player_x - (int)X;
float compareY = player_y - (int)Y;
// Y is closer, so we're moving horizontally.
if(Math.abs(compareX) < Math.abs(compareY)){
//Moving North
if(player_y > Y){
Y+=1;
}
//Moving South
else if(player_y < Y){
Y-=1;
}
}
// X is closer, so we're moving vertically.
else{
//Moving East
if(player_x > X){
X+=1;
}
//Moving West
else if(player_x < X){
X-=1;
}
}
}
public void l(Object string){
System.out.println("Log - " + string);
}
}
The problem I'm having is that it will move relative to a number (So, it does move relative to something) but, not the correct thing.
logcat tells me this:
Where is it in zombie class: player - 750 451 zombie - 750 451
Where it is in main_activity: player - 750 451 zombie - 792 619
Could anyone help me understand what I'm doing wrong?
The entire project is located here.
Zombies that moves away from the Brainz, must be an ill Zombie. We can't have that, now can we?
To have a function that moves the Zombie towards the non-zombie, you use a function, but that function makes use of variables that are not arguments, and there fore hard to find out where they come from. I'd go with something like this: (this is a bit verbose, but clearly shows what is happening)
/*
* Function to update the position of the Zombie, aka walk to the Player.
* #player_pos - Where's the Brainz at?
* #zombie_pos - Where am I?
* Might want to build it overloaded with an option for the speed.
*
* #return - We return the new Zombie pos.
*/
private double [] moveTowardsPlayer(double [] player_pos, double [] zombie_pos) {
// To make sure we don't override the old position, we copy values. (Java stuff)
double [] player_pos_old = player_pos.clone();
double [] zombie_pos_old = zombie_pos.clone();
// Let's get the new X pos for the Zombie
double left_or_right = player_pos_old[0] - zombie_pos_old[0]; // pos number is right, neg is left
double zombie_pos_new_x;
if (left_or_right > 0) { // Right
zombie_pos_new_x = player_pos_old[0] + zombie_speed;
} else { // Left - this way we make sure we are always getting nearer to the Brainz.
zombie_pos_new_x = player_pos_old[0] - zombie_speed;
}
// TODO: do the same for the Y pos.
// Bring it together
double [] zombie_pos_new = {zombie_pos_new_x, zombie_pos_new_y};
// One step closer to the Brainz!
return zombie_pos_new;
}
And use like:
double [] zombie_pos = moveTowardsPlayer([2, 2], [5, 4]);
this.X = zombie_pos[0]; // I'd advice to keep these together as one var.
this.Y = zombie_pos[1]; // But it's your game.
And then figure out when the Zombie gets the Brainz (or the Bullet)
this here is not correct:
if(!zoms.equals(null)){
if you are trying to check that zoms is not pointing to a null reference then do
if(zoms != null ){
I have a problem with the execution of the methods in a for loop. I want my program to execute the programmedMoving() method 5 times.
This programmedMoving() method consists of two methods:
the first one ( chooseTheDirection() ) executes some algorithm and returns the Point2D towards which the object should move;
the second one ( moveToThePoint() ) should get this point and move the object.
public boolean dispatchKeyEvent(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_H) {
for(int i=0; i<5; i++{
programmedMoving();
}
}
}
//////////////////////////////////////////////////////////////////
private void programmedMoving(){
chooseTheDirection(); //returns the Point2D
moveToThePoint();//according to the direction starts moving the point
}
The problem is that it executes chooseTheDirection() method 5 times without waiting for the moveToThePoint() method to finish it's execution. So, by the time the object on the JPanel starts actually moving, the chooseTheDirection() method already provides 5 Point2D points, whereas I need it to provide only one, and wait for the end of the next method.
Could anyone tell me what am I doing wrong? Thank you.
ADDITIONALLY:
private Direction chooseDirection(){
final List<Direction> directionList = Collections.unmodifiableList(Arrays.asList(Direction.values()));
int pick = random.nextInt(directionList.size());
dir = directionList.get(pick);
directionsArchive.add(dir);
System.out.println("dir " + dir + " " + directionsArchive);
if(directionsArchive.size() == 1){
dir = directionsArchive.get(0);
System.out.println("equal to one taken " + dir + " size of dir " + directionsArchive.size());
directionsArchive.add(dir);
}
if(directionsArchive.size() > 1){
int last = directionsArchive.size()-1;
System.out.println("index of last " + last);
if(directionsArchive.get(last).equals(dir)){
pick = random.nextInt(directionList.size());
dir = directionList.get(pick);
directionsArchive.add(dir);
}
System.out.println("more than one taken " + dir + " size of dir " + directionsArchive.size());
directionsArchive.add(dir);
}
else{
directionsArchive.add(dir);
System.out.println(" size of dir " + directionsArchive.size());}
return dir;
}
private void moveToThePoint(){
if(dir.equals(Direction.NORTH)){
this.robot.turnUp();
this.robot.go();
}
if(dir.equals(Direction.SOUTH)){
this.robot.turnDown();
this.robot.go();
}
if(dir.equals(Direction.EAST)){
this.robot.turnRight();
this.robot.go();
}
if(dir.equals(Direction.WEST)){
this.robot.turnLeft();
this.robot.go();
}
}
// SOME EXAMPLES TO THE MOVING METHODS. I PROVIDED ONLY ONE, CAUSE THEY ARE PRETTY SIMILAR
public void turnDown()
{
//System.out.println("Robot - Turn Down!");
this.m_nXDir = 0;
this.m_nYDir = 1;
}
public void go()
{
this.m_nStep = 1;
//System.out.println("Robot - Go!");
}
public void move(int d, int e)
{
//from start to finish
int x = d + this.m_nStep * this.m_nXDir;
int y = e + this.m_nStep * this.m_nYDir;
int w = getWidth();
int h = getHeight();
setBounds(x, y, w, h);
}
moveToThePoint doesn't actually move the object, it merely sets the step size to 1. I assume the actual move() function is called on a timer.
Here are 2 suggestions on how to fix it:
Re-run the decision algorithm (programmedMoving) for every step.
or
Queue up future moves. You store a queue of future moves for the object, and each times it is scheduled to move() you remove the next move from the queue and execute it.
public class SnowflakeWallpaper extends WallpaperService {
// Limit of snowflakes per snowflake type; 4 types * 4 snowflake = 16 total
// Should keep memory usage at a minimal
static int SNOWFLAKE_AMOUNT = 4;
Drawable drawWall;
Rect wallBounds;
// Draw all snowflakes off screen due to not knowing size of canvas at creation
static int SNOW_START = -90;
ArrayList<Snowflakes> snow = new ArrayList<Snowflakes>();
private final Handler mHandler = new Handler();
#Override
public void onCreate() {
super.onCreate();
//WallpaperManager to pull current wallpaper
WallpaperManager wManager = WallpaperManager.getInstance(this);
drawWall = wManager.getFastDrawable();
wallBounds = drawWall.copyBounds();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Engine onCreateEngine() {
return new SnowEngine();
}
class SnowEngine extends Engine {
private final Runnable mDrawSnow = new Runnable() {
public void run() {
drawFrame();
}
};
private boolean mVisible;
SnowEngine() {
if(snow.size() < 16){
//Back snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakeback),
SNOW_START,
SNOW_START,
((float)(Math.random() * 2) + 1)) // Fall speed initial setup, back slowest to front fastest potentially
);
}
//MidBack snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakemid),
SNOW_START,
SNOW_START,
((float)(Math.random() * 4) + 1)
));
}
// Mid snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflakemidfront),
SNOW_START,
SNOW_START,
((float)(Math.random() * 8) + 1))
);
}
// Front snowflakes
for(int i = 0; i < SNOWFLAKE_AMOUNT; i++){
snow.add(new Snowflakes(
BitmapFactory.decodeResource(getResources(),
R.drawable.snowflake),
SNOW_START,
SNOW_START,
((float)(Math.random() * 16) + 1))
);
}
}
}
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
#Override
public void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mDrawSnow);
}
#Override
public void onVisibilityChanged(boolean visible) {
mVisible = visible;
if (visible) {
drawFrame();
} else {
mHandler.removeCallbacks(mDrawSnow);
}
}
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
super.onSurfaceChanged(holder, format, width, height);
drawFrame();
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
}
#Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
mVisible = false;
mHandler.removeCallbacks(mDrawSnow);
}
/*
* Update the screen with a new frame
*/
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
/*
* if the snow goes too low or too right, reset;
*/
for(int i = 0; i < snow.size(); i++){
if(snow.get(i).getX() > holder.getSurfaceFrame().width()){
snow.get(i).setX(-65);
}
if(snow.get(i).getY() > holder.getSurfaceFrame().height()){
snow.get(i).setY(-69);
}
}
// Test if the array was just create; true - randomly populate snowflakes on screen
if(snow.get(1).getX() < -70){
for(int i = 0; i < snow.size(); i++){
snow.get(i).setX((int)(Math.random() * getSurfaceHolder().getSurfaceFrame().width() +1));
snow.get(i).setY((int)(Math.random() * getSurfaceHolder().getSurfaceFrame().height() + 1));
}
}
// Change snowflake x & y
for(int i = 0; i < snow.size(); i++){
snow.get(i).delta();
}
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// call to draw new snow position
drawSnow(c);
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
// Reschedule the next redraw
mHandler.removeCallbacks(mDrawSnow);
if (mVisible) {
mHandler.postDelayed(mDrawSnow, 1000 / 100);
}
}
/*
* Draw the snowflakes
*/
void drawSnow(Canvas c) {
c.save();
// Draw bg
//********** add code to pull current bg and draw that instead of black. Maybe set this in config?
if(drawWall == null){
c.drawColor(Color.BLACK);
}else{
drawWall.copyBounds(wallBounds);
drawWall.draw(c);
}
/*
* draw up the snow
*/
for(int i = 0; i < snow.size(); i++){
c.drawBitmap(snow.get(i).getImage(), snow.get(i).getX(), snow.get(i).getY(), null);
}
c.restore();
}
}
}
Same question as Gabe - what's the problem?
Some general thoughts:
* You should avoid doing lots of work in a constructor. Your constructor does a ton of work that should (imho) be in some init/setup method instead. Easier to benchmark / profile there independently from instance creation.
You're using Math.random in many places - I assume you are singly threaded, but Math.random is synchronized. Per the javadocs: " if many threads need to generate pseudorandom numbers at a great rate, it may reduce contention for each thread to have its own pseudorandom-number generator"
You're using Math.random which gets you a double, then multiplying, then adding, then casting. This looks wasteful. Any way to get fewer ops?
You seem to have some division - see line "mHandler.postDelayed(mDrawSnow, 1000 / 100);". Sure, that's probably compiled or JIT'd away, but you should avoid division in performance critical code (it's far slower than multiplying). So any div by a constant can be replaced by multiplying by 1 / C as a static.
You have lots of repeat accessor method calls (in some cases nearly all are repeat). See snippit:
for(int i = 0; i < snow.size(); i++){
if(snow.get(i).getX() > holder.getSurfaceFrame().width()){
snow.get(i).setX(-65);
}
if(snow.get(i).getY() > holder.getSurfaceFrame().height()){
snow.get(i).setY(-69);
}
}
You should store "holder.getSurfaceFrame().width() in a temporary / local var (perhaps once per draw loop assuming your surface is resizable by the user). You might also store snow.get(i) in a local var. Better yet (style) you can use the enhanced for loop as snow is an ArrayList. So use
for (Snow mySnow : snow) {
// Do something with mySnow
}
Hope this helps. Good luck!