最近在研究android原生的遊戲,主要是打地鼠這款經典小遊戲的熟悉和修改,因為直接copy的是開源的程式碼,不過因為年代久遠,再加上要修改的符合需求,所以做了一些調整,先看介面:
這個介面是用自定義View來實現的,所以可擴充套件性比較強,HP是血量,總共20ms,進行倒數計時操作,Grade是分數,每打中一次地鼠,則增加100分,首先,我們先看看這兩個是如何繪製的:
也就五行程式碼,利用Paint畫筆,設定字型顏色為紅色,字型大小為30sp,利用Canva的drawText()方法進行字型文字的繪製,同時GameAgrs這個類是遊戲屬性的封裝類,裡面的HP_X和HP_Y,Grade_X和Grade_Y分別對應著各自的橫座標和縱座標。
接下來,我們來仔細講解一下這九個老鼠窩的繪製,因為老鼠窩有九個,但是其中的每個屬性都是一樣的,我們先來看看其中一個鼠窩究竟有哪些元素和屬性構成的:
因為是直接拿原始碼直接來改的,所以這裡面的倒數計時有一點問題,利用的是自減而非傳統的CountDownTime等倒數計時的方式,所以會倒數計時會出現一點問題,這個未來會做相應的修改。
得到每個方塊的屬性和方法之後,我們雖然可以繪製鼠窩,但是為了給老鼠一個美麗的家,所以我們設定一下背景和老鼠彈出、撤退的圖片。
利用Map儲存老鼠的三個狀態,暴露出一個getBitmap的方法讓外界進行呼叫,獲取鼠窩的背景和老鼠的三個狀態。
到目前為止,我們完成了單個鼠窩的屬性,背景和老鼠的繪製,接下來我們需要對九個鼠窩進行繪製,由於程式碼太多,就不截圖了,直接看程式碼
public classGameViewextendsView {
public static intspeed=100;//重新整理速度
private static final intsHeight=298;
private booleanisCancel=false;
publicGameView(Context context) {
super(context);
initView(context);
GameView.this.post(updatePaint);//重新整理畫面執行緒
GameView.this.postDelayed(setMouse,1000);//增加地鼠的執行緒,1s後執行
}
publicGameView(Context context,@NullableAttributeSet attrs) {
super(context,attrs);
initView(context);
GameView.this.post(updatePaint);//重新整理畫面執行緒
GameView.this.postDelayed(setMouse,1000);//增加地鼠的執行緒,1s後執行
Log.i("GameViewThread:",Thread.currentThread() +"");
}
publicGameView(Context context,@NullableAttributeSet attrs, intdefStyleAttr) {
super(context,attrs,defStyleAttr);
initView(context);
GameView.this.post(updatePaint);//重新整理畫面執行緒
GameView.this.postDelayed(setMouse,1000);//增加地鼠的執行緒,1s後執行
}
public static voidinitView(Context context){
DisplayMetrics dMetrics =newDisplayMetrics();
//獲取螢幕解析度
dMetrics = context.getResources().getDisplayMetrics();
GameAgrs.InitArgs(dMetrics);
MainMapManager.Init(context);
VolumsPlayer.init(context);
}
intstart_X= GameAgrs.start_X;//開始畫方塊的X座標
intstart_Y= GameAgrs.start_Y;//開始畫方塊的Y座標
privateRandomrandom=newRandom();//定義一個隨機函式
//初始化每塊方塊的資訊
privateListmainList=newArrayList(GameAgrs.Total_Count);
{
for(inti =0;i < GameAgrs.Total_Count;i++) {
mainList.add(newEpicture());
}
}
RunnableupdatePaint=newRunnable() {
@Override
public voidrun() {
GameView.this.invalidate();
GameView.this.postDelayed(this,speed);//每100ms重新整理一次
}
};
Runnable setMouse=new Runnable() {
@Override
public void run() {
LinkedList tempList =new LinkedList();
for(inti =0;i
Epicture epicture =mainList.get(i);
if(epicture.getCurrenty() == GameAgrs.Hole_Empty) {
tempList.add(epicture);
}
}
inttempSize = tempList.size();
if(tempSize ==1) {
tempList.poll().toShow();
}else if(tempSize >1) {
//隨機出一個或者兩個地鼠
for(inti =0;i
tempList.remove(random.nextInt(tempList.size())).toShow();
}
GameView.this.invalidate();
}
GameView.this.postDelayed(this,800- GameAgrs.Grade/20);
}
};
private void drawMessage(Canvas canvas) {
Paint paint =new Paint();
paint.setColor(Color.RED);
paint.setTextSize(GameAgrs.text_size);
canvas.drawText("HP:"+ GameAgrs.HP,GameAgrs.HP_X,GameAgrs.HP_Y,paint);
canvas.drawText("\nGrade: "+ GameAgrs.Grade,GameAgrs.Grade_X,GameAgrs.Grade_Y,paint);
}
//遊戲結束處理
private void doGameOver() {
AlertDialog.Builder builder =new AlertDialog.Builder(getContext());
if(GameAgrs.Grade<2500)
builder.setTitle("繼續努力O(∩_∩)O");
else if(GameAgrs.Grade<3500)
builder.setTitle("那你很棒棒呦(。^▽^)");
else
builder.setTitle("哇!你墜棒(๑•̀ㅂ•́)و✧");
builder.setMessage("得分:"+ GameAgrs.Grade+",是否重新開始遊戲?");
builder.setPositiveButton("確定", newDialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, intwhich) {
GameAgrs.Reset();//使用者資訊重置
isCancel=false;
initView(getContext());
GameView.this.post(updatePaint);//重新整理畫面執行緒
GameView.this.postDelayed(setMouse,1000);
}
});
builder.setNegativeButton("取消", newDialogInterface.OnClickListener() {
@Override
public voidonClick(DialogInterface dialog, intwhich) {
isCancel=true;
GameAgrs.Reset();//使用者資訊重置
initView(getContext());
GameView.this.invalidate();
}
});
builder.setCancelable(false);
AlertDialog dialog = builder.create();
dialog.show();
}
@Override
protected void onMeasure(intwidthMeasureSpec, intheightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
setMeasuredDimension(ViewGroup.LayoutParams.MATCH_PARENT,sHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(GameAgrs.HP<=0) {
getHandler().removeCallbacks(updatePaint);
getHandler().removeCallbacks(setMouse);
doGameOver();
return;
}
canvas.drawColor(Color.WHITE);//將背景設定為白色
drawMessage(canvas);//顯示使用者資訊
if(isCancel) {
for(inti =0;i
Epicture epicture =mainList.get(i);
intx = i % GameAgrs.Colums_Count;//獲得所在列
inty = i / GameAgrs.Colums_Count;//獲得所在行
canvas.drawBitmap(MainMapManager.getBitmap(epicture.getEmpty()),
start_X+ x * GameAgrs.EpicSize,start_Y+ y
* GameAgrs.EpicSize, null);
}
return;
}
for(inti =0;i
Epicture epicture =mainList.get(i);
intx = i % GameAgrs.Colums_Count;//獲得所在列
inty = i / GameAgrs.Colums_Count;//獲得所在行
canvas.drawBitmap(MainMapManager.getBitmap(epicture.getCurrenty()),
start_X+ x * GameAgrs.EpicSize,start_Y+ y
* GameAgrs.EpicSize, null);
epicture.toNext();//進入下一個狀態
}
}
//觸控事件
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() != MotionEvent.ACTION_DOWN) {
return true;
}
floatx = event.getX();
floaty = event.getY();
intX = (int) ((x -start_X) / GameAgrs.EpicSize);
intY = (int) ((y -start_Y) / GameAgrs.EpicSize);
if(X <0|| Y <0|| X >=3|| Y >=4) {//如果打擊不在方塊範圍內則不響應
//點選外圍的時候,則開始遊戲
startGame();
return true;
}
intnum = Y * GameAgrs.Colums_Count+ X;
Log.i("game-----------",num +"");
if(num <9){//暫時先解決陣列出界的問題
Epicture epicture =mainList.get(num);
epicture.beHited();
}
return true;
}
private void startGame() {
GameAgrs.Reset();//使用者資訊重置
initView(getContext());
isCancel=false;
GameView.this.post(updatePaint);//重新整理畫面執行緒
GameView.this.postDelayed(setMouse,1000);
}
}
程式碼不長,但是有幾個方法,首先定義了updatePaint和setMouse的兩個runnable,分別用來更新UI介面和老鼠動作,onDraw()方法用來繪製九個鼠窩和背景和老鼠的動作狀態,onTouchEvent主要用來處理點選事件,具體的可以看看程式碼,都有註釋,並不難理解,doGameOver(),則定義了一個彈窗,點選確定則繼續開始,點選取消,則重置頁面初始狀態。
以上就是打地鼠的程式碼了,雖然不全,但是精華已經全都交代出來了,另外這個還只是最初的修改版本,未來工作中還有更多的修改,包括頁面的重繪,邏輯的修改等等。