手机平台上开发斜45度地图系统的游戏,相信做惯了正面俯视的开发者刚接触总很不习惯。所谓斜45度游戏,也就是常说的2.5D游戏,用斜方向俯视的角度来增强立体感的一种技术。这种技术在PC平台上早就流行了,手机平台由于屏幕表现力的限制,大部分使用正面视角。但随着手机屏幕分辨率不断增大,斜45度视角的游戏出现得越来越多。
斜45度地图系统分Staggered、Slide、Diamond等几种,除了起始位置的区别,与正视地图系统的主要区别在于使用菱形的图块。关于什么是45度地图系统以及其原理,我不想再多说,网上有很多的资料,下面主要讲一讲坐标系的转换。
图1
图2(该图来自云风的博客http://www.codingnow.com/)
如图1,虽然图块是菱形拼接,但图片还是矩形,绘制地图就是要矩形的图片映射到正确的屏幕坐标系上,使图片无缝地拼接起来。如图2,以Diamond地图系统为例,为了简化,掠过滚屏等问题,假设始终从屏幕左上角开始绘制0,0的数据,图中所标的数字是指图块数据下标(data[j][i]中的(j,i))。通过观察你会发现:图片的X位置为数据j,i之差乘以图片宽度的一半,Y位置为数据j,i之和乘以高度的一半,即公式1:
px = (CHIP_W >> 1) * (i - j);
py = (CHIP_H >> 1) * (i + j);
通过公式1,我们就能根据数据数组的下标值获得对应图块在屏幕坐标系中的位置。逆推,得到公式2:
i = 0.5f * (y / (CHIP_H >> 1) + x / (CHIP_W >> 1));
j = 0.5f * (y / (CHIP_H >> 1) - x / (CHIP_W >> 1));
通过公式2,可以把图块位置映射到对应的数据坐标。
下面开始绘制地图内容。传统的俯视角度游戏地图绘制,都是记录屏幕(或摄像机)所在的数据j,i位置,计算屏幕能容纳多少个图块,用一个嵌套循环完成地图的绘制。如下所示:
for(int j=0;j<SCREEN_HEIGHT/CHIP_H;j++)
{
for(int i=0;i<SCREEN_WIDTH/CHIP_W;i++)
{
......//do draw
}
}
图3
但是如图3所示,斜45度地图是斜的,无法用数组的循环遍历来绘制。不过既然有了转换公式,我们可以把屏幕分割成CHIP_W/2*CHIP_H/2的若干个区域,通过“遍历”这些区域的坐标,可以用公式知道,要在这个坐标上画哪张图片。算法如下:
//当paintY为CHIP_H / 2的奇数倍时,paintX需要偏移CHIP_W / 2 int offset = 0; for (int paintY = 0; paintY <= SCREEN_H + CHIP_H; paintY += CHIP_H / 2) { for (int paintX = 0; paintX <= SCREEN_W + CHIP_W; paintX += CHIP_W) { int gx = getGx(paintX + offset, paintY) + startCol; int gy = getGy(paintX + offset, paintY) + startRow; if (gy < 0 || gx < 0 || gy > 10 || gx > 10) { continue; } drawTile(g, data[gy][gx], paintX + offset, paintY); } offset = offset == 0 ? CHIP_W / 2 : 0; }
//屏幕坐标转换成游戏坐标 int getGx(int x, int y) { return (int) (0.5f * (y / (CHIP_H >> 1) + x / (CHIP_W >> 1))); } int getGy(int x, int y) { return (int) (0.5f * (y / (CHIP_H >> 1) - x / (CHIP_W >> 1))); }
关于地图上的碰撞与拾取。当你需要判断精灵所处地图数据位置的时候用公式2,会发现判断误差很严重,仔细想一下不难解释:逆推过来的公式是根据图片之间的坐标系来定义的,而菱形切片之间透明的部分都是重叠的,所以当你判断重叠部分的碰撞时就无法预计判断是那一块数据了,所以用公式2进行地图数据的碰撞判断不可行。
图4
图5 图6
介绍一下我的碰撞方法。如图4,要判断屏幕中任意一点X,Y于数据中的位置。首先按图块的图片尺寸将屏幕分割,计算X,Y位于图中绿色矩形框选的哪个图片中,初步得到j,i。知道了位于哪张图块图片中,如图5,再判断X,Y位于图5中四个角落哪一个区域。结合图6,如果都不位于这四个角落,那X,Y就属于j,i。位于左上角的红色区域,对应i-1,其它三个角落同理。这里的难点在于如何判断红色区域,建议用三角形与点碰撞的算法。这种碰撞拾取算法的优点是精确无误差,而且无论菱形图块比例是32:15 、2:1还是其他比例都可以检测。
数据碰撞检测的主要代码:
int[] checkInDataIJ(int x, int y) { final int I = 0; final int J = 1; int[] data = new int[] { 0, 0 }; Log.e("", "click:" + x + "," + y); int xd = x / CHIP_W; int yd = y / CHIP_H; if (x < 0) { xd -= 1; } if (y < 0) { yd -= 1; } Log.e("", "xd:" + xd + " yd:" + yd); data[I] = yd + xd; data[J] = yd - xd; //计算触摸点位于矩形中,与菱形的位置 int cx = x % CHIP_W; if (cx < 0) { cx += CHIP_W; } int cy = y % CHIP_H; if (cy < 0) { cy += CHIP_H; } //是否位于左上角的三角形 if (MyMath.isInsideTriangle(cx, cy, new int[] { 0, CHIP_W / 2, 0 }, new int[] { 0, 0, CHIP_H / 2 })) { data[I] -= 1; } //是否位于右上角的三角形 else if (MyMath.isInsideTriangle(cx, cy, new int[] { CHIP_W / 2, CHIP_W, CHIP_W }, new int[] { 0, 0, CHIP_H / 2 })) { data[J] -= 1; } //是否位于右下角的三角形 else if (MyMath.isInsideTriangle(cx, cy, new int[] { CHIP_W, CHIP_W, CHIP_W / 2 }, new int[] { CHIP_H / 2, CHIP_H, CHIP_H })) { data[I] += 1; } //是否位于左下角的三角形 else if (MyMath.isInsideTriangle(cx, cy, new int[] { 0, CHIP_W / 2, 0 }, new int[] { CHIP_H / 2, CHIP_H, CHIP_H })) { data[J] += 1; } Log.e("debug", "get(:" + data[J] + "," + data[I] + ")"); return data; }
三角形检测算法:
public static boolean isInsideTriangle(int cx, int cy, int[] x, int[] y) { float vx2 = cx - x[0]; float vy2 = cy - y[0]; float vx1 = x[1] - x[0]; float vy1 = y[1] - y[0]; float vx0 = x[2] - x[0]; float vy0 = y[2] - y[0]; float dot00 = vx0 * vx0 + vy0 * vy0; float dot01 = vx0 * vx1 + vy0 * vy1; float dot02 = vx0 * vx2 + vy0 * vy2; float dot11 = vx1 * vx1 + vy1 * vy1; float dot12 = vx1 * vx2 + vy1 * vy2; float invDenom = 1.0f / (dot00 * dot11 - dot01 * dot01); float u = (dot11 * dot02 - dot01 * dot12) * invDenom; float v = (dot00 * dot12 - dot01 * dot02) * invDenom; return ((u > 0) && (v > 0) && (u + v < 1)); }
在Android实际开发中,我们可能常常需要弹出一个Dialog,让用户输入一些数据,而对用户的输入数据还需要进行一些比如不能为空的验证。但是在正常情况下,用户点击Dialog中的按钮后窗口会随即关闭,用户可能看不到你的错误提示,这样会显得很不友好。那有没有一种解决办法,可以让用户输入出错时单击按钮,继续留在页面而不是关闭本dialog呢?今天在工作中刚好碰到了这个问题,下面介绍一下我的解法办法。
首先看一个截图:
稍微解释一下需求,点击图中的“自定义”按钮弹出添加自定义零件的AlertDialog,该AlertDialog中的零件名称和单价是必填的,当用户没有填零件名称或单价时会给出提示而该AlertDialog不关闭。
看了一下Dialog的源代码,它有一个mShowing成员变量(private boolean mShowing = false;),当点击dialog上的按钮时会先判断mShowing的值,如果为true表示窗口正打开,就将其关闭,反之亦反。所以,我们可以通过手动设置mShowing的值来达到目的。
如上图,我会在点击“保存”按钮时进行用户输入验证,于是我在“保存”按钮的onClick事件中加入下面的代码就可以了:
if(ljmc==null || ljmc.equals("")){ diaCustomName.setError("自定义零件名称不能为空!"); //进行以下设置将不能关闭dialog try { Field field = dialog.getClass().getSuperclass().getDeclaredField("mShowing"); field.setAccessible(true); field.set(dialog, false); } catch (Exception e) { e.printStackTrace(); } }
前面说了,mShowing是Dialog类的一个成员变量,在上面的方法中改变之后会影响别的按钮,如现在点击“取消”按钮将不能关闭对话框,解决办法在“取消”的onClick事件中再改变mShowing的值,如下:
try { Field field = dialog.getClass().getSuperclass().getDeclaredField("mShowing"); field.setAccessible(true); field.set(dialog, true); } catch (Exception e) { e.printStackTrace(); }
安装步骤如下:
1.首先安装需要JAVA环境,先下载JDK/JRE,点击下载,已经有JAVA环境的可跳过此步
2.到code.google上下载apktool.jar以及相关文件:http://code.google.com/p/android-apktool/downloads/list
点击下载apktool-1.0.0.tar.bz2 和apktool-install-windows-2.1_r01-1.zip
3.解压apktool.jar 到 C:\Windows文件夹下
解压apktool-install-windows.zip到任意文件夹
4.点击开始菜单,运行,输入CMD回车,用cd命令转到刚刚解压apktool-install-windows所在的文件夹,输入apktool,出现一些命令说明即成功安装。
Apktool 命令
apktool d geek.apk test 反编译 geek.apk到文件夹test
apktool b test geek-unsigned.apk 把文件夹test打包为geek-unsigned.apk
此时并未签名。需要重新签名:
1。使用源码环境下的key进行签名:
java -jar signapk.jar testkey.x509.pem testkey.pk8 test-unsigned.apk test_signed.apk
2。使用自己的签名信息,进行签名:
jarsigner -verbose -keystore my-release-key.keystore
my_application.apk alias_name
my_application.apk 是需要签名的apk,也是签名之后生成的apk
jarsigner -verbose -keystore my-release-key.keystore -signedjar my_application.apk my_application_signed.apk alias_name
http://www.cnblogs.com/feisky/archive/2010/01/17/1650076.html