alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Android example source code file (GameView.java)

This example Android source code file (GameView.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Android by Example" TM.

Java - Android tags/keywords

android, arraylist, audio, c2b, drawing, graphics, last_rating_miss, last_rating_perfect, paint, penalty_factor, photo, points_for_perfect, pre_threshold, soundpool, string, target, target_radius, thread, ui, vibrator

The GameView.java Android example source code

/*
 * Copyright (C) 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.clickin2dabeat;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.media.AudioManager;
import android.media.SoundPool;
import android.os.Vibrator;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;

/**
 * Draws the beat targets and takes user input. 
 * This view is used as the foreground; the background is the video 
 * being played.
 */
public class GameView extends View {
  private static final long[] VIBE_PATTERN = {0, 1, 40, 41};

  private static final int INTERVAL = 10; // in ms
  private static final int PRE_THRESHOLD = 1000; // in ms
  private static final int POST_THRESHOLD = 500; // in ms
  private static final int TOLERANCE = 100; // in ms
  private static final int POINTS_FOR_PERFECT = 100;
  private static final double PENALTY_FACTOR = .25;
  private static final double COMBO_FACTOR = .1;

  private static final float TARGET_RADIUS = 50;
  
  public static final String LAST_RATING_OK = "(^_')";
  public static final String LAST_RATING_PERFECT = "(^_^)/";
  public static final String LAST_RATING_MISS = "(X_X)";

  private C2B parent;
  private Vibrator vibe;
  private SoundPool snd;
  private int hitOkSfx;
  private int hitPerfectSfx;
  private int missSfx;

  public int comboCount;
  public int longestCombo;
  
  public String lastRating;

  Paint innerPaint;
  Paint borderPaint;
  Paint haloPaint;

  private ArrayList<Target> drawnTargets;
  private int lastTarget;

  public ArrayList<Target> recordedTargets;

  private int score;

  public GameView(Context context) {
    super(context);

    parent = (C2B) context;
    lastTarget = 0;
    score = 0;
    comboCount = 0;
    longestCombo = 0;
    lastRating = "";

    drawnTargets = new ArrayList<Target>();
    recordedTargets = new ArrayList<Target>();

    vibe = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);

    snd = new SoundPool(10, AudioManager.STREAM_SYSTEM, 0);
    missSfx = snd.load(context, R.raw.miss, 0);
    hitOkSfx = snd.load(context, R.raw.ok, 0);
    hitPerfectSfx = snd.load(context, R.raw.perfect, 0);

    innerPaint = new Paint();
    innerPaint.setColor(Color.argb(127, 0, 0, 0));
    innerPaint.setStyle(Paint.Style.FILL);
    borderPaint = new Paint();
    borderPaint.setStyle(Paint.Style.STROKE);
    borderPaint.setAntiAlias(true);
    borderPaint.setStrokeWidth(2);
    haloPaint = new Paint();
    haloPaint.setStyle(Paint.Style.STROKE);
    haloPaint.setAntiAlias(true);
    haloPaint.setStrokeWidth(4);

    Thread monitorThread = (new Thread(new Monitor()));
    monitorThread.setPriority(Thread.MIN_PRIORITY);
    monitorThread.start();
  }


  private void updateTargets() {
    int i = lastTarget;
    int currentTime = parent.getCurrentTime();

    // Add any targets that are within the pre-threshold to the list of
    // drawnTargets
    boolean cont = true;
    while (cont && (i < parent.targets.size())) {
      if (parent.targets.get(i).time < currentTime + PRE_THRESHOLD) {
        drawnTargets.add(parent.targets.get(i));
        i++;
      } else {
        cont = false;
      }
    }
    lastTarget = i;

    // Move any expired targets out of drawn targets
    for (int j = 0; j < drawnTargets.size(); j++) {
      Target t = drawnTargets.get(j);
      if (t == null) {
        // Do nothing - this is a concurrency issue where
        // the target is already gone, so just ignore it
      } else if (t.time + POST_THRESHOLD < currentTime) {
        try {
          drawnTargets.remove(j);
        } catch (IndexOutOfBoundsException e){
          // Do nothing here, j is already gone
        }
        if (longestCombo < comboCount) {
          longestCombo = comboCount;
        }
        comboCount = 0;
        lastRating = LAST_RATING_MISS;
      }
    }
  }


  @Override
  public boolean onTouchEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
      int currentTime = parent.getCurrentTime();
      float x = event.getX();
      float y = event.getY();
      boolean hadHit = false;

      if (parent.mode == C2B.ONEPASS_MODE) { // Record this point as a target
        hadHit = true;
        snd.play(hitPerfectSfx, 1, 1, 0, 0, 1);
        Target targ = new Target(currentTime, (int) x, (int) y, "");
        recordedTargets.add(targ);
      } else if (parent.mode == C2B.TWOPASS_MODE) {
        hadHit = true;
        parent.beatTimes.add(currentTime);
      } else { // Play the game normally
        for (int i = 0; i < drawnTargets.size(); i++) {
          if (hitTarget(x, y, drawnTargets.get(i))) {
            Target t = drawnTargets.get(i);
            int points;
            double timeDiff = Math.abs(currentTime - t.time);
            if (timeDiff < TOLERANCE) {
              points = POINTS_FOR_PERFECT;
              snd.play(hitPerfectSfx, 1, 1, 0, 0, 1);
              lastRating = LAST_RATING_PERFECT;
            } else {
              points = (int) (POINTS_FOR_PERFECT - (timeDiff * PENALTY_FACTOR));
              points = points + (int) (points * (comboCount * COMBO_FACTOR));
              snd.play(hitOkSfx, 1, 1, 0, 0, 1);
              lastRating = LAST_RATING_OK;
            }
            if (points > 0) {
              score = score + points;
              hadHit = true;
            }
            drawnTargets.remove(i);
            break;
          }
        }
      }
      if (hadHit) {
        comboCount++;
      } else {
        if (longestCombo < comboCount) {
          longestCombo = comboCount;
        }
        comboCount = 0;
        snd.play(missSfx, 1, 1, 0, 0, 1);
        lastRating = LAST_RATING_MISS;
      }
      vibe.vibrate(VIBE_PATTERN, -1);
    }
    return true;
  }

  private boolean hitTarget(float x, float y, Target t) {
    if (t == null) {
      return false;
    }
    // Use the pythagorean theorem to solve this.
    float xSquared = (t.x - x) * (t.x - x);
    float ySquared = (t.y - y) * (t.y - y);
    if ((xSquared + ySquared) < (TARGET_RADIUS * TARGET_RADIUS)) {
      return true;
    }
    return false;
  }


  @Override
  public void onDraw(Canvas canvas) {
    if (parent.mode != C2B.GAME_MODE) {
      return;
    }

    int currentTime = parent.getCurrentTime();

    // Draw the circles
    for (int i = 0; i < drawnTargets.size(); i++) {
      Target t = drawnTargets.get(i);
      if (t == null) {
        break;
      }
      // Insides should be semi-transparent
      canvas.drawCircle(t.x, t.y, TARGET_RADIUS, innerPaint);
      // Set colors for the target
      borderPaint.setColor(t.color);
      haloPaint.setColor(t.color);
      // Perfect timing == hitting the halo inside the borders
      canvas.drawCircle(t.x, t.y, TARGET_RADIUS - 5, borderPaint);
      canvas.drawCircle(t.x, t.y, TARGET_RADIUS, borderPaint);
      // Draw timing halos - may need to change the formula here
      float percentageOff = ((float) (t.time - currentTime)) / PRE_THRESHOLD;
      int haloSize = (int) (TARGET_RADIUS + (percentageOff * TARGET_RADIUS));
      canvas.drawCircle(t.x, t.y, haloSize, haloPaint);
    }



    // Score and Combo info
    String scoreText = "Score: " + Integer.toString(score);
    int x = getWidth() - 100; // Fudge factor for making it on the top right
    // corner
    int y = 30;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    paint.setColor(Color.RED);
    paint.setTextAlign(Paint.Align.CENTER);
    paint.setTextSize(24);
    paint.setTypeface(Typeface.DEFAULT_BOLD);
    y -= paint.ascent() / 2;
    canvas.drawText(scoreText, x, y, paint);

    x = getWidth() / 2;
    canvas.drawText(lastRating, x, y, paint);

    String comboText = "Combo: " + Integer.toString(comboCount);
    x = 60;
    canvas.drawText(comboText, x, y, paint);
  }



  private class Monitor implements Runnable {
    public void run() {
      while (true) {
        try {
          Thread.sleep(INTERVAL);
        } catch (InterruptedException e) {
          // This should not be interrupted. If it is, just dump the stack
          // trace.
          e.printStackTrace();
        }
        updateTargets();
        postInvalidate();
      }
    }
  }


}

Other Android examples (source code examples)

Here is a short list of links related to this Android GameView.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2024 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.