/** player lost a life */ private void loseLife() { lives--; act.playSound(Sound.DEATH); if (lives == 0) { // game over! wrap things up and write hi score file gamestate = State.GAMEOVER; try { BufferedWriter f = new BufferedWriter(new FileWriter(act.getFilesDir() + HISCORE_FILE)); f.write(Integer.toString(hiscore) + "\n"); f.write(Integer.toString(hilev) + "\n"); f.close(); } catch (Exception e) { // if we can't write the hi score file...oh well. Log.d(MainActivity.LOG_ID, "WriteHiScore", e); } } else gamestate = State.PLAYERDIED; }
@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); } } } } }