Envision, Create, Share

Welcome to HBGames, a leading amateur game development forum and Discord server. All are welcome, and amongst our ranks you will find experts in their field from all aspects of video game design and development.

viewport masking ? [advanced]

I'm trying to find a way to alpha-mask a viewport. I've learned to handle bitmaps and sprites, including planed sprites, but I can't seem to come up with a way to mask viewports. I have another script lined up that would be absolutely amazing if I could break the rectangular shape of a viewport. I don't need fading opacity, a hard cut would be good enough.

I'm not looking for a script, just a thought or idea on how this could be possible.

I've thought about trying to do a pixel-by-pixel z mask... but that's even less probable than a straightforward mask. The only other thought suggested was rewriting the rect() defintion. As ugly as that sounds, I'd be willing to try it. Does anyone know how to rewrite the rect definition in Ruby?
 
Well, first of all the Rect object is an RGSS specific object, from my understanding the ONLY thing rect does is store x, y, width and height variables in one object. Alot of RGSS methods accept individual x, y, w, h variables and/or a Rect object...

bitmap.fill_rect(x, y, width, height, color)

...or...

rect = Rect.new(x, y, width, height)
bitmap.fill_rect(rect, color)

So I don't really think anything else needs to be done to Rect, however you could probably figure a way to create like a Circ (circle) object or something...

Okay, onto bussiness... can you explain what you mean by "alpha masking" a little more? Like, do you want all sprites within a certain viewport/spriteset to be the same opacity, or even the same tone? That shouldn't be too hard, I think the easiest way to do it would be to create methods for the Viewport class that does this for you...

Code:
class Viewport

  def opacity_all=(n)

    self.instance_variables.each do |object_name|

      object = eval object_name

      if object.respond_to?("opacity=")

        object.opacity = n

      end

    end

  end

end


Keep in mind, I didn't test it I just barely wrote that as an example, you can see if it works. I'm a little rusty because I haven't had time to script in weeks, but I think the same thing can be applied with .tone and other properties though.
 
Looking a little deeper than that. Say I have a bitmap object on a Plane object, scrolling every frame. What I'm trying to do is essentially cut a whole in it, or cut the borders into a unique shape. I'd love to find a way to do it with a graphic.

Right now, with planes, we are limited to square shapes. I need to find a way to use other shapes, preferably by reading the data from a hidden bitmap overlay and removing the visibility of the black pixels from the viewport. A la "alpha mask".

To give a simple example: The ability to black out a (non-square) screen area from the fog effect.
 
I just wrote something up for you:

[rgss] 
class Viewport
  def set_alpha_mask(matrix)
    @mask = Sprite.new(self)
    @mask.bitmap = Bitmap.new(self.rect.width,self.rect.height)
    @mask.opacity = 160
    @mask.z = 999
    for i in 0..matrix.size
      if !matrix.nil?
        com = matrix[0]
        case com.downcase
        when 'line'
          x1 = matrix[1][0]
          y1 = matrix[1][1]
          x2 = matrix[1][2]
          y2 = matrix[1][3]
          cl = matrix[1][4]
          wd = matrix[1][5]
          @mask.bitmap.draw_line(x1, y1, x2, y2, cl, wd)
        when 'rect_filled'
          x = matrix[1][0]
          y = matrix[1][1]
          w = matrix[1][2]
          h = matrix[1][3]
          c = matrix[1][4]
          @mask.bitmap.fill_rect(x, y, w, h, c)
        when 'ellipse_unfilled'
          ox = matrix[1][0]
          oy = matrix[1][1]
          vr = matrix[1][2]
          rt = 1.0
          cl = matrix[1][3]
          lw = matrix[1][4]
          @mask.bitmap.draw_ellipse(ox, oy, vr, rt, cl, lw)
        end
      end
    end
  end
end
 
