How should I render a 2D flashlight effect?

后端 未结 2 1579
無奈伤痛
無奈伤痛 2021-02-11 01:03

I\'m trying to make a flashlight effect in my 2D game. My flashlight is represented as a line segment extending from the entity at a specific angle. The flashlight can point in

相关标签:
2条回答
  • 2021-02-11 01:49

    Number one is called stencil buffer. But you'll find it difficult to achieve soft effects. -- Easy.

    Number two: You just need a texture with the light. The completely black areas can be drawn repeating a black sprite around the light sprite (left, right, up, down). Or you can mix it with the stencil buffer. Or you can carefully calculate your texture coordinates and use GL.clamp_to_edge to propagate all the black pixels. Depending on how you render your scene, you can render your light first with alpha information and then blend the scene (darkening according to the dst_alpha). -- Not that hard to implement.

    Number three would be research on shaders (GLES 2.0). You can render a mesh to fill the hole screen and darken it with some shader calculations. -- This is the most flexible option, and the most difficult (far from rocket science anyway).

    Some options sound better than others, but providing the info you gave, there's nothing more I can tell you. You have a good start point from where to start to research on.

    **If you choose working with textures, consider to have a couple of different textures depending on the battery. You can scale them a bit or tint them, but having different textures is way more flexible.

    0 讨论(0)
  • 2021-02-11 02:01

    If the graphic element is a BufferedImage or Graphics2D instance, you might approach it like this.

    Flashlight using incandescent (yellow) beam

    Flashlight using halogen (blue) beam

    import java.awt.*;
    import java.awt.geom.*;
    import java.awt.image.BufferedImage;
    import javax.swing.*;
    
    public class FlashLight {
    
        public static void main(String[] args) throws Exception {
            Robot robot = new Robot();
            int w = 500, h = 200;
            Rectangle rect = new Rectangle(0, 0, w, h);
            final BufferedImage bi = robot.createScreenCapture(rect);
            final BufferedImage bi2 = FlashLight.draw(
                    bi, 10, 180, 420, 90, .3,
                    new Color(255, 255, 120, 15), new Color(0, 0, 0, 220));
            final BufferedImage bi3 = FlashLight.draw(
                    bi, 10, 180, 420, 90, .3,
                    new Color(180, 250, 255, 15), new Color(0, 0, 0, 220));
    
            Runnable r = new Runnable() {
    
                @Override
                public void run() {
                    JPanel gui = new JPanel(new GridLayout(3,0,2,2));
                    gui.add(new JLabel(new ImageIcon(bi2)));
                    gui.add(new JLabel(new ImageIcon(bi)));
                    gui.add(new JLabel(new ImageIcon(bi3)));
    
                    JOptionPane.showMessageDialog(null,gui);
                }
            };
            // Swing GUIs should be created and updated on the EDT
            // http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
            SwingUtilities.invokeLater(r);
        }
    
        public static BufferedImage draw(
                BufferedImage source,
                double x1, double y1, double x2, double y2,
                double beamWidth,
                Color beamColor, Color darknessColor) {
            RenderingHints hints = new RenderingHints(
                  RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);
    
            BufferedImage bi = new BufferedImage(
                    source.getWidth(), source.getHeight(),
                    BufferedImage.TYPE_INT_ARGB);
    
            Graphics2D g = bi.createGraphics();
            g.setRenderingHints(hints);
    
            g.drawImage(source, 0, 0, null);
    
            // Create a conical shape to constrain the beam
            double distance = Math.sqrt(Math.pow(x1 - x2, 2d) + Math.pow(y1 - y2, 2d));
            double tangent = (y2 - y1) / (x2 - x1);
            double theta = Math.atan(tangent);
            System.out.println(
                    "distance: " + distance
                    + "  tangent: " + tangent
                    + "  theta: " + theta);
            double minTheta = theta + beamWidth / 2;
            double maxTheta = theta - beamWidth / 2;
            double xMin = x1 + distance * Math.cos(minTheta);
            double yMin = y1 + distance * Math.sin(minTheta);
    
            double xMax = x1 + distance * Math.cos(maxTheta);
            double yMax = y1 + distance * Math.sin(maxTheta);
    
            Polygon beam = new Polygon();
            beam.addPoint((int) x1, (int) y1);
            beam.addPoint((int) xMax, (int) yMax);
            beam.addPoint((int) xMin, (int) yMin);
    
            g.setColor(beamColor);
            GradientPaint gp = new GradientPaint(
                    (int)x1,(int)y1, beamColor,
                    (int)x2,(int)y2, darknessColor);
            g.setClip(beam);
            g.setPaint(gp);
            g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
    
            // create an area the size of the image, but lacking the beam area
            Area darknessArea = new Area(new Rectangle(0, 0, bi.getWidth(), bi.getHeight()));
            darknessArea.subtract(new Area(beam));
            g.setColor(darknessColor);
            g.setClip(darknessArea);
            g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
    
            // fill in the beam edges with black (mostly to smooth lines)
            g.setClip(null);
            g.setColor(Color.BLACK);
            g.setStroke(new BasicStroke(2));
            g.draw(new Line2D.Double(x1,y1,xMin,yMin));
            g.draw(new Line2D.Double(x1,y1,xMax,yMax));
    
            g.dispose();
    
            return bi;
        }
    }
    
    0 讨论(0)
提交回复
热议问题