最近在研究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(),则定义了一个弹窗,点击确定则继续开始,点击取消,则重置页面初始状态。
以上就是打地鼠的代码了,虽然不全,但是精华已经全都交代出来了,另外这个还只是最初的修改版本,未来工作中还有更多的修改,包括页面的重绘,逻辑的修改等等。