简体   繁体   English

Java游戏2D重叠阴影与Swing

[英]Java game 2D overlapping shadows with Swing

I am currently developing a 2D Java game using Swing as my primary drawing component. 我目前正在使用Swing作为我的主要绘图组件开发2D Java游戏。 Every object has a shadow (BufferedImage) but every shadow overlaps other shadows. 每个对象都有一个阴影(BufferedImage),但每个阴影都与其他阴影重叠。 Is it possible to only have the shadows not overlap each other? 是否可以只让阴影不相互重叠? Because I still want the shadows to draw over the player if the object is beneath it, and not if the object is above of the player. 因为如果对象在它下面,我仍然希望阴影在玩家上方绘制,而不是如果对象在玩家之上。 Here is a picture for clarity: 为清晰起见,这是一张图片:

截图

I have looked at alpha compositing, I guess I need Source Out? 我看过alpha合成,我想我需要Source Out? I also thought of having all the shadows (with no transparency) draw on one layer and then draw it with transparency but then it won't draw over the player and other objects like before. 我还想过让所有阴影(没有透明度)在一个图层上绘制,然后用透明度绘制它,然后它不会像以前那样在玩家和其他对象上绘制。

I have a Draw object which is a JPanel and overrides the paintComponent method. 我有一个Draw对象,它是一个JPanel并覆盖paintComponent方法。 Within this method I draw the floor of the current room and then I iterate over the list of objects that belongs to the current room and call each objects' draw method to draw everything. 在这个方法中,我绘制当前房间的地板,然后迭代属于当前房间的对象列表,并调用每个对象的绘制方法来绘制所有内容。

The object draw method: 对象绘制方法:

public void draw(Graphics g) {
    if (visible && checkInScreen()) {

        // The required drawing location
        int drawLocationX = getX() - globalCameraX;
        int drawLocationY = getY() - globalCameraY;

        if (shadow) {
            g.drawImage(shadowImages.get(imageIndex),
                    drawLocationX + shadowOffset.x + (getImageWidth()/2),
                    drawLocationY + shadowOffset.y, null);
        }
        g.drawImage(images.get(imageIndex), drawLocationX, drawLocationY, null);

        //Collisionbox
        if (SHOW_COLLISION_BOXES){
            g.setColor(Color.WHITE);
            g.drawRect(drawLocationX + getCollBoxX(), drawLocationY + getCollBoxY(), getCollBoxW() - getCollBoxX(), getCollBoxH() - getCollBoxY());
        }
    }
}

My apologies if this question has already been asked but I couldn't find something similar like this. 如果这个问题已被提出,我很抱歉,但我找不到类似的东西。

What I would do to solve this is to have a shadow-layer bitmap. 我要做的就是有一个阴影层位图。 By which I mean: have your shadow textures saved as a 2D array of boolean values (representing the position of a shadow pixel). 我的意思是:将阴影纹理保存为布尔值的二维数组(表示阴影像素的位置)。

What you can do with this is to then logically or the shadow maps together to create a single layer, which can be layered behind the tree textures to create the shadows. 你可以用它来做逻辑或阴影贴图一起创建一个单独的图层,可以在树纹理后面分层以创建阴影。

You may want to change the booleans to floats to represent the colour/intensity of the shadow, then have a larger calculation to merge the shadows together. 您可能希望将布尔值更改为浮点数以表示阴影的颜色/强度,然后使用更大的计算将阴影合并在一起。

The below ShadowMap class is used to store the data for each shadow: 下面的ShadowMap类用于存储每个阴影的数据:

class ShadowMap {

    public int xPos, yPos;
    public boolean[][] array;

    public ShadowMap(int xPos, int yPos, boolean[][] array) {
        this.xPos = xPos;
        this.yPos = yPos;
        this.array = array;
    }

}

The ShadowLayer class creates a 2D array for the entire screen, containing if a shadow is present for each pixel: ShadowLayer类为整个屏幕创建一个2D数组,包含每个像素是否存在阴影:

class ShadowLayer {

    public static boolean[][] array = new boolean[SCREEN_WIDTH][SCREEN_HEIGHT];

    public static makeNew(ShadowMap[] shadows) {
        for (int x = 0; x < SCREEN_WIDTH; x++) {
            for (int y = 0; y < SCREEN_HEIGHT; y++) {
                array[x][y] = false;
            }
        }
        for (ShadowMap map : shadows) {
            for (int i = 0; i < SCREEN_WIDTH; i++) {
                for (int j = 0; j < SCREEN_HEIGHT; j++) {
                    // Logical or such that the pixel at (x, y) has a shadow 
                    // if any shadow map also has a shadow at pixel (x, y)
                    array[i + map.xPos][j + map.yPos] |= map.array[i][j];
                }
            }
        }
    }

}

Using this ShadowLayer class, you just need to darken each pixel on the screen if the ShadowMap has a shadow on the same pixel: 使用此ShadowLayer类,如果ShadowMap在同一像素上有阴影,则只需要使屏幕上的每个像素变暗:

public static Color ajustPixelForShadows(int x, int y, Color pixel) {
    return ShadowMap.array[x][y] ? pixel.darken() : pixel;
}

I admit I'm not familiar with Swing so I'm not sure it is possible with that specific interface but the below solution could be used in a variety of 2D graphics engines. 我承认我不熟悉Swing所以我不确定这个特定界面是否可行,但下面的解决方案可以用于各种2D图形引擎。

You'll need an off-screen "shadow layer" to draw to that matches the screen dimensions. 你需要一个离屏的“阴影层”来绘制,以匹配屏幕尺寸。 Initialize the shadow layer to being pure white. 将阴影层初始化为纯白色。

For each object you draw from back to front (y-sorting), do the following, in order, with the shadow layer: 对于从后向前绘制的每个对象(y排序),按顺序执行以下阴影图层:

  • Draw the object's shadow shape in a single solid dark grey color to the shadow layer 将对象的阴影形状以单一实心深灰色绘制到阴影层

  • Draw the object itself to the shadow layer as a pure white sprite (ie all non-transparent pixels in the object's bitmap are white) 将对象本身绘制为阴影图层作为纯白色精灵(即对象位图中的所有非透明像素均为白色)

Of course, also draw the object itself to the screen. 当然,也将对象本身绘制到屏幕上。

Then, once all objects have been drawn to both the screen and the shadow layer, draw the shadow layer to the screen using multiply blending. 然后,一旦所有对象都被绘制到屏幕和阴影层,使用多重混合将阴影层绘制到屏幕。 The multiply blend guarantees shadows will darken whatever they are drawn over (unlike alpha blend which, with very light shadows, could potentially actually lighten the colors they are drawn over). 多重混合可确保阴影会使绘制的任何内容变暗(与alpha混合不同,非常轻的阴影可能会实际上减轻它们被绘制的颜色)。 It will also make the pure white portions of the layer do nothing, which is what you want. 它还会使图层的纯白色部分无效,这就是你想要的。

The above steps mean that after each object draws a shadow, it erases any shadows that would be underneath it in the final scene when it draws itself in white to the shadow layer. 上述步骤意味着在每个对象绘制阴影之后,它会在最终场景中将其自身绘制为阴影层时擦除其下方的任何阴影。 Therefore it won't cast a shadow on itself, and objects won't cast shadows over other objects that are technically in front of them. 因此,它不会在自身上投射阴影,并且对象不会将阴影投射到技术上位于其前面的其他对象上。

Objects will still cast shadows onto other objects that are behind them as you wanted, since any parts of the shadow that haven't been erased by an overlapping object will still apply (or if they are erased, will be potentially re-drawn by a later object). 对象仍然可以根据需要将阴影投射到其后面的其他对象上,因为尚未被重叠对象擦除的阴影的任何部分仍将应用(或者如果它们被擦除,则可能会被重新绘制后来的对象)。 And, since you are drawing the shadows as a single non-translucent color to the shadow layer, multiple shadows overlapping won't affect each other either, which was of course the main point. 并且,由于您将阴影作为单个非半透明颜色绘制到阴影层,因此多个阴影重叠也不会相互影响,这当然是主要观点。

You could modify this technique depending on what you have available. 您可以根据可用的内容修改此技术。 For example, instead of white you could use a fully transparent shadow layer initially and an "erase" blend mode [(src * 0) + (dst * (1 - srcAlpha))] to draw the objects that erase shadows underneath them. 例如,最初可以使用完全透明的阴影图层而不是白色,并使用“擦除”混合模式[(src * 0)+(dst *(1 - srcAlpha))]来绘制擦除阴影下方的阴影的对象。 You could then use alpha instead of multiply blend if you prefer for drawing the shadow layer to the screen. 如果您希望将阴影图层绘制到屏幕,则可以使用alpha而不是乘法混合。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM