整理下自己最近尝试 开发的连连看小游戏吧,实现了游戏基本的一些功能,例如碰撞是否能连线,连线,点击标记等一些很基本的功能。图片来自于互联网,下面是游戏运行的一些图:
下面来说下开发过程中遇到的一些问题吧:
1.在xml里嵌套一个view抛出异常,谷歌百度,问题解决了,原来是要实现一个构造函数即可,注意,在xml里嵌套的那个view报名不要写错了,例如可以写成:
<com.love.myView.GameView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/gameView"/>
要实现的那个构造函数
public GameView(Context context, AttributeSet attrs) {//注:在xml里使用这一个类必须得添加该构造函数
super(context, attrs);
paint = new Paint();
//在view里获取分辨率
DisplayMetrics dm = context.getApplicationContext().getResources().getDisplayMetrics();
screenWidth = dm.widthPixels;
screenHeight = dm.heightPixels;
//per_Row = screenHeight/ ROW;
//per_Col = (screenWidth - 20)/ COL;
//init();
//canvas = new Canvas();
bmp = new Bitmap[id.length];
setFocusable(true);
setFocusableInTouchMode(true);
}
2.在xml里嵌套的那个view如何确定大小
自己也纠结了这个好久,到最后还是被我找到了,哈哈,其实只要复写protected void onSizeChanged(int w, int h, int oldw, int oldh)函数即可,部分代码如下:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO Auto-generated method stub
width = w / ROW;
height = h / COL;
System.out.println("每列的宽度为:" + width);
super.onSizeChanged(w, h, oldw, oldh);
}
3.如何判断所点击的图能否连线
这个我是参照一位前辈写的,博客网址是:http://www.iteye.com/topic/480020,里面讲的灰常清楚,非常值得去看下。思路是:分为四种判断形式:分别为水平判断,垂直判断,一个拐角的判断和两个拐角的判断。自己有点难懂的是对于两个拐角的实现,这里说下自己的理解吧:首先可以通过scan函数来保存那些两个拐角的点,例如有源点A和目标点D,可以先假设A点向右扫描,扫描到B,建立C点坐标 ,这时如果可以保证B,C之间可以直接垂直连通,那么就可以通过判断A和B,C和D是否同时水平连通,如果符合的话,这样就说明A和D符合两个拐角 ,可以连线了。不符合的话,可以继续扫描到B1,建立坐标C1,继续判断,其他情况也类似。scan函数所做的就是在B和C可以直接连通的情况下,把B和C点加入链表中,到时再取出来判断。图如下所示:
下面是部分代码:
private boolean horizon(Point1 a, Point1 b) {//水平判断
if (a.x == b.x && a.y == b.y)
return false;
int x_start = a.y <= b.y ? a.y : b.y;
int x_end = a.y <= b.y ? b.y : a.y;
for (int x = x_start + 1; x < x_end; x++)
if (grid[a.x][x] != 0) {
return false;
}
p = new Point1[] { a, b };
lineType = H_LINE;
return true;
}
public boolean vertical(Point1 a,Point1 b)//垂直判断
{
if(a.x == b.x && a.y == b.y)return false;
int start = a.x <= b.x ? a.x:b.x;
int end = a.x >= b.x?a.x:b.x;
for(int i = start + 1; i < end; i++)
{
if(grid[i][a.y] != 0)return false;
}
p = new Point1[]{a, b };
lineType = V_LINE;
return true;
}
public boolean oneCorner(Point1 a,Point1 b)//一个拐角的判断
{
Point1 c = new Point1(a.x, b.y);
Point1 d = new Point1(b.x, a.y);
if(grid[c.x][c.y] == 0)
{
boolean method1 = horizon(a, c) && vertical(b, c);
p = new Point1[] { a, new Point1(c.x, c.y), b };
lineType = ONE_LINE;
return method1;
}
if(grid[d.x][d.y] == 0)
{
boolean method2 = vertical(a, d) && horizon(b, d);
p = new Point1[] { a, new Point1(d.x, d.y), b };
lineType = ONE_LINE;
return method2;
}
else {return false;}
}
/*
* scan函数实现的是原点所扫描的上下左右四个方向以及终点组成的线的两个坐标加入到Linkedlist表中去
*/
private LinkedList<Line> scan(Point1 a,Point1 b) {//扫描所有可走路径
li = new LinkedList<Line>();
for (int y = a.y; y >= 0; y--)//纵向向上扫描,把纵向所有向上的点都加进去
if (grid[a.x][y] == 0 && grid[b.x][y] == 0
&& vertical(new Point1(a.x, y), new Point1(b.x, y)))
li.add(new Line(0, new Point1(a.x, y), new Point1(b.x, y)));
for (int y = a.y; y < ROW; y++)//纵向向下扫描,把纵向所有向上的点都加进去
if (grid[a.x][y] == 0 && grid[b.x][y] == 0
&& vertical(new Point1(a.x, y), new Point1(b.x, y)))
li.add(new Line(0, new Point1(a.x, y), new Point1(b.x, y)));
for (int x = a.x; x >= 0; x--)//横向向左扫描,把横向所有向左的点都加进去
if (grid[x][a.y] == 0 && grid[x][b.y] == 0
&& horizon(new Point1(x, a.y), new Point1(x, b.y)))
li.add(new Line(1, new Point1(x, a.y), new Point1(x, b.y)));
for (int x = a.x; x < COL; x++)//横向向右扫描,把横向所有向右的点都加进去
if (grid[x][a.y] == 0 && grid[x][b.y] == 0
&& horizon(new Point1(x, a.y), new Point1(x, b.y)))
li.add(new Line(1, new Point1(x, a.y), new Point1(x, b.y)));
return li;
}
private boolean twoCorner(Point1 a,Point1 b)//两个拐角的判断
{
li = scan(a, b);
if(li.isEmpty())return false;
for (int index = 0; index < li.size(); index++) {//遍历所有可能的情况
Line line = li.get(index);
if(line.direct == 1)
{
if(vertical(a, line.a) && vertical(b, line.b))
{
System.out.println("二次转角");
p = new Point1[]{a,line.a,line.b,b};
lineType = TWO_LINE;
return true;
}
}
else if(horizon(a, line.a) && horizon(b, line.b))
{
p = new Point1[]{a,line.a,line.b,b};
System.out.println("二次转角");
lineType = TWO_LINE;
return true;
}
}
return false;
}
public boolean checkLink(Point1 a,Point1 b)
{
if (grid[a.x][a.y] != grid[b.x][b.y])// 如果图案不同,直接为false
return false;
if (a.x == b.x && horizon(a, b))
return true;
if (a.y == b.y && vertical(a, b))
return true;
if (oneCorner(a, b))
return true;
else
return twoCorner(a, b);
}
4.怎样实现连线
可以这样做:建立一个数组用来保存连线 所有 的点,在判断可以连线的时候可以把连线的类型保存下来,例如,可以是一个拐角 ,两个拐角等。等到 在画图的时候就可以通过判断连线的类型,把数组中的点取出来,在进行画线,例如,如果是两个拐角的话,数组就应该保存四个节点, 如果是一个拐角的话,就应该是三个节点。部分代码如下:
public void drawLines(Canvas canvas){
need_line = false;
Paint lineColor = new Paint();
lineColor.setStrokeWidth(3.0f);
lineColor.setColor(Color.GREEN);
switch (lineType) {
case H_LINE:
canvas.drawLine(p[0].x * width + width / 2, p[0].y * height
+ height / 2, p[1].x * width + width / 2, p[1].y
* height + height / 2, lineColor);
break;
case V_LINE:
canvas.drawLine(p[0].x * width + width / 2, p[0].y * height
+ height / 2, p[1].x * width + width / 2, p[1].y
* height + height / 2, lineColor);
break;
case ONE_LINE:
canvas.drawLine(p[0].x * width + width / 2, p[0].y * height
+ height / 2, p[1].x * width + width / 2, p[1].y
* height + height / 2, lineColor);
canvas.drawLine(p[1].x * width + width / 2, p[1].y * height
+ height / 2, p[2].x * width + width / 2, p[2].y
* height + height / 2, lineColor);
break;
case TWO_LINE:
canvas.drawLine(p[0].x * width + width / 2, p[0].y * height
+ height / 2, p[1].x * width + width / 2, p[1].y
* height + height / 2, lineColor);
canvas.drawLine(p[1].x * width + width / 2, p[1].y * height
+ height / 2, p[2].x * width + width / 2, p[2].y
* height + height / 2, lineColor);
canvas.drawLine(p[3].x * width + width / 2, p[3].y * height
+ height / 2, p[2].x * width + width / 2, p[2].y
* height + height / 2, lineColor);
break;
default:
break;
}
让线一会消失的思路,我的思路是这样的。设置一个布尔变量,如果判断需要画线的时候,布尔变量为真,在 绘制函数中绘图,画完之后,布尔变量为假,在invalidate重新刷新一次,不过这样做效率不高,不过自己也没找到啥好方法,这个只能留给以后再慢慢想了。同样,这个问题,也同样困扰了我点击标记图片为已选,功能是实现了,可是反应有点慢,标记图片部分代码如下:
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
System.out.println("点击了" + event.getX() / width +"列的图片");
if(event.getAction() != MotionEvent.ACTION_DOWN)return true;
int num_x = (int) (event.getX() / width);
int num_y = (int) (event.getY() / height);
if(grid[num_x][num_y] == 0)
{
return true;
}
else {
if(!hasSelect)//如果当前没有选定图案
{
hasSelect = true;
p1 = new Point1(num_x, num_y);
//invalidate(rect);//消除点击图片的标志,待定中
rect.set(num_x * ((int)width),num_y * ((int)height),
(num_x + 1) * ((int)width),(num_y + 1) * ((int)height));
//isSelected = true;
invalidate(rect);//局部刷新特定的区域(这里是一个网格 内的颜色)
}
else {//之前已经选定了图案
p2 = new Point1(num_x, num_y);
if(checkLink(p2, p1))
{
need_line = true;
rid_rect = true;
System.out.println("可以消去");
hasSelect = false;
hero_num = hero_num - 2;
grid[p1.x][p1.y] = 0;
grid[p2.x][p2.y] = 0;
invalidate();//待定中
//invalidate(rect);
}
else//如果之前选择了图案而这次选择的图案不能连线
{
System.out.println("不能消去");
hasSelect = true;
p1 = p2;
invalidate(rect);//消除点击图片的标志
rect.set(num_x * ((int)width),num_y * ((int)height),
(num_x + 1) * ((int)width),(num_y + 1) * ((int)height));
//isSelected = true;
invalidate(rect);
}
}
}
//click[0] = num_x;
//click[1] = num_y;
//selImage = BitmapFactory.decodeResource(getResources(), grid[num_x][num_y]);
//invalidate();
return super.onTouchEvent(event);
}
还有一个问题就是,游戏过程中 不能自动检测游戏是否进入了“死局"的情况。,问题还是存在不少,不扯了,以后还得要更加努力加油啊
。