My game runs on Samsung Galaxy J3, but crashes on old Samsung tablet.

Hey all!

I recently coded a game on Processing Android. It is riddled with animated objects and arraylists of them. It seems to run smoothly on my Samsung Galaxy J3, but when ran on an old Samsung tablet, the game crashes. I have narrowed down the reason to all the arraylists - if I set them to only 2-3 objects each, it is still laggy, but it doesn't crash. If I add more, it crashes after a few seconds.

I am using a P2D render for all of the game. Could that be an issue? Do I need a JAVA2D render for the calculations?

Answers

  • It is hard to say what is causing the problem. Could be the version of Android you are using, the version of Android Mode in Processing or the design of your app. So many things we don't know. What could help in this case is to provide a MCVE (https://stackoverflow.com/help/mcve) and take it from there.

    Why do you say is could be the renderer? If you change the renderer to JAVA2D, does it solve the problem?

    Kf

  • //import display metrics. Will be used to find how many pixels is 1 cm on the device the code is played in.//
    import android.util.DisplayMetrics;
    //variable to assign amount of pixels per 1 cm//
    float cm;
    //blob animations//
    PImage[] blobanimation = new PImage[45];
    //red cells animations//
    PImage[] redcellanimation = new PImage[8];
    //white cells animations//
    PImage[] whitecellanimation = new PImage[14];
    //array list of basic score objects//
    //arraylist of red cells//
    ArrayList<basicscoreobject> redcells = new ArrayList<basicscoreobject>();
    //array list of white cells//
    ArrayList<basicscoreobject> whitecells = new ArrayList<basicscoreobject>();
    //[0] to [10] - eat animation;
    //[11] to [25] - drool/lick animation;
    //[26] to [28] - outstretch tongue animation;
    //[29] and [30] - waving tongue loop;
    //[31] to [32] - from waving tongue to faster waving tongue;
    //[33] to [35] - fast waving tongue loop (33,34,35,33,34,35,etc);
    //[36] to [44] - eat animation if tongue is out
    
    //blob variables//
    float blobrotationx;
    float blobrotationy;
    //variables for blob's animations
    int previousblobframe;
    int blobframe;
    int blobframechangemillis = 70;
    int blobtrackframechange;
    int blobframechange;
    //booleans to track blob's animations for smooth transitioning//
    boolean eatingscoreball;
    boolean licking;
    
    //variables to remember basicscoreobject coords when interacting with blob. Used to keep blob rotated in the same direction even after score object gets 'eaten' and repositioned//
    float rememberbasicscoreobjectx, rememberbasicscoreobjecty;
    
    player blob;
    basicscoreobject redcell;
    
    void setup() {
      size(displayWidth, displayHeight, P2D);
      orientation(PORTRAIT);
      //functions to get screen DPI and divide by 2.54 to get centimeres per inch//
      DisplayMetrics metrics = new DisplayMetrics();
      getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
      float dpi = metrics.densityDpi;
      cm = dpi/2.54;
      //
      textAlign(CENTER);
      imageMode(CENTER);
      textureMode(NORMAL);
      frameRate(45);
      for (int i = 0; i<blobanimation.length; i++) {
        blobanimation[i] = loadImage("blob"+(i+1)+".png");
      }
      for (int i = 0; i<2; i++) {
        redcells.add(new basicscoreobject("redcell", cm*0.5, 3));
      }
      for (int i = 0; i<2; i++) {
        whitecells.add(new basicscoreobject("whitecell", cm*0.5, 13));
      }
      blob = new player();
    }
    void draw() {
      background(50, 0, 0);
      //spawn basic score objects//
      for (int i = 0; i<redcells.size(); i++) {
        basicscoreobject redcell = redcells.get(i);
        redcell.drawscoreobject();
        redcell.givescore();
        redcell.movescoreobject(0);
        redcell.animate();
      }
      for (int i = 0; i<whitecells.size(); i++) {
        basicscoreobject whitecell = whitecells.get(i);
        whitecell.drawscoreobject();
        whitecell.takescore();
        whitecell.movescoreobject(cm);
        whitecell.animate();
      }
      blob.drawblob();
      blob.playblobanimations();
      blob.moveblob();
    //..................//
    }
    
    void onPause() {
      noLoop();
    }
    void onResume() {
      loop();
    }
    
    //...................................//
    class player {
      float xspeed;
      float yspeed;
      //variables to store previous xspeed and yspeed//
      float pxspeed;
      float pyspeed;
      float x = width/2;
      float y = 40;
      float size;
      //variable to store initial size. Size will be increased by a portion of initial size.//
      float initialsize;
      float remembersize;
      //variable to swap rotation when flipping image//
      float rotateamount;
      //variables for vertexes. Will be used to flip image when moving backwards so it's not upside down//
      float v1x, v1y, v2x, v2y, v3x, v3y, v4x, v4y;
      //boolean to know when to flip image//
      boolean flipimage;
    
      player() {
        size = cm*0.7;
        initialsize = size;
        remembersize = size;
      }
      void drawblob() {
        float angle;
        //if blob is moving backwards, flip image so it's not upside down//
        if (xspeed<0) {
          flipimage = true;
        } else if (xspeed>=0) {
          flipimage = false;
        }
        //adjust vertexes accordingly so that image is flipped.//
        if (flipimage == true) {
          v1x = 1.5*size;
          v1y = -1.5*size;
          v2x = 1.5*size;
          v2y = 1.5*size;
          v3x = -1.5*size;
          v3y = 1.5*size;
          v4x = -1.5*size;
          v4y = -1.5*size;
          rotateamount = 0.5;
        } else if (flipimage == false) {
          v1x = -1.5*size;
          v1y = -1.5*size;
          v2x = -1.5*size;
          v2y = 1.5*size;
          v3x = 1.5*size;
          v3y = 1.5*size;
          v4x = 1.5*size;
          v4y = -1.5*size;
          rotateamount = 1.5;
        }
        pushMatrix();
        //rotation angle//
        angle = atan2(blobrotationx, blobrotationy);
        translate(x, y);
        rotate(-angle-rotateamount*PI);
        beginShape(QUADS);
        texture(blobanimation[blobframe]);
        noStroke();
        vertex(v1x, v1y, 0, 0);
        vertex(v2x, v2y, 0, 1);
        vertex(v3x, v3y, 1, 1);
        vertex(v4x, v4y, 1, 0);
        endShape();
        popMatrix();
      }
    
      void playblobanimations() {
        //[0] to [10] - eat animation;
        //[11] to [25] - drool/lick animation;
        //[26] to [28] - outstretch tongue animation;
        //[29] and [30] - waving tongue loop;
        //[31] to [32] - from waving tongue to faster waving tongue;
        //[33] to [35] - fast waving tongue loop (33,34,35,33,34,35,etc);
        //[36] to [44] - eat animation if tongue is out
        blobrotationx = xspeed;
        blobrotationy = yspeed;
        //function for eat animation (IT IS IMPORTANT THAT THIS SNIPPET IS ABOVE THE REST!!)//
        if (((blobframe>0) && (blobframe<=10)) || ((blobframe>35) && (blobframe<=44))) {
          eatingscoreball = true;
          blobframechange = 1;
          blobframechangemillis = 60;
        } else if (((blobframe>10) && (blobframe<=35) && (blobframe>44)) || (blobframe == 0) ) {
          eatingscoreball = false;
        }
        //if blob eats more than one red cells at once, play lick animation after eat animation is finished//
        if (remembersize+2*(initialsize/20)<=size) {
          licking = true;
        }
        if (blobframe == 25) {
          licking = false;
          blobframe = 0;
          blobframechange = 0;
          blobframechangemillis = 30;
        }
        if ((blobframe == 0) && (licking)) {
          blobframe = 11;
          blobframechange = 1;
          blobframechangemillis = 70;
        }
        //Adjust remembersize. IT IS IMPORTANT THAT THIS COMES AFTER LICK ANIMATION FUNCTIONS.//
        remembersize = size;
        //functions to wave blob's tongue in air when it's speeding a lot//
        //only apply when blob is not eating a scoreball or licking//
        if ((!eatingscoreball) && (!licking)) {
          //if accelerating beyond 13 pixels per frame, play outstretch tongue animation//
          if ((abs(yspeed) + abs(xspeed)>=6) && (abs(pyspeed) + abs(pxspeed)<6)) {
            blobframechangemillis = 70;
            blobframe = 26;
            blobframechange = 1;
          }
          //once tongue is outstretched, loop frames 29 and 30 to wave tongue in air continuously//
          if (blobframe == 30) {
            blobframechange = -1;
          }
          if (blobframe == 29) {
            blobframechange = 1;
          }
          //if acceleration goes beyond 20 pixels per frame, outstretch tongue more//
          if ((abs(yspeed) + abs(xspeed)>=14) && (abs(pyspeed) + abs(pxspeed)<14)) {
            blobframechangemillis = 130;
            blobframe = 31;
            blobframechange = 1;
          }
          //loop frames 33 to 35 (33,34,35,33,34,35,etc) to wave tongue more//
          if (blobframe==35) {
            blobframe = 33;
          }
          //for wave tongue more loop, change frames slower for better looking animation//
          if ((blobframe>=33) && (blobframe<=35)) {
            blobframechangemillis = 70;
          }
          //if deccelerating from more than 20 pixels per frame to less than 20 pixels per frame, transition tongue from fully outstretched to partially outstretched (loop backwards)//
          if ((abs(yspeed) + abs(xspeed)>=6) && (abs(yspeed) + abs(xspeed)<14) && (abs(pyspeed) + abs(pxspeed)>=14)) {
            blobframechangemillis = 70;
            blobframe = 32;
            blobframechange = -1;
          }
          //if deccelerating from more than 13 pixels per frame to less than 13 pixels per frame, put tongue back in mouth (loop outstretch tongue backwards)//
          if (((abs(yspeed) + abs(xspeed)<6)) && ((abs(pyspeed) + abs(pxspeed)>=6))) {
            blobframe = 28;
            blobframechange = -1;
          }
        }
        //if putting tongue back in mouth is finished, return to frame 0.//
        if ((blobframe == 26) && (blobframechange == -1)) {
          blobframe = 0;
          blobframechange = 0;
        }
        //if blob has finished eating a red blood cell, end animation//
        if (blobframe == 10) {
          xspeed = 0;
          yspeed = 0;
          blobframe = 0;
          blobframechange = 0;
        }
        if (blobframe == 44) {
          xspeed = 0;
          yspeed = 0;
          blobframe = 0;
          blobframechange = 0;
        }
        //function to play animations (change frames)//
        previousblobframe = blobframe;
        if (millis()>blobtrackframechange+blobframechangemillis) {
          blobtrackframechange = millis();
          blobframe = blobframe + blobframechange;
        }
      }
    
    //...................//
    
    class basicscoreobject {
      float x;
      float previousx;
      float initialx;
      float y;
      float xspeed;
      float constrainx = random(width/2, width);
      float objectsize;
      float rememberobjectsize;
      PImage[] objectimage;
      int objectframecount;
      int objectframe;
      int trackframechange;
    
      basicscoreobject(String imagename, float tempobjectsize, int tempobjectframecount) {
        objectsize = tempobjectsize;
        rememberobjectsize = tempobjectsize;
        x = random(objectsize/2, width/3);
        y = random(cm, 2*height/3);
        //value of FrameRate in xspeed random span. For some reason it's 10 in setup function so I had to type it manually//
        xspeed = random(cm/45, (cm*3)/45);
        initialx = x;
        objectframecount = tempobjectframecount;
        objectimage = new PImage[objectframecount+1];
        objectframe = int(random(0, objectframecount));
        for (int i = 0; i<objectimage.length; i++) {
          objectimage[i] = loadImage(imagename+(i+1)+".png");
        }
      }
    
      void drawscoreobject() {
        beginShape(QUADS);
        texture(objectimage[objectframe]);
        vertex(x-0.75*objectsize, y-0.75*objectsize, 0, 0);
        vertex(x-0.75*objectsize, y+0.75*objectsize, 0, 1);
        vertex(x+0.75*objectsize, y+0.75*objectsize, 1, 1);
        vertex(x+0.75*objectsize, y-0.75*objectsize, 1, 0);
        endShape();
      }
    
      void movescoreobject(float distancefromwall) {
        previousx = x;
        x = x + xspeed;
        if (x>x+constrainx) {
          x = x+constrainx;
          xspeed = xspeed*-1;
        }
        if (x<initialx+(objectsize-rememberobjectsize)/2) {
          x = initialx+(objectsize-rememberobjectsize)/2;
          xspeed = xspeed*-1;
        }
        if (x>width-objectsize/2-distancefromwall) {
          x = width-objectsize/2-distancefromwall;
          xspeed = xspeed*-1;
        }
        if (x<0+objectsize/2+distancefromwall) {
          x = 0+objectsize/2+distancefromwall;
          xspeed = xspeed*-1;
        }
      }
      void givescore() {
        if (eatingscoreball) {
          blobrotationx = rememberbasicscoreobjectx-blob.x;
          blobrotationy = rememberbasicscoreobjecty-blob.y;
        }
        if ((dist(x, y, blob.x, blob.y)<(objectsize*2)) && (!licking)) {
          blob.xspeed = (x - blob.x)/frameRate + xspeed;
          blob.yspeed = (y - blob.y)/frameRate;
          rememberbasicscoreobjectx = x;
          rememberbasicscoreobjecty = y;
          if ((blobframe>=26) && (blobframe<=30)) {
            blobframe = 0;
          }
          if ((blobframe>30) && (blobframe<36)) {
            blobframe = 36;
          }
          blobframechange = 1;
          if (((previousblobframe == 6) && (blobframe == 7)) || ((previousblobframe == 40) && (blobframe == 41))) {
            blob.size = blob.size + blob.initialsize/20;
            x = random(objectsize/2, width/3);
            initialx = x;
            y = random(0, 2*height/3);
            xspeed = random(cm/frameRate, (cm*3)/frameRate);
            constrainx = random(width/2, width);
          }
        }
      }
      void takescore() {
        float rememberxspeed = blob.xspeed;
        float rememberyspeed = blob.yspeed;
        //vector for distance between blob and score object. Will be used to correct distance//
        PVector distanceBlobBasicscoreobject = new PVector(x - blob.x, y - blob.y);
        float magnitude = distanceBlobBasicscoreobject.mag();
        float correctionx = distanceBlobBasicscoreobject.x*(((objectsize/2+blob.size/2)-magnitude)/magnitude);
        float correctiony = distanceBlobBasicscoreobject.y*(((objectsize/2+blob.size/2)-magnitude)/magnitude);
    
    //NOTE THAT THIS IS ALSO A LIKELY CANDITATE FOR THE SLOWNESS OF THE SKETCH. All objects from this animated arraylist gradually grow in size.//
        objectsize = objectsize + (cm/frameRate)/10;
        objectsize = constrain(objectsize,0,cm*2);
    //..........................................//
      }
      void animate() {
        if (millis()>trackframechange+70) {
          trackframechange = millis();
          objectframe = objectframe + 1;
        }
        if (objectframe>objectframecount) {
          objectframe = 0;
        }
      }
    }
    

    This is a series of snippets from the game's code. //......// signifies a chunk of code has been removed there. I have tried to filter out the likely factors for the game's slowness.

    Does anybody see anything amiss in this code?

  • Sounds like it could be an out of memory error. What is the available heap memory on the tablet compared to the J3?

    One way to test this would be to edit the manifest to set a large heapsize.

  • Answer ✓

    you are loading a new set of images for each red and white cell. you only need one copy of each. they don't need their own copies of the image, just am index into a shared global copy.

    i think generally you have too many images. and that's using too much memory, causing the crash.

  • in fact you define arrays for the two animations in lines 8 and 10 and then don't use them.

    we can't run your 383 lines of code without the 70 images.

  • @koogs Damn, you are right. I don't even use those arraylists.

    How do I use one copy for each red and white cell? Do I need separate arraylists for the red and for the white cells?

  • class basicscoreobject {
      float x;
      float previousx;
      float initialx;
      float y;
      float xspeed;
      float constrainx = random(width/2, width);
      float objectsize;
      float rememberobjectsize;
      PImage[] objectimage;
      int objectframecount;
      int objectframe;
      int trackframechange;
    
      basicscoreobject(PImage[] tempobjectimage, float tempobjectsize, int tempobjectframecount) {
        objectimage = tempobjectimage;
        objectsize = tempobjectsize;
        rememberobjectsize = tempobjectsize;
        x = random(objectsize/2, width/3);
        y = random(cm, 2*height/3);
        //value of FrameRate in xspeed random span. For some reason it's 10 in setup function so I had to type it manually//
        xspeed = random(cm/45, (cm*3)/45);
        initialx = x;
        objectframecount = tempobjectframecount;
        objectframe = int(random(0, objectframecount));
      }
    
      void drawscoreobject() {
        beginShape(QUADS);
        texture(objectimage[objectframe]);
        vertex(x-0.75*objectsize, y-0.75*objectsize, 0, 0);
        vertex(x-0.75*objectsize, y+0.75*objectsize, 0, 1);
        vertex(x+0.75*objectsize, y+0.75*objectsize, 1, 1);
        vertex(x+0.75*objectsize, y-0.75*objectsize, 1, 0);
        endShape();
      }
    
      void movescoreobject(float distancefromwall) {
        previousx = x;
        x = x + xspeed;
        if (x>x+constrainx) {
          x = x+constrainx;
          xspeed = xspeed*-1;
        }
        if (x<initialx+(objectsize-rememberobjectsize)/2) {
          x = initialx+(objectsize-rememberobjectsize)/2;
          xspeed = xspeed*-1;
        }
        if (x>width-objectsize/2-distancefromwall) {
          x = width-objectsize/2-distancefromwall;
          xspeed = xspeed*-1;
        }
        if (x<0+objectsize/2+distancefromwall) {
          x = 0+objectsize/2+distancefromwall;
          xspeed = xspeed*-1;
        }
      }
      void animate() {
        if (millis()>trackframechange+70) {
          trackframechange = millis();
          objectframe = objectframe + 1;
        }
        if (objectframe>objectframecount) {
          objectframe = 0;
        }
      }
    }
    

    This is what I tried, and I tried using the image arrays that I didnt use previously in the arraylist's argument but I get black squares instead of images.

  • Can't see anything immediately wrong with the snippet you posted (but obviously don't have all your changes or any of the images...)

    Using camelCase for your variable names would aid readability.

    if (x>x+constrainx) {

    What's this doing?

  • @koogs I'm sorry, but I can't share the images due to obvious risks of them being stolen by somebody. I spent hours upon hours creating the animations, I'm definitely not posting them as royalty free material in a public forum.

    I've tried using camelCase, but it really slows down my coding process. Literally half of the time I spend writing code is spent double and triple checking whether capitalization is accurate, and I still overlook some letters resulting in hours of headbanging over a capital letter. It's just not my thing.

    Every score object moves left and right within a random "span" - some cover the whole screen, some mere centimeters. That span is randomized for each cell, that is what constrainx is.

    I have revisited my code with a fresh mind today and I made it work by only initializing the arrays once in setup. I don't have my tablet handy right now to retry it, but I will keep everyone posted once I find out if it runs better.

    Thanks a lot for the help!

  • I can confirm that this was the reason.

    Cheers!

  • if (x>x+constrainx) {

    You missed my point with this. How can x be greater than x + something? Only if the something is negative. So a better test would be if (constrainx < 0)

    (Spaces between operators makes code more readable)

  • Ah, yes, how clumsy.

    The reason for the error is that I was using vectors first, and the initial position of the object was x, so the function would go something like if(vector.x > x+constrainx) {...

    Thanks, I have fixed it now.

Sign In or Register to comment.