为了给2d游戏做出比较绚丽的效果,最近我真是煞费苦心啊,在3d中做出动态阴影比较好实现,2d中就比较难了,尤其是手机游戏中,要是给每个单位都加一张阴影贴图,容量就太过庞大了,所以应该要想一个比较好的算法能在游戏中动态生成阴影图,下面我给大家介绍一个在2d桌面游戏动态生成阴影的算法,并将它用j2me实现,最终效果如图:
要实现一个阴影首先要了解阴影的特点,当阳光照射到物体表面时,会投影出一个当前物体外观轮廓类似份的黑影,而这个黑影多半又是倾斜的,且是半透明的,并不会完全覆盖地表使之成为一团黑色,那么算法由此就可以得出。
生成阴影的步骤是:
1,将当前动作的原图不透明部分全部变黑
2,将变黑后的图片纵向缩小50%
3,将缩小后的图片倾斜45度
4,半透明黑色的部分
其中每一步都涉及一个比较复杂的算法
下面将挨着讲解每一步的实现,
首先,将动作原图不透明的部分全部变黑,需要操作png图片rgb数组,
使图片的不透明rgb值全部等于0,实现函数如下:
public Image effect_black(Image src) {
int srcW = src.getWidth();
int srcH = src.getHeight();
int[] srcPixels = getPixels(src);
int r = 0;
int g = 0;
int b = 0;
int a = 0;
int argb;
for (int i = 0; i < srcH; i++) {
for (int ii = 0; ii < srcW; ii++) {
argb = srcPixels[i * srcW + ii];
a = ((argb & 0xff000000) >> 24); // alpha channel
r = ((argb & 0x00ff0000) >> 16); // red channel
g = ((argb & 0x0000ff00) >> 8); // green channel
b = (argb & 0x000000ff); // blue channel
if (r != 0) {
r = 0;
}
if (g != 0) {
g = 0;
}
if (b != 0) {
b = 0;
}
srcPixels[i * srcW + ii] = ((a << 24) | (r << 16) | (g << 8) | b);
}
}
return drawPixels(srcPixels, srcW, srcH);
}
第二步,将变黑图片纵向缩小50%
其实也不一定是50%,你可以想象清晨和傍晚的影子是最长的,而中午几乎是个脚下的圈,因此要根据你所设定的游戏发生时间来决定,甚至可以让影子的长度动态改变,可以和以前我帖子中介绍过的昼夜系统结合,较好的模拟真实环境,实现函数如下:
/*
* 纵向调整图片大小,注意参数1~10时会放大
* n 缩放比例
*/
public Image effect_resizeImageH(Image src, int n) {
if (n == 0) {
n = 10;
}
int srcW = src.getWidth();
int srcH = src.getHeight();
int destH = srcH * 10 / n;
int destW = srcW;
int[] destPixels = new int[destW * destH];
int[] srcPixels = getPixels(src);
for (int destY = 0; destY < destH; ++destY) {
for (int destX = 0; destX < destW; ++destX) {
int srcX = (destX * srcW) / destW;
int srcY = (destY * srcH) / destH;
destPixels[destX + destY * destW] = srcPixels[srcX + srcY * srcW];
}
}
return drawPixels(destPixels, destW, destH);
}
第三步,将图片倾斜45度
这点是算法最复杂的部分
因为sprite类本身只能进行90度的整数倍数的旋转,因此要实现任意角度的旋转同样需要我们操作png的像素
其基本的原理是
假设,P(x,y)旋转t角度后,得到P’(x’,y’),P与P’之间的关系如下:
(x’,y’) = (x cos(t) + y sin(t),y cos(t) - x sin(t))下面采用minisoyou的bb大侠的算法,它的算法旋转后比其他算法失真较小,实现的代码如下
/** 透明 **/
public static int TRANSPARENT = 0;
/** 不透明 **/
public static int OPAQUE = 15;
//任意角度翻转,注意角度是弧度值
public Image rotateAny(Image src, double _angle) {
int i, j;
int[] _pixels = getPixels(src);
int _width = src.getWidth();
int _height = src.getHeight();
double radius = Math.sqrt(_width * _width + _height * _height);
int r = (int) radius;
int[] newPixels = new int[r * r];
for (i = 0; i < newPixels.length; i++) {
newPixels = (TRANSPARENT) << 24;
// newPixels = 0x00ffffff;
}
double x2, y2;
int x3, y3;
double cos = Math.cos(_angle);
double sin = Math.sin(_angle);
for (i = 0; i < r; i++) {
x2 = (-r / 2) * cos + (i - r / 2) * sin;
y2 = (r / 2) * sin + (i - r / 2) * cos;
x3 = (int) x2;
y3 = (int) y2;
for (j = 0; j < r; j++) {
if (x3 >= -_width / 2 && x3 < _width / 2 && y3 >= -_height / 2 && y3 < _height / 2) {
newPixels[i * r + j] = _pixels[(y3 + _height / 2) * _width + x3 + _width / 2];
}
x2 += cos;
y2 -= sin;
x3 = (int) x2;
y3 = (int) y2;
}
}
return drawPixels(newPixels, r, r);
}
最后,半透明黑色的部分
注意是只半透明黑色部分,则需要判断png中的每个像素的透明度
实现代码如下:
/** * * @param img * 原始图片 * @param transparent * 透明度 0-255之间 * 越大越不透明,只半透明黑色部分 * @return 处理透明度后的图片 */ public Image effect_transparent_black(Image img, int transparent) { if (transparent < 0 || transparent > 255) { return img; } int srcW = img.getWidth(); int srcH = img.getHeight(); int[] srcPixels = getPixels(img); //函数功能 讲图片数据存入指定数组 int r = 0; int g = 0; int b = 0; int a = 0; int argb; for (int i = 0; i < srcH; i++) { for (int ii = 0; ii < srcW; ii++) { argb = srcPixels[i * srcW + ii]; a = ((argb & 0xff000000) >> 24); // alpha channel r = ((argb & 0x00ff0000) >> 16); // red channel g = ((argb & 0x0000ff00) >> 8); // green channel b = (argb & 0x000000ff); // blue channel if (argb == 0xff000000) { srcPixels[i * srcW + ii] = ((transparent << 24) | (r << 16) | (g << 8) | b); } else { srcPixels[i * srcW + ii] = 0x00ffffff; } } } return drawPixels(srcPixels, srcW, srcH); //将数组转化为图片 }
最后你在绘制时需要注意,需要先绘制阴影再绘制人物或建筑,毕竟,影子是从脚下“长“出来的,而不是头上
希望大家支持~~(转自 hustGame)
要实现一个阴影首先要了解阴影的特点,当阳光照射到物体表面时,会投影出一个当前物体外观轮廓类似份的黑影,而这个黑影多半又是倾斜的,且是半透明的,并不会完全覆盖地表使之成为一团黑色,那么算法由此就可以得出。
生成阴影的步骤是:
1,将当前动作的原图不透明部分全部变黑
2,将变黑后的图片纵向缩小50%
3,将缩小后的图片倾斜45度
4,半透明黑色的部分
其中每一步都涉及一个比较复杂的算法
下面将挨着讲解每一步的实现,
首先,将动作原图不透明的部分全部变黑,需要操作png图片rgb数组,
使图片的不透明rgb值全部等于0,实现函数如下:
public Image effect_black(Image src) {
int srcW = src.getWidth();
int srcH = src.getHeight();
int[] srcPixels = getPixels(src);
int r = 0;
int g = 0;
int b = 0;
int a = 0;
int argb;
for (int i = 0; i < srcH; i++) {
for (int ii = 0; ii < srcW; ii++) {
argb = srcPixels[i * srcW + ii];
a = ((argb & 0xff000000) >> 24); // alpha channel
r = ((argb & 0x00ff0000) >> 16); // red channel
g = ((argb & 0x0000ff00) >> 8); // green channel
b = (argb & 0x000000ff); // blue channel
if (r != 0) {
r = 0;
}
if (g != 0) {
g = 0;
}
if (b != 0) {
b = 0;
}
srcPixels[i * srcW + ii] = ((a << 24) | (r << 16) | (g << 8) | b);
}
}
return drawPixels(srcPixels, srcW, srcH);
}
第二步,将变黑图片纵向缩小50%
其实也不一定是50%,你可以想象清晨和傍晚的影子是最长的,而中午几乎是个脚下的圈,因此要根据你所设定的游戏发生时间来决定,甚至可以让影子的长度动态改变,可以和以前我帖子中介绍过的昼夜系统结合,较好的模拟真实环境,实现函数如下:
/*
* 纵向调整图片大小,注意参数1~10时会放大
* n 缩放比例
*/
public Image effect_resizeImageH(Image src, int n) {
if (n == 0) {
n = 10;
}
int srcW = src.getWidth();
int srcH = src.getHeight();
int destH = srcH * 10 / n;
int destW = srcW;
int[] destPixels = new int[destW * destH];
int[] srcPixels = getPixels(src);
for (int destY = 0; destY < destH; ++destY) {
for (int destX = 0; destX < destW; ++destX) {
int srcX = (destX * srcW) / destW;
int srcY = (destY * srcH) / destH;
destPixels[destX + destY * destW] = srcPixels[srcX + srcY * srcW];
}
}
return drawPixels(destPixels, destW, destH);
}
第三步,将图片倾斜45度
这点是算法最复杂的部分
因为sprite类本身只能进行90度的整数倍数的旋转,因此要实现任意角度的旋转同样需要我们操作png的像素
其基本的原理是
假设,P(x,y)旋转t角度后,得到P’(x’,y’),P与P’之间的关系如下:
(x’,y’) = (x cos(t) + y sin(t),y cos(t) - x sin(t))下面采用minisoyou的bb大侠的算法,它的算法旋转后比其他算法失真较小,实现的代码如下
/** 透明 **/
public static int TRANSPARENT = 0;
/** 不透明 **/
public static int OPAQUE = 15;
//任意角度翻转,注意角度是弧度值
public Image rotateAny(Image src, double _angle) {
int i, j;
int[] _pixels = getPixels(src);
int _width = src.getWidth();
int _height = src.getHeight();
double radius = Math.sqrt(_width * _width + _height * _height);
int r = (int) radius;
int[] newPixels = new int[r * r];
for (i = 0; i < newPixels.length; i++) {
newPixels = (TRANSPARENT) << 24;
// newPixels = 0x00ffffff;
}
double x2, y2;
int x3, y3;
double cos = Math.cos(_angle);
double sin = Math.sin(_angle);
for (i = 0; i < r; i++) {
x2 = (-r / 2) * cos + (i - r / 2) * sin;
y2 = (r / 2) * sin + (i - r / 2) * cos;
x3 = (int) x2;
y3 = (int) y2;
for (j = 0; j < r; j++) {
if (x3 >= -_width / 2 && x3 < _width / 2 && y3 >= -_height / 2 && y3 < _height / 2) {
newPixels[i * r + j] = _pixels[(y3 + _height / 2) * _width + x3 + _width / 2];
}
x2 += cos;
y2 -= sin;
x3 = (int) x2;
y3 = (int) y2;
}
}
return drawPixels(newPixels, r, r);
}
最后,半透明黑色的部分
注意是只半透明黑色部分,则需要判断png中的每个像素的透明度
实现代码如下:
/** * * @param img * 原始图片 * @param transparent * 透明度 0-255之间 * 越大越不透明,只半透明黑色部分 * @return 处理透明度后的图片 */ public Image effect_transparent_black(Image img, int transparent) { if (transparent < 0 || transparent > 255) { return img; } int srcW = img.getWidth(); int srcH = img.getHeight(); int[] srcPixels = getPixels(img); //函数功能 讲图片数据存入指定数组 int r = 0; int g = 0; int b = 0; int a = 0; int argb; for (int i = 0; i < srcH; i++) { for (int ii = 0; ii < srcW; ii++) { argb = srcPixels[i * srcW + ii]; a = ((argb & 0xff000000) >> 24); // alpha channel r = ((argb & 0x00ff0000) >> 16); // red channel g = ((argb & 0x0000ff00) >> 8); // green channel b = (argb & 0x000000ff); // blue channel if (argb == 0xff000000) { srcPixels[i * srcW + ii] = ((transparent << 24) | (r << 16) | (g << 8) | b); } else { srcPixels[i * srcW + ii] = 0x00ffffff; } } } return drawPixels(srcPixels, srcW, srcH); //将数组转化为图片 }
最后你在绘制时需要注意,需要先绘制阴影再绘制人物或建筑,毕竟,影子是从脚下“长“出来的,而不是头上
希望大家支持~~(转自 hustGame)