安卓版?zhèn)鞲衅骼弥畮w感操作的打磚塊小游戲

創(chuàng)建一個名為HitBall-master的項目

Ball類:主要是進行打向磚塊的球進行的繪制优质,以及最開始的位置的定義

部分代碼如下:

//發(fā)射坐標
public void shot(int x, int y) {
mSpeed.x = x;
mSpeed.y = y;
}

//繪制圓
public void draw(Canvas canvas) {
canvas.drawCircle(mCenter.x, mCenter.y, mRadius, mPaint);
mCenter.offset(mSpeed.x, mSpeed.y);
}

//設置繪制位置
public void setPosition(int x, int y) {
mCenter.x = x;
mCenter.y = y;
}



public Bat() {
mPaint = new Paint();
mPaint.setColor(Color.MAGENTA);
mSpeed = DEFAULT_SPEED;
mWidth = DEFAULT_WIDTH;
}

//左移
public void moveLeft() {
mBody.offset(-mSpeed, 0);
}
//右移
public void moveRight() {
mBody.offset(mSpeed, 0);
}
//繪制板
public void draw(Canvas canvas) {
canvas.drawRect(mBody, mPaint);
}
//設置位置
public void setBodyPosition(Rect body) {
mBody = body;
}

Brick類:繪制磚塊,實現磚塊的顏色隨機變化霞幅,以及磚塊的生命周期:碰撞后顏色改變,以及最終 的消失

部分代碼如下:


//磚塊顏色
private static int[] sBloodColors = {
Color.RED, Color.YELLOW, Color.GREEN
};

//定義磚塊
public Brick(int row, int col, int width, int height, int blood) {
int left = col * width + BRICK_GAP / 2;
int right = left + width - 3 * BRICK_GAP / 2;
int top = row * height + BRICK_GAP;
int bottom = top + height - BRICK_GAP;
mBody = new Rect(left, top, right, bottom);
mBlood = blood;
this.row = row;
this.col = col;
}

//磚塊繪制
@Override
public void draw(Canvas canvas) {
mPaint.setColor(sBloodColors[mBlood]);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(mBody, mPaint);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawRect(mBody.left + BRICK_BORDER,
mBody.top + BRICK_BORDER,
mBody.right - BRICK_BORDER,
mBody.bottom - BRICK_BORDER,
mPaint);
}

//判斷磚塊數(碰撞則減一)
@Override
public boolean hit() {
mBlood--;
return mBlood < 0;
}

Table類:判斷磚塊打擊的位置以及應該做出的相應反應叮雳,添加了背景音樂温圆,

部分代碼如下

//音樂加載
private void loadSound(Context context) {
mSoundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
mAssetManager = context.getAssets();
try {
String[] filenames = mAssetManager.list("sounds");
for (String filename : filenames) {
AssetFileDescriptor fd = mAssetManager.openFd("sounds/" + filename);
int soundId = mSoundPool.load(fd, 0);
mSounds.add(soundId);
}
} catch (IOException e) {
e.printStackTrace();
}
}

//更新關卡
private void loadLevel() {
//初始化數組,20行诲宇,5列
mCells = new Cell[ROW_NUM][COL_NUM];
mCellWidth = mBoundary.width() / COL_NUM;
mCellHeight = mBoundary.height() / ROW_NUM;
try {
String[] filenames = mAssetManager.list("levels");
// TODO: 應該根據關卡加載
String filename = filenames[0];
loadLevel(filename);
} catch (IOException e) {
e.printStackTrace();
}
}

private void loadLevel(String filename) {
try {
InputStream inputStream = mAssetManager.open("levels/" + filename);
BufferedReader reader =
new BufferedReader(new InputStreamReader(inputStream));
String line;
int row = 0;
Paint paint = new Paint();
while ((line = reader.readLine()) != null) {
String[] cells = line.split(",");
for (int col = 0; col < cells.length; col++) {
String cell = cells[col];
if (cell.equals("x")) {
int blood = (int) (Math.floor(Math.random() * 3));
//繪制磚塊
Cell brick = new Brick(row, col, mCellWidth, mCellHeight, blood);
brick.setPaint(paint);
mCells[row][col] = brick;
}
}
row++;
}
} catch (IOException e) {
e.printStackTrace();
}
}

//設置球體
public void setBall(Ball ball) {
mBall = ball;
mBall.setRadius(mBoundary.width() / 20);
}
//設置球板
public void setBat(Bat bat) {
mBat = bat;
mBat.setWidth(mBoundary.width() / 3);
}

public void showGameOver() {
mShowGameOver = true;
mBall.stop();
}

public void showGamePass() {
mShowGamePass = true;
mBall.stop();
}

public void draw(Canvas canvas) {
canvas.drawColor(Color.LTGRAY);
if (mShowGameOver) {
canvas.drawText("Game Over!", mBoundary.centerX() - 218, mBoundary.centerY(), mPaintGameOver);
} else if (mShowGamePass) {
canvas.drawText("過關了!", mBoundary.centerX() - 168, mBoundary.centerY(), mPaintGameOver);
}
// 繪制邊界
canvas.drawPath(mBoundaryPath, mPaintBoundary);

// 繪制磚塊
for (int row = 0; row < ROW_NUM; row++) {
for (int col = 0; col < COL_NUM; col++) {
Cell cell = mCells[row][col];
if (cell != null) {
cell.draw(canvas);
}
}
}

// 判斷球是否和邊界碰撞(碰撞則改變移動的x.y為相反值)
int hitType = getHitType();
if ((hitType & (HIT_TOP | HIT_BOTTOM)) > 0) {
mBall.reverseYSpeed();
}
if ((hitType & (HIT_LEFT | HIT_RIGHT)) > 0) {
mBall.reverseXSpeed();
}
if (isBatHit() && mBall.isToBottom()) {
mBall.reverseYSpeed();
}
mBall.draw(canvas);

moveBat();
mBat.draw(canvas);
}

//是否球與板是否碰撞
private boolean isBatHit() {
Point c = mBall.getCenter();
float r = mBall.getRadius();
Rect batBody = mBat.getBody();
if (c.x >= batBody.left && c.x <= batBody.right) {
if (c.y - r < batBody.bottom && c.y + r > batBody.top) {
return true;
}
}
return false;
}

//獲取球與球板的碰撞類型
private int getHitType() {
int type = HIT_NONE;
Point c = mBall.getCenter();
float r = mBall.getRadius();
int row = c.y / mCellHeight;
int col = c.x / mCellWidth;
Cell cell = null;
boolean hitCell = false;
Rect body = null;
boolean ballInTable = mBoundary.contains(c.x, c.y);
// 判斷撞頭
if (ballInTable && row > 0) {
cell = mCells[row - 1][col];
if (cell != null) {
body = cell.getBody();
hitCell = c.y > body.bottom && c.y - r <= body.bottom;
if (hitCell) {
playHitBrickSound(cell);
if (cell.hit()) {
mCells[cell.row][cell.col] = null;
}
}
}
}
if (mBall.isToTop() && (c.y - r <= 0 || hitCell)) {
type |= HIT_TOP;
}
// 判斷撞右邊
hitCell = false;
if (ballInTable && col < COL_NUM - 1) {
cell = mCells[row][col + 1];
if (cell != null) {
body = cell.getBody();
hitCell = c.x < body.left && c.x + r >= body.left;
if (hitCell) {
playHitBrickSound(cell);
if (cell.hit()) {
mCells[cell.row][cell.col] = null;
}
}
}
}
if (mBall.isToRight() &&
(c.x + r >= mBoundary.right && c.y < mBoundary.bottom || hitCell)) {
type |= HIT_RIGHT;
}
// 判斷撞左邊
hitCell = false;
if (ballInTable && col > 0) {
cell = mCells[row][col - 1];
if (cell != null) {
body = cell.getBody();
hitCell = c.x > body.right && c.x - r <= body.right;
if (hitCell) {
playHitBrickSound(cell);
if (cell.hit()) {
mCells[cell.row][cell.col] = null;
}
}
}
}
if (mBall.isToLeft() &&
((c.x - r <= 0 && c.y < mBoundary.bottom) || hitCell)) {
type |= HIT_LEFT;
}
// 判斷撞下邊
if (ballInTable && row < ROW_NUM - 1) {
cell = mCells[row + 1][col];
if (cell != null) {
body = cell.getBody();
hitCell = c.y < body.top && c.y + r >= body.top;
if (hitCell) {
playHitBrickSound(cell);
if (cell.hit()) {
mCells[cell.row][cell.col] = null;
}
}
}
}
if (mBall.isToBottom() && hitCell) {
type |= HIT_BOTTOM;
}
return type;
}

//設置碰撞音樂
private void playHitBrickSound(Cell cell) {
mSoundPool.play(mSounds.get(cell.getBlood()), 1f, 1f, 0, 0, 1);
}

// 移動板
public void moveBat() {
if (isBatMoving) {
if (isBatMoveToLeft) {
// 板左移動
if (mBat.getBody().left > mBoundary.left) mBat.moveLeft();
} else {
// 板右移動
if (mBat.getBody().right < mBoundary.right) mBat.moveRight();
}
}
}

//通過觸摸事件移動板
public void startBatMove(MotionEvent e) {
if (mBoundary.contains((int) e.getX(), (int) e.getY())) {
isBatMoving = true;
if (e.getX() > mBoundary.centerX()) { // move right
if (mBat.getBody().right < mBoundary.right) isBatMoveToLeft = false;
} else {
if (mBat.getBody().left > mBoundary.left) isBatMoveToLeft = true;
}
}
}


public void startBatMove(double roll) {
if (isBatMoving) {
if (isBatMoveToLeft) {
if (roll < 8 && roll > -10) {
isBatMoving = false;
} else if (roll <= -10) {
isBatMoveToLeft = true;
}
} else {
if (roll > -8 && roll < 10) {
isBatMoving = false;
} else if (roll >= 10) {
isBatMoveToLeft = false;
}
}
} else {
if (roll <= -10) {
isBatMoving = true;
isBatMoveToLeft = true;
} else if (roll >= 10) {
isBatMoving = true;
isBatMoveToLeft = false;
}
}
}

// 通過傾斜度改變板的形狀
public void changeBatBody(double pitch) {
Rect body = mBat.getBody();
boolean wider = body.width() == mBoundary.width(); //板和邊界寬度是否一致
boolean higher = body.height() > mNormalBatBody.height();//板是否比正常板的高度高
if (wider) {
if (pitch > -25) {//傾斜度判斷
body.left = mNormalBatBody.left;
body.right = mNormalBatBody.right;
}
} else {
if (pitch < -30) {
body.left = mBoundary.left;
body.right = mBoundary.right;
}
}
if (higher) {
if (pitch < 10) {
body.top = mNormalBatBody.top;
}
} else {
if (pitch > 15) {
body.top = body.bottom - 10 * body.height();
}
}
}

public void stopBatMove() {
isBatMoving = false;
}

// 重置body和球
public void reset() {
int left = mBoundary.centerX() - mBat.getWidth() / 2;
int top = mBoundary.bottom - Bat.DEFAULT_HEIGHT;
int right = mBoundary.centerX() + mBat.getWidth() / 2;
int bottom = mBoundary.bottom;
// 繪制body
Rect body = new Rect(left, top, right, bottom);
mNormalBatBody = new Rect(body);
// 設置body位置
mBat.setBodyPosition(body);
// 設置球位置
mBall.setPosition(mBoundary.centerX(), (int) (top - mBall.getRadius()));
mBall.stop();
loadLevel();
mShowGameOver = false;
mShowGamePass = false;
}

//射擊球
public void shotBall() {
mBall.shot(20, -20);
}

//是否球出界
public boolean isBallOutside() {
Point c = mBall.getCenter();
return c.y - 100 > mBoundary.bottom;
}

// 是否還有磚塊
public boolean hasNoneBrick() {
for (int row = 0; row < ROW_NUM; row++) {
for (int col = 0; col < COL_NUM; col++) {
Cell cell = mCells[row][col];
if (cell instanceof Brick) {
return false;
}
}
}
return true;
}
}

GameView類:利用安卓的傳感器操作隨著手機的傾斜角度實現打磚塊的帶體感操作

部分代碼如下:

/**
* 第一步:獲得傳感器管理器
* 第二步:為具體的傳感器注冊監(jiān)聽器
* 第三步:實現具體的監(jiān)聽方法
* public void onSensorChanged(SensorEvent event) {}
public void onAccuracyChanged(Sensor sensor ,int accuracy ){}
*/

public class GameView extends SurfaceView implements Runnable, SurfaceHolder.Callback,
SensorEventListener {
//游戲狀態(tài)常量
public static int STATE_READY = 1;
public static int STATE_PLAY = 2;
public static int STATE_PASS = 3;
public static int STATE_OVER = 4;

private int mState;

private Table mTable;
private Ball mBall;
private Bat mBat;

private boolean mIsRunning;
private GestureDetector mGestureDetector;
private SensorManager mSensorManager;

public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
//實例化手勢識別對象
mGestureDetector = new GestureDetector(getContext(), new GameGestureDetector());
//實例化傳感器管理對象
mSensorManager = (SensorManager) context.getSystemService(context.SENSOR_SERVICE);

//設置旋轉向量傳感器
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
//監(jiān)聽傳感器改變的采樣率是否為適合游戲的速率
boolean ok = mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_GAME);

Log.d("mytag", "ok = " + ok);
Log.d("mytag", "sensor = " + sensor);

//實例化窗口管理器
WindowManager windowManager = (WindowManager) context.getSystemService(context.WINDOW_SERVICE);

//實例化矩形
Rect screenRect = new Rect();

windowManager.getDefaultDisplay().getRectSize(screenRect);

mTable = new Table(context, screenRect);
mBall = new Ball();
mTable.setBall(mBall);
mBat = new Bat();
mTable.setBat(mBat);
mTable.reset();
mState = STATE_READY;
}

//判斷觸摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: //單點觸碰
case MotionEvent.ACTION_MOVE: //觸摸點移動動作
if (mIsRunning && mState == STATE_PLAY) {
//當狀態(tài)為STATE_PLAY球開始移動(mIsRunning mState同為1)
mTable.startBatMove(event);
}
break;
case MotionEvent.ACTION_UP: //單點觸摸離開動作
mTable.stopBatMove();
break;
}
mGestureDetector.onTouchEvent(event);
return true;
}

//監(jiān)聽傳感器的值變化
@Override
public void onSensorChanged(SensorEvent event) {
//
if (!mIsRunning || mState != STATE_PLAY) return;
int sensorType = event.sensor.getType(); //存儲傳感器類型
float[] rotationMatrix;
switch (sensorType) {
case Sensor.TYPE_ROTATION_VECTOR: //為旋轉矢量傳感器(代表設備的方向)
rotationMatrix = new float[16];
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values);
float[] orientationValues = new float[3];
SensorManager.getOrientation(rotationMatrix, orientationValues);
//傾斜度獲取
double pitch = Math.toDegrees(orientationValues[1]);
double roll = Math.toDegrees(orientationValues[2]);
Log.e("mytag", "pitch = " + pitch + ", roll = " + roll);

//球板移動
mTable.startBatMove(roll);
//改變球板大小
mTable.changeBatBody(pitch);
break;

}
}

main類:加載mGameView

部分代碼:

 protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
mGameView = new GameView(this, null);
setContentView(mGameView);
}
}

程序運行截圖:

程序結果截圖1
程序運行結果2
程序截圖結果3

總結:這次實驗使得我對畫布更加了解际歼,還有就是那個安卓傳感器的使用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末姑蓝,一起剝皮案震驚了整個濱河市鹅心,隨后出現的幾起案子,更是在濱河造成了極大的恐慌纺荧,老刑警劉巖旭愧,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異宙暇,居然都是意外死亡输枯,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門占贫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桃熄,“玉大人,你說我怎么就攤上這事型奥◎卟Γ” “怎么了池充?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缎讼。 經常有香客問我收夸,道長,這世上最難降的妖魔是什么血崭? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任卧惜,我火速辦了婚禮,結果婚禮上夹纫,老公的妹妹穿的比我還像新娘咽瓷。我一直安慰自己,他們只是感情好舰讹,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布茅姜。 她就那樣靜靜地躺著,像睡著了一般月匣。 火紅的嫁衣襯著肌膚如雪钻洒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天锄开,我揣著相機與錄音素标,去河邊找鬼。 笑死萍悴,一個胖子當著我的面吹牛头遭,可吹牛的內容都是我干的。 我是一名探鬼主播癣诱,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼计维,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了撕予?” 一聲冷哼從身側響起鲫惶,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎嗅蔬,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體疾就,經...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡澜术,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了猬腰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸟废。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖姑荷,靈堂內的尸體忽然破棺而出盒延,到底是詐尸還是另有隱情缩擂,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布添寺,位于F島的核電站胯盯,受9級特大地震影響,放射性物質發(fā)生泄漏计露。R本人自食惡果不足惜博脑,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望票罐。 院中可真熱鬧叉趣,春花似錦、人聲如沸该押。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚕礼。三九已至烟具,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闻牡,已是汗流浹背净赴。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罩润,地道東北人玖翅。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像割以,于是被迫代替她去往敵國和親金度。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內容