@Override
  public boolean onTouch(MotionEvent e) {
    switch (e.getAction()) {
      case MotionEvent.ACTION_DOWN:
        if (gamestate == State.ROUNDSUMMARY
            || gamestate == State.STARTGAME
            || gamestate == State.PLAYERDIED) {
          // Game�̏�����
          randomSpeed = 1;
          gamestate = State.STARTROUND; // prep and start round
          return false; // no followup msgs
        } else if (gamestate == State.GAMEOVER) {
          act.leaveGame(); // user touched after gameover -> back to entry screen
          return false; // no followup msgs
        } else {
          synchronized (fruitsSelectable) {
            Iterator<Fruit> itf = fruitsSelectable.iterator();
            while (itf.hasNext()) {
              Fruit f = itf.next();
              if (f.hasCollision(e.getX(), e.getY()))
                if (f.seed == ketseed) {
                  // user popped ketchup
                  act.playSound(Sound.KSPLAT);
                  f.burst();
                  loseLife();
                  return false; // no followup msgs
                } else {
                  // user picked up a fruit
                  selectedFruit = f;
                }
            }
          }
          if (mVelocityTracker == null) {
            // Retrieve a new VelocityTracker object to watch the velocity of a motion.
            mVelocityTracker = VelocityTracker.obtain();
          } else {
            // Reset the velocity tracker back to its initial state.
            mVelocityTracker.clear();
          }
          // Add a user's movement to the tracker.
          mVelocityTracker.addMovement(e);
        }
        break;

      case MotionEvent.ACTION_MOVE:
        if (selectedFruit != null) {
          selectedFruit.x = e.getX();
          selectedFruit.y = e.getY();
        }
        mVelocityTracker.addMovement(e);
        break;

      case MotionEvent.ACTION_UP:
        if (selectedFruit != null) {
          Fruit f = selectedFruit;
          selectedFruit = null;

          mVelocityTracker.computeCurrentVelocity(1000);
          int pointerId = e.getPointerId(e.getActionIndex());
          float tvx = VelocityTrackerCompat.getXVelocity(mVelocityTracker, pointerId);
          float tvy = VelocityTrackerCompat.getYVelocity(mVelocityTracker, pointerId);

          if (-tvy > 10) {
            // there is upward motion at release-- user threw fruit

            // scale throw speed for display size/density
            tvx = tvx / act.densityscalefactor;
            tvy = tvy / act.densityscalefactor;

            // help ease perspective problem when we release fruit away from the horizontal center
            // of the screen
            tvx += (e.getX() - width / 2) * 3.5 * act.densityscalefactor;

            f.throwFruit(tvx, tvy);
            synchronized (fruitsFlying) {
              fruitsFlying.add(f);
              fruitsSelectable.remove(f);
            }

            // attempting to adjust sound for how hard fruit was thrown horizontally.
            // hardness == 0 --> not thrown with any force
            // hardness == 1 --> thrown as hard as possible
            // assume that 5000 represents "really fast"; z vel should sound "harder" than y-vel
            float hardness = (f.vz - f.vy / 2) / 5000; // vy: up is negative.
            if (hardness >= 1f) hardness = 1.0f;
            if (hardness < .3f) hardness = .3f;
            act.playSound(Sound.THROW, hardness * .9f, hardness * 2);
          }
        }
        mVelocityTracker.recycle();
        // seems to be a bug here on android 4.4, causing IllegalStateException - not addressing,
        // since it doesn't affect game play, and may be resolved in later versions
        break;
    }

    return true;
  }
  @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);
          }
        }
      }
    }
  }