class Bitmap
  #----------------------------------------------------------------------------
  # draw_line: Draw a line between points (x1;y1) and (x2;y2) on the bitmap.
  #   x1: X-coordinate of an extremity of the line.
  #   y1: Y-coordinate of an extremity of the line.
  #   x2: X-coordinate of another extremity of the line.
  #   y2: Y-coordinate of another extremity of the line.
  #   color: The color of the line. It can support transparency.
  #   line_width: The width of the line in pixels. Set to 1 by default.
  #----------------------------------------------------------------------------
  def draw_line(x1, y1, x2, y2, color, line_width = 1)
    # calculate the dimensions vertically and horizontally
    cx = x2 - x1
    cy = y2 - y1
   
    # if the line is larger horizontally, draw it on an horizontal basis
    if cx.abs > cy.abs
      # if cx is 0, don't draw
      return if cx == 0
     
      # reorder the coordinates so that x1 is left of x2
      if x2 < x1
        temp = x2
        x2 = x1
        x1 = temp
        temp = y2
        y2 = y1
        y1 = temp
      end
     
      # draw the line
      for i in 0..cx.abs
        # calculate the position vertically
        y = y1 + cy * i / cx
       
        # draw the pixel
        set_pixel(x1 + i, y, color)
       
        # enlarge the line if the width is not 1
        if line_width != 1
          for j in 2..line_width
            # on even counter, draw higher; on odd draw below
            if (j & 1) == 0
              set_pixel(x1 + i, y - j / 2, color)
            else
              set_pixel(x1 + i, y + j / 2, color)
            end
          end
        end
      end
    else
      # if cy is 0, don't draw
      return if cy == 0
     
      # reorder the coordinates so that y1 is above y2
      if y2 < y1
        temp = x2
        x2 = x1
        x1 = temp
        temp = y2
        y2 = y1
        y1 = temp
      end
     
      # draw the line
      for i in 0..cy.abs
        # calculate the position horizontally
        x = x1 + cx * i / cy
       
        # draw the pixel
        set_pixel(x, y1 + i, color)
       
        # enlarge the line if the width is not 1
        if line_width != 1
          for j in 2..line_width
            # on even counter, draw left; on odd draw right
            if j & 1
              set_pixel(x - j / 2, y1 + i, color)
            else
              set_pixel(x + j / 2, y1 + i, color)
            end
          end
        end
      end
    end
  end
 
  #----------------------------------------------------------------------------
  # draw_square: Draw an unfilled square.
  #   rect: Rectangle corresponding to the square to draw.
  #   color: Color of the border.
  #   line_width: Width of the border. Set to 1 by default.
  #----------------------------------------------------------------------------
  def draw_square(rect, color, line_width = 1)
    draw_square(rect.x, rect.y, rect.cx, rect.cy, color, line_width)
  end
 
  #----------------------------------------------------------------------------
  # draw_square: Draw an unfilled square.
  #   x: X-coordinate of the top-left corner of the square.
  #   y: Y-coordinate of the top-left corner of the square.
  #   width: Width of the square.
  #   height: Height of the square.
  #   color: Color of the border.
  #   line_width: Width of the border. Set to 1 by default.
  #----------------------------------------------------------------------------
  def draw_square(x, y, width, height, color, line_width = 1)
    # draw the left side
    fill_rect(x, y, line_width, height, color)
   
    # draw the top side
    fill_rect(x, y, width, line_width, color)
   
    # draw the right side
    fill_rect(x + width - line_width + 1, y, line_width, height, color)
   
    # draw the bottom side
    fill_rect(x, y + height - line_width + 1, width, line_width, color)
  end
 
  #----------------------------------------------------------------------------
  # draw_ellipse: Draw an unfilled ellipse. (oval)
  #   ox: X-coordinate of the center of the ellipse.
  #   oy: Y-coordinate of the center of the ellipse.
  #   vradius: The vertical radius.
  #   ratio: Ratio between the vertical radius and the horizontal radius.
  #            (horizontal / vertical) Set it to 1.0 to draw a circle.
  #   color: Color of the border.
  #   line_width: Width of the border. Set to 1 by default.
  #   sides: The ellipse is in fact an elliptic polygon with a given amount of
  #            faces. For example, set it to 6 to have an hexagon. This is set
  #            to 32 by default, which is plenty to make it look like a curved
  #            form.
  #----------------------------------------------------------------------------
  def draw_ellipse(ox, oy, vradius, ratio, color, line_width = 1, sides = 32)
    # calculate the horizontal radius
    hradius = (vradius * ratio).to_i
   
    # set the initial coordinate
    x1 = ox
    y1 = oy - vradius
   
    # draw each side of the polygon except the last
    for i in 1...sides
      # calculate the position of the new point
      x2 = ox - (hradius * Math.sin(i * Math::PI * 2 / sides)).round
      y2 = oy - (vradius * Math.cos(i * Math::PI * 2 / sides)).round
     
      # draw a the line
      draw_line(x1, y1, x2, y2, color, line_width)
     
      # store the old position
      x1 = x2
      y1 = y2
    end
   
    # draw the last side
    draw_line(x1, y1, ox, oy - vradius, color, line_width)
  end
end
 
 
[/rgss]

It takes a 3 dimensional array.

Usage:

[rgss] 
    black = Color.new(0,0,0,255)
    matrix = []
    matrix[0] = ['line']
    matrix[0][1] = [0,0,320,240,black,5]
    matrix[1] = ['rect_filled']
    matrix[1][1] = [320, 240, 200, 200, black]
    matrix[2] = ['ellipse_unfilled']
    matrix[2][1] = [128, 128, 64, black, 5]
    @viewport1 = Viewport.new(0, 0, 640, 480)
    @viewport1.set_alpha_mask(matrix)
 
[/rgss]

You can add more shapes(right now there's only line, filled rect, and unfilled circle)by adding more drawing functions to the Bitmap class.
 
Near, I was looking through that script, and I had a question. Does that script simply place an image over everything else, or does it actually make the masked portions of the viewport transparent? Looking through it, it appears to only put an image on top, instead of making the masked portion transparent. I may be wrong though, which is why I asked.
 
Near":1f70qj6s said:
It just draws a bitmap over the other sprites.
How would I go about make portions transparent?

I have absolutely no idea, which is why theory asked here. I mean, it might be possible if you literally edit the cached bitmaps and remove pixels based on their location relative to the viewport, but that would be a huge pain to code and would probably end up with a very laggy result. (And, unlike just modifying bitmaps, it would be much more difficult to encapsulate in a .dll file. Although it could theoretically feed the .dll file all the bitmpas, their coordinates, and the size of the viewport, so the .dll can do the work, but I have no idea how to make a recursively infinite number of possible variables to feed the .dll file, unless it actually goes sprite by sprite.)
 
I don't really see true alpha masking in the near future.

What I do see as more likely, is defining a custom-shape viewport. If we can draw a bitmap in different shapes... why not just "draw" the viewport in a custom shape? True, not as powerful as true alpha masking... But a step in advancement, regardless.

Or, I could be wrong. Found an article that might help spark this discussion along. Still looking for information on the RGSS viewport class.

http://www.tech-archive.net/Archive/Dev ... 00007.html

I'm thinking that we have two options. Rewrite the viewport, with some kind of alpha masking capability, or rewrite .rect to somehow let the viewport be drawn in non-squares. I'd even settle for a diamond shape, as that would be a strong step.

Can we rotate or otherwise manipulate a viewport, to anyone's knowledge?
 
theory":3kp42v7u said:
I don't really see true alpha masking in the near future.

What I do see as more likely, is defining a custom-shape viewport. If we can draw a bitmap in different shapes... why not just "draw" the viewport in a custom shape? True, not as powerful as true alpha masking... But a step in advancement, regardless.

Or, I could be wrong. Found an article that might help spark this discussion along. Still looking for information on the RGSS viewport class.

http://www.tech-archive.net/Archive/Dev ... 00007.html

I'm thinking that we have two options. Rewrite the viewport, with some kind of alpha masking capability, or rewrite .rect to somehow let the viewport be drawn in non-squares. I'd even settle for a diamond shape, as that would be a strong step.

Can we rotate or otherwise manipulate a viewport, to anyone's knowledge?

Actually, bitmaps are rectangles too. It's just that they have transparent pixels around the sprite, and appear to be shaped like the sprite.
 
:p hum hum ...

There is something called "blend_type" in rgss :p for sprites... ( and planes )

the mask will be a sprite in substractive blend mode, filled with a white bitmap :)
[rgss] 
a = Sprite.new
a.bitmap = Bitmap.new(64,64)
a.bitmap.fill_rect(a.src_rect,Color.new(255,0,0))
 
b = Sprite.new
b.bitmap = Bitmap.new(32,32)
b.bitmap.fill_rect(b.src_rect,Color.new(255,255,255))
b.blend_type = 2
 
[/rgss]
 

Thank you for viewing

HBGames is a leading amateur video game development forum and Discord server open to all ability levels. Feel free to have a nosey around!

Discord

Join our growing and active Discord server to discuss all aspects of game making in a relaxed environment. Join Us

Content

  • Our Games
  • Games in Development
  • Emoji by Twemoji.
    Top