@Override
  public void update(View v) {
    long newtime = System.nanoTime();
    // �ɋ}���‚���
    // �[������
    if (lastSeedCount != fruitsSplatted.size()) {
      Random random = new Random();
      int randomIndex = random.nextInt(2) + 1;
      if (randomIndex % 2 == 0) {
        // ����������+0.5�͈͂ł����Ă���
        // 1~5�͈̔͂��쐬����1/10�ɂ���
        randomSpeed += (random.nextInt(3) + 1) / 10.0f;
        float decimalSpeed = random.nextFloat(); // 0.1~1.0
        //                randomSpeed = random.nextInt(4) + decimalSpeed + 1;    // 1~5
        System.out.println(randomSpeed);
      }
      lastSeedCount = fruitsSplatted.size(); // �ʕ��̕\����
    }
    String speedStr = String.format("SPEED:%f", randomSpeed);
    System.out.println(speedStr);
    float elapsedsecs = (float) (newtime - frtime) / ONESEC_NANOS * randomSpeed;
    frtime = newtime;
    fps = (int) (1 / elapsedsecs);

    // update combo hits
    Iterator<Combo> hcit = hitCombos.keySet().iterator();
    while (hcit.hasNext()) {
      Combo combo = hcit.next();
      ComboHit ch = hitCombos.get(combo);
      ch.y -= COMBOHIT_SPEED * elapsedsecs;
      float chtime = frtime - ch.hitTime;
      ch.alpha = (int) (255 * (1.0f - chtime / COMBOHIT_DISPLAYTIME));
      if (frtime - ch.hitTime > COMBOHIT_DISPLAYTIME / 3)
        fps = 0; // excuse to put a breakpoint here
      if (frtime - ch.hitTime > COMBOHIT_DISPLAYTIME) hcit.remove();
    }

    if (gamestate == State.STARTROUND) {
      // this goofy construction is to make sure we initialize the round from
      // the update/draw thread, not from the UI thread.
      initRound();
      return;
    }

    if (width == 0) {
      // set variables that rely on screen size
      width = v.getWidth();
      height = v.getHeight();
      wallxcenter = width / 2;
      wallycenter = (int) (height * WALL_Y_CENTER_FACTOR);

      inity =
          (int)
              (INIT_SELECTABLE_Y_FACTOR * height); // initial fruit placement, also bottom of wall.

      // attempt to compute wall bounds at wall z  from screen size.  constants are pure
      // magic, found thru trial and error iterations.
      // if the background picture changes, they will need to be recalibrated.
      wallbounds_at_wall_z.set(
          (int) (-1.5 * width),
          (int) (-height * .94),
          (int) (2.43 * width),
          inity); // wall bounds AT WALL Z (!!WaLLzeY!!)

      // magic trial and error world-bounds contants, based on screen image size
      minXbound = 8 * -width;
      maxXbound = 8 * width;
      maxYbound = 5 * height;

      // compute wall bounds at screen z, used for clipping.
      int effl =
          (int)
              (wallbounds_at_wall_z.left + (WALLZFACT * (wallxcenter - wallbounds_at_wall_z.left)));
      int efft =
          (int) (wallbounds_at_wall_z.top + (WALLZFACT * (wallycenter - wallbounds_at_wall_z.top)));
      int effr =
          (int)
              (wallbounds_at_wall_z.right
                  + (WALLZFACT * (wallxcenter - wallbounds_at_wall_z.right)));
      int effb =
          (int)
              (wallbounds_at_wall_z.bottom
                  + (WALLZFACT * (wallycenter - wallbounds_at_wall_z.bottom)));
      wallbounds_at_screen_z.set(effl, efft, effr, effb);
      p.setTextSize(act.TS_NORMAL);
      p.setTypeface(act.getGameFont());
      String t = "SCORE: 999999";
      rhstextoffset = (int) p.measureText(t);
      p.getTextBounds(t, 0, t.length() - 1, scaledDst);
      statstextheight = (int) (scaledDst.height() + 5);
      statstextheight2 = statstextheight * 2;
    }

    if (gamestate == State.RUNNING
        && fruitsSelectable.size() < maxShownSelectableFruit
        && seedsQueued.size() > 0
        && frtime > possspawntime + MIN_SPAWN_INTERVAL_NANOS) {
      possspawntime = frtime;
      // "every now and then" make a fruit available
      if (Math.random() > .6) {
        Fruit newf = null;
        if (fruitsRecycled.size() > 0) { // recycle a fruit if we can
          newf = fruitsRecycled.get(0);
          fruitsRecycled.remove(0);
        } else { // create if needed
          newf = new Fruit();
        }

        // choose fruit
        Seed s = seedsQueued.get(0);
        seedsQueued.remove(0);

        int initx = (int) -s.halfWidth;
        int speed = selectable_speed;
        if (Math.random() > .5) {
          initx = (int) (width + s.halfWidth);
          speed = -speed;
        }

        newf.init(s, initx, inity, 0, speed);
        fruitsSelectable.add(newf);
      }
    } else if (gamestate == State.RUNNING
        && fruitsSelectable.size() == 0
        && fruitsFlying.size() == 0
        && seedsQueued.size() == 0) {
      // round is complete
      if (nWallSplats * 100 / nTotFruit >= minRoundPassPct) {
        act.playSound(Sound.PASSLEVEL);
        round++;
        gamestate = State.ROUNDSUMMARY;
      } else loseLife();
    }

    // update fruit positions
    synchronized (fruitsFlying) {
      Iterator<Fruit> fit = fruitsFlying.iterator();
      while (fit.hasNext()) {
        Fruit f = fit.next();
        f.x += f.vx * elapsedsecs;
        f.y += f.vy * elapsedsecs;
        f.z += f.vz * elapsedsecs;
        f.vy += ACC_GRAVITY * elapsedsecs;
        if (f.z >= WALL_Z && wallbounds_at_wall_z.contains((int) f.x, (int) f.y)) {
          // fruit has hit wall
          fit.remove();
          fruitsSplatted.add(f);
          nWallSplats++;

          if (f.isBonus == true) {
            score += f.seed.points * 5;
          } else {
            score += f.seed.points;
          }

          act.playSound(f.getSplatSound());

          // check combo
          for (Combo c : combos) {
            neededSeeds.clear();
            neededSeeds.addAll(c.seeds);
            neededSeeds.remove(f.seed);
            comboFruits.clear();
            comboFruits.add(f);
            for (Fruit spf : fruitsSplatted) {
              if (neededSeeds.contains(spf.seed)) {
                if (spf.getBounds().intersect(f.getBounds())) {
                  neededSeeds.remove(spf.seed);
                  comboFruits.add(spf);
                }
                if (neededSeeds.size() == 0) break;
              }
            }
            if (neededSeeds.size() == 0) {
              // combo is hit
              score += c.points;
              for (Fruit spf : comboFruits) {
                fruitsSplatted.remove(spf);
              }

              // display combo hit message "somewhere next to" combo hit
              effpt = renderFromZ(f.x, f.y, f.z, wallxcenter, wallycenter);
              ComboHit ch = new ComboHit();
              ch.x = effpt.x + (float) Math.random() * 100 - 50;
              ch.y = effpt.y + (float) Math.random() * 100 - 80;

              // play sound
              act.playSound(Sound.COMBO);

              // ensure combo display is fully onscreen
              p.getTextBounds(c.name, 0, c.name.length(), scaledDst);
              if (ch.x < 0) ch.x = 0;
              else if (ch.x > width - scaledDst.width()) ch.x = width - scaledDst.width();

              ch.hitTime = System.nanoTime();
              hitCombos.put(c, ch);
            }
          }
        } else if (f.y > inity && f.y < inity + f.vy * elapsedsecs && f.z > WALL_Z / 2) {
          // fruit has hit ground near wall
          fit.remove();
          fruitsSplatted.add(f);
        } else if (f.z > WALL_Z
            // here we goofily force java to call render function when we need it
            && (effpt = renderFromZ(f.x, f.y, f.z, wallxcenter, wallycenter)) != null
            && wallbounds_at_screen_z.contains(effpt.x, effpt.y)) {
          // wild pitch, behind wall
          fit.remove();
          fruitsRecycled.add(f);
        } else if (f.y > maxYbound || f.x >= maxXbound || f.x <= minXbound) {
          // wild pitch, out of bounds
          fit.remove();
          fruitsRecycled.add(f);
        }
      }
    }

    if (gamestate == State.RUNNING) {
      synchronized (fruitsSelectable) {
        Iterator<Fruit> fit = fruitsSelectable.iterator();
        while (fit.hasNext()) {
          Fruit f = fit.next();
          if (f != selectedFruit) {
            //                        f.x += f.vx * elapsedsecs;
            f.x += f.vx * elapsedsecs * f.scroll;

            // wobble displayable fruit up and down, and return them to regular line when let go
            // int targy = inity + (int)(Math.sin(f.x/15)* selectable_y_play);
            // f.y += (targy - f.y) / SELECTABLE_FRUIT_BRAKING_FACTOR;
            int targy = inity + (int) (Math.sin(f.x / 65) * selectable_y_play);
            f.y += (targy - f.y) / SELECTABLE_FRUIT_BRAKING_FACTOR;
          }
          if (f.x < -f.seed.halfWidth || f.x > width + f.seed.halfWidth) {
            // we floated off screen
            fit.remove();
            fruitsRecycled.add(f);
          }
        }
      }
    }
  }
  /**
   * draw the screen.
   *
   * @param c
   * @param v
   */
  @Override
  public void draw(Canvas c, View v) {
    try {
      // actually draw the screen
      scaledDst.set(0, 0, width, height);
      c.drawBitmap(wallbtm, null, scaledDst, p);

      // draw wall's bounds, for debugging
      // p.setColor(Color.RED);
      // c.drawRect(wallbounds_at_screen_z, p);

      // draw fruits
      for (Fruit f : fruitsSplatted) {
        drawFruit3Dcoords(c, f, f.getSplatBitmap(), wallxcenter, wallycenter);
      }
      synchronized (fruitsFlying) {
        for (Fruit f : fruitsFlying) {
          drawFruit3Dcoords(c, f, f.getBitmap(System.nanoTime()), wallxcenter, wallycenter);
        }
      }
      synchronized (fruitsSelectable) {
        for (Fruit f : fruitsSelectable) {
          // selectable fruit is on z=0, so we can just display normally:
          c.drawBitmap(f.getBitmap(), f.x - f.seed.halfWidth, f.y - f.seed.halfHeight, p);
        }
      }

      // draw combo hits
      for (Combo combo : hitCombos.keySet()) {
        ComboHit ch = hitCombos.get(combo);
        // p.setColor(Color.YELLOW);
        p.setARGB(
            ch.alpha,
            190 + (int) (Math.random() * 60),
            190 + (int) (Math.random() * 60),
            (int) (Math.random() * 60));
        p.setTypeface(act.getGameFont());
        p.setTextSize(act.TS_NORMAL);
        c.drawText(combo.name, ch.x, ch.y, p);
      }

      //            c.drawText("fps: "+fps
      //                    +" w:"+width + " h:" +height
      //                        +"x:"+touchx+" y:"+touchy+"
      // tvx:"+(int)touchvx+"\ttvy:"+(int)touchvy+
      //                    "\tflying:" + fruitsFlying.size()
      //                            + "\nff vz:" + (fruitsFlying.size() > 0 ? fruitsFlying.get(0).vz
      // : -1)
      //                            + "\nff vy:" + (fruitsFlying.size() > 0 ? fruitsFlying.get(0).vy
      // : -1)
      //                            + " ffvz:" + (fruitsFlying.size() > 0 ? fruitsFlying.get(0).vz :
      // -1),
      //                    , 0, 200, p);
      p.setColor(Color.WHITE);
      p.setTextSize(act.TS_NORMAL);
      p.setTypeface(act.getGameFont());
      p.setFakeBoldText(true);
      // drawtext draws bottom-aligned?
      c.drawText("ROUND: " + round, width - rhstextoffset, statstextheight, p);
      c.drawText("LIVES: " + lives, width - rhstextoffset, statstextheight2, p);
      c.drawText("SCORE: " + score, 10, statstextheight, p);
      if (score >= hiscore) {
        hiscore = score;
        hilev = round;
      }
      c.drawText("HIGH: " + hiscore + ", r" + hilev, 10, statstextheight2, p);

      // game programming!  pure and constant state manipulation!
      // this is like fingernails on a chalkboard for the functional programming crowd
      if (gamestate == State.ROUNDSUMMARY
          || gamestate == State.STARTGAME
          || gamestate == State.PLAYERDIED
          || gamestate == State.GAMEOVER) {
        if (gamestate != State.STARTGAME) {
          // round ended, by completion or player death, display stats
          int splatPct = (int) (nWallSplats * 100 / nTotFruit);

          drawCenteredText(
              c, splatPct + "% sPLAttaGe! (" + minRoundPassPct + "% required)", height / 3, p, 0);
          if (gamestate == State.ROUNDSUMMARY) {
            if (splatPct < 80) drawCenteredText(c, "not too bad.", (int) (height / 2.5), p, 0);
            else if (splatPct < 85) drawCenteredText(c, "nice!", (int) (height / 2.5), p, 0);
            else if (splatPct <= 95) {
              drawCenteredText(c, "cRudE!", (int) (height / 2.5), p, 0);
            } else if (round > 10) {
              c.drawText("Dude, really?!", width / 4, (int) (height / 2.5), p);
              c.drawText("Awesome.", width / 3, (int) (height / 2.2), p);
            } else {
              drawCenteredText(c, "eEEeEeeEh!! sPAzMiC!", (int) (height / 2.5), p, 0);
            }
          } else if (gamestate == State.PLAYERDIED || gamestate == State.GAMEOVER)
            c.drawText("...Ooops.", width / 3, (int) (height / 2.5), p);
        }

        if (gamestate != State.PLAYERDIED && gamestate != State.GAMEOVER) {
          String msg = levelmsgMap.get(Integer.valueOf(round));
          if (msg != null) {
            if (msg.contains(LINE_SPLIT_MARKER)) {
              drawCenteredText(
                  c, msg.substring(0, msg.indexOf(LINE_SPLIT_MARKER)), height * 3 / 5, p, 0);
              drawCenteredText(
                  c, msg.substring(msg.indexOf(LINE_SPLIT_MARKER) + 1), height * 2 / 3, p, 0);
            } else drawCenteredText(c, msg, height * 3 / 5, p, 0);
          }
        }

        if (gamestate != State.GAMEOVER) {
          p.setTextSize(act.TS_BIG);
          p.setColor(Color.RED);
          drawCenteredText(c, "Touch to continue", height * 4 / 5, p, -2);
          p.setColor(Color.WHITE);
          drawCenteredText(c, "Touch to continue", height * 4 / 5, p, 0);
        }
      }
      if (gamestate == State.GAMEOVER) {
        p.setTextSize(act.TS_BIG);
        p.setColor(Color.RED);
        drawCenteredText(c, "GamE oVeR!", height / 2, p, -2);
        drawCenteredText(c, "Touch to end game", height * 4 / 5, p, -2);
        p.setColor(Color.WHITE);
        drawCenteredText(c, "GamE oVeR!", height / 2, p, 0);
        drawCenteredText(c, "Touch to end game", height * 4 / 5, p, 0);
      }

    } catch (Exception e) {
      Log.e(MainActivity.LOG_ID, "draw", e);
      e.printStackTrace();
    }
  }
  public PlayScreen(MainActivity act) {
    p = new Paint();
    this.act = act;
    AssetManager assetManager = act.getAssets();
    try {
      // wall
      InputStream inputStream = assetManager.open("wall1_800x1104_16.png");
      wallbtm = BitmapFactory.decodeStream(inputStream);
      inputStream.close();

      // pear
      pearbtm = new Bitmap[4];
      pearbtm[0] = act.getScaledBitmap("pear1.png");
      pearbtm[1] = act.getScaledBitmap("pear2.png");
      pearbtm[2] = act.getScaledBitmap("pear3.png");
      pearbtm[3] = act.getScaledBitmap("pearsplat1.png");

      // banana
      banbtm = new Bitmap[5];
      banbtm[0] = act.getScaledBitmap("ban1.png");
      banbtm[1] = act.getScaledBitmap("ban2.png");
      banbtm[2] = act.getScaledBitmap("ban3.png");
      banbtm[3] = act.getScaledBitmap("ban4.png");
      banbtm[4] = act.getScaledBitmap("bansplat.png");

      // orange
      orangebtm = new Bitmap[4];
      orangebtm[0] = act.getScaledBitmap("orange1.png");
      orangebtm[1] = act.getScaledBitmap("orange2.png");
      orangebtm[2] = act.getScaledBitmap("orange3.png");
      orangebtm[3] = act.getScaledBitmap("orangesplat.png");

      // nutella
      nutbtm = new Bitmap[5];
      nutbtm[0] = act.getScaledBitmap("nut1.png");
      nutbtm[1] = act.getScaledBitmap("nut2.png");
      nutbtm[2] = act.getScaledBitmap("nut3.png");
      nutbtm[3] = act.getScaledBitmap("nut4.png");
      nutbtm[4] = act.getScaledBitmap("nutsplat.png");

      // ice cream
      icbtm = new Bitmap[5];
      icbtm[0] = act.getScaledBitmap("icecream1.png");
      icbtm[1] = act.getScaledBitmap("icecream2.png");
      icbtm[2] = act.getScaledBitmap("icecream3.png");
      icbtm[3] = act.getScaledBitmap("icecream4.png");
      icbtm[4] = act.getScaledBitmap("icecreamsplat.png");

      // milk
      milkbtm = new Bitmap[5];
      milkbtm[0] = act.getScaledBitmap("milk1.png");
      milkbtm[1] = act.getScaledBitmap("milk2.png");
      milkbtm[2] = act.getScaledBitmap("milk3.png");
      milkbtm[3] = act.getScaledBitmap("milk4.png");
      milkbtm[4] = act.getScaledBitmap("milksplat1.png");

      // ketchup
      ketbtm = new Bitmap[2];
      ketbtm[0] = act.getScaledBitmap("ketch1.png");
      ketbtm[1] = act.getScaledBitmap("ketchsplat.png");

      // initialize types of fruit (seeds), point values
      pearseed = new Seed(pearbtm, 10, Sound.WETSPLAT, SeedType.pearseed);
      orangeseed = new Seed(orangebtm, 15, Sound.WETSPLAT, SeedType.orangeseed);
      banseed = new Seed(banbtm, 20, Sound.WETSPLAT, SeedType.banseed);
      milkseed = new Seed(milkbtm, 25, Sound.SPLAT, SeedType.milkseed);
      icseed = new Seed(icbtm, 30, Sound.SPLAT, SeedType.icseed);
      nutseed = new Seed(nutbtm, 40, Sound.SPLAT, SeedType.nutseed);
      ketseed = new Seed(ketbtm, 0, Sound.KSPLAT, SeedType.ketseed);

      // init combos
      ArrayList<Seed> sl = new ArrayList<Seed>();
      sl.add(pearseed);
      sl.add(orangeseed);
      sl.add(banseed);
      combos.add(new Combo(sl, "Fruit Salad!", 100));
      sl = new ArrayList<Seed>();
      sl.add(milkseed);
      sl.add(banseed);
      combos.add(new Combo(sl, "BanaNa MiLKshaKe!", 80));
      sl = new ArrayList<Seed>();
      sl.add(orangeseed);
      sl.add(icseed);
      combos.add(new Combo(sl, "OrangE CreaM!", 80));
      sl = new ArrayList<Seed>();
      sl.add(milkseed);
      sl.add(nutseed);
      combos.add(new Combo(sl, "chOcolaTe MiLK!", 100));
      sl = new ArrayList<Seed>();
      sl.add(banseed);
      sl.add(icseed);
      sl.add(nutseed);
      combos.add(new Combo(sl, "bAnaNA sPLiT!!", 150));

      levelmsgMap.put(Integer.valueOf(1), "tImE to SPlaT!#fling the fruit at the wall!");
      levelmsgMap.put(Integer.valueOf(LEVEL_ORANGE), "Orange ya glad there's more#fruit to throw?");
      levelmsgMap.put(
          Integer.valueOf(LEVEL_BANANA),
          "Splat pear+orange+banana#in the same place...fruit salad!");
      levelmsgMap.put(
          Integer.valueOf(LEVEL_MILK), "how about Some milk to wash#all that fruit down?");
      levelmsgMap.put(
          Integer.valueOf(LEVEL_KETCHUP), "Don't pop the keTChup packets!#Nobody likes that.");
      levelmsgMap.put(
          Integer.valueOf(LEVEL_ICECREAM), "time for ICE CREAM!#And more combinations!");
      levelmsgMap.put(Integer.valueOf(LEVEL_NUT), "Mmm...chocolate sauce!#Hit those COMBOS!");
      levelmsgMap.put(
          Integer.valueOf(LEVEL_DANCING_FRUIT), "Sometimes...the fruit...#likes to DANCE!");
      levelmsgMap.put(Integer.valueOf(LEVEL_MOREFRUIT1), "more fruit!#fling, fling, fling");
      levelmsgMap.put(Integer.valueOf(LEVEL_MOREFRUIT2), "getting a little crazy now, yes?");

      p.setTypeface(act.getGameFont());
      round = 1;

    } catch (IOException e) {
      Log.d(act.LOG_ID, "wha?", e);
    }
  }