bri*_*ore 22
有很多方法可以实现这一目标.花一些时间做出最终决定.我将简要总结一些您可以选择使用的技术并最终提供一些代码.
如果你想创建一个硬边照明效果(如你的示例图像),我想到了一些方法:

问题是,既不能使瓷砖比以前更亮(高光),也不能改变光的颜色.这些都是通常使游戏中的照明看起来很好的方面.
这种方法与第一种方法具有相同的效果,具有优势,您现在可以使用除黑色之外的另一种颜色为覆盖图块着色,这允许彩色光和高光.
例:

即使这很容易,但问题是,这确实是一种非常无效的方式.(每个拼贴两个渲染的拼贴,不断重新着色,许多渲染操作等)
在看你的例子时,我想光总是来自特定的源瓦(角色,火炬等)


柔和的光线现在没什么大不了的,只需在光罩中使用更多细节,而不是瓷砖.通过在通常的黑色区域中仅使用15%的alpha,您可以在瓷砖未点亮时添加低视效:

您甚至可以通过更改遮罩图像轻松实现更复杂的照明形式(视锥细胞等).
当组合多个光源时,这种方法会导致一个问题:绘制两个相互交叉的掩模可能会自行取消:

我们想要的是他们添加灯而不是减去它们.避免问题:
这将产生类似于此的东西:

假设你先渲染所有的瓷砖BufferedImage,我会提供一些类似于最后显示的方法的指导代码(只有灰度支持).
例如火炬和玩家的多个光罩可以像这样组合:
public BufferedImage combineMasks(BufferedImage[] images)
{
// create the new image, canvas size is the max. of all image sizes
int w, h;
for (BufferedImage img : images)
{
w = img.getWidth() > w ? img.getWidth() : w;
h = img.getHeight() > h ? img.getHeight() : h;
}
BufferedImage combined = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
// paint all images, preserving the alpha channels
Graphics g = combined.getGraphics();
for (BufferedImage img : images)
g.drawImage(img, 0, 0, null);
return combined;
}
Run Code Online (Sandbox Code Playgroud)
使用此方法创建并应用最终掩码:
public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask)
{
int width = image.getWidth();
int height = image.getHeight();
int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);
for (int i = 0; i < imagePixels.length; i++)
{
int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
// get alpha from color int
// be careful, an alpha mask works the other way round, so we have to subtract this from 255
int alpha = (maskPixels[i] >> 24) & 0xff;
imagePixels[i] = color | alpha;
}
image.setRGB(0, 0, width, height, imagePixels, 0, width);
}
Run Code Online (Sandbox Code Playgroud)
如上所述,这是一个原始的例子.实现颜色混合可能需要更多工作.
Mar*_*ank 10
也许你不会沿着整个地图走路,也许只要你设置你的光线跟踪20x20视图就足够了?注意:你真的必须沿着视口的边界走,它不需要跟踪每个点.
我正在添加线算法来简化您的工作:
public static ArrayList<Point> getLine(Point start, Point target) {
ArrayList<Point> ret = new ArrayList<Point>();
int x0 = start.x;
int y0 = start.y;
int x1 = target.x;
int y1 = target.y;
int sx = 0;
int sy = 0;
int dx = Math.abs(x1-x0);
sx = x0<x1 ? 1 : -1;
int dy = -1*Math.abs(y1-y0);
sy = y0<y1 ? 1 : -1;
int err = dx+dy, e2; /* error value e_xy */
for(;;){ /* loop */
ret.add( new Point(x0,y0) );
if (x0==x1 && y0==y1) break;
e2 = 2*err;
if (e2 >= dy) { err += dy; x0 += sx; } /* e_xy+e_x > 0 */
if (e2 <= dx) { err += dx; y0 += sy; } /* e_xy+e_y < 0 */
}
return ret;
}
Run Code Online (Sandbox Code Playgroud)
我前段时间做了这一整个闪电事件,一个*pathfindin随时可以提出进一步的问题
附录:也许我可以简单地为光线追踪添加小算法^^
获取南北边界点只需使用此代码段:
for (int x = 0; x <map.WIDTH; x++){
Point northBorderPoint = new Point(x,0);
Point southBorderPoint = new Point(x,map.HEIGHT);
rayTrace( getLine(player.getPos(), northBorderPoint), player.getLightRadius()) );
rayTrace( getLine(player.getPos(), southBorderPoint, player.getLightRadius()) );
}
Run Code Online (Sandbox Code Playgroud)
而光线跟踪的工作方式如下:
private static void rayTrace(ArrayList<Point> line, WorldMap map, int radius) {
//int radius = radius from light source
for (Point p: line){
boolean doContinue = true;
float d = distance(line.get(0), p);
//caclulate light linear 100%...0%
float amountLight = (radius - d) / radius;
if (amountLight < 0 ){
amountLight = 0;
}
map.setLight( p, amountLight );
if ( ! map.isViewBlocked(p) ){ //can be blockeb dy wall, or monster
doContinue = false;
break;
}
}
}
Run Code Online (Sandbox Code Playgroud)