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.

What are you working on? (Game Making Thread)

rey meustrus":15lm5pam said:
Well shoot, I can't remember exactly what I was responding to. It certainly wasn't the back-buffer thing; what I know about is the serialized data formats and how to reverse engineer the RGSS classes for OpenGL.
How do you reverse engineer the RGSS classes? I assumed it was all trial-and-error.

Knowing how to write my own tile rendering class would be nice (That is what Tile_Map does, right?), that way I could render the tiles myself in OpenGL and make a "mode-7" style demo.

Would be blazing fast if done in GL.
 
Working on graphics for Ghost Shards with the bossman~

screenshot-2014-12-17-20-54-17.png
 
I'm hacking away at the Tilemap class right now, seeing how it all works.

It is a lot less daunting than I first thought, just that most other Tilemap replacements have a lot of features/mature code so seem huge and complex, but really it's just a sprite that's handed tileset data and map data and is expected to blit it all.

It is a very good candidate for my OpenGL experiments, I can upload all the requires tilesets to the GPU and then assign a tile array that fills the screen and is offset by a single matrix, from there I can order the blitting by layer and texture (Reducing texture switches is more important than anything) and then the next layer can reuse the same tile meshes but with different texture switching.

Very cool.

Autotiles look annoying to implement.
 

Hybrida

☆ Biggest Ego ☆
Member

I'm currently working on water animations for my RPGMXP project (Omega Force H). Water animations are kinda difficult to construct. I'm using bubbles a lot. Thankfully I ripped a good waterfall animation located in RPGMVX. I've created about six water animations in the animator: The Hydro Rail, Hydro Pulse, Aqua Flood, Bubble Beam, Bubble Pillar, and The H20 Bomb (big ass bubble explodes).

Water animations I still need to complete: Water spout, Bubble Storm, Whirlpool, and the Super Hydro Blast Cannon.
I'm seriously trying to avoid bubbles. I'm glad I have something to accomplish finally. Water Skills for the win.
 
I have the same problem with Wind animations. Trying not to use green crescents for everything.
But to do effective vortexs, I had make a script that lets me put animation cells behind the battler. And also move the battler around the screen.

Which reminds me, It's just sitting in a numbered project folder somewhere, I needs to properly label and backup that up before I accidentally delete it.
 
@Xilef: Here's an implementation of the RMXP Tilemap class I wrote for a GOSU integration project I never finished. I can't tell you exactly how performant it is, or if the "flash data" part actually works right (nobody as far as I know actually used that part of the interface anyway). But it has full autotile and Z-ordering support.

[rgss]# RMXP Tilemap (gosu)
#
# Readable/Writable Attributes :
#  - viewport         : Viewport used for sprites
#  - map_data         : 3D Table of Tile ID Data
#  - flash_data       : 3D Table of Tile Color Data
#  - priorities       : 3D Table of Tile Priorities
#  - ox, oy           : Tilemap layer offsets
#  - zoom             : Zoom value as fraction
#  - tilesize         : Zoom value as size of individual tile (normal is 32.0)
#  - visible          : Tilemap Visible Flag
#  - is_a_plane       : Behaves like a Plane (loops around edges)
#  - tileset          : Name of Bitmap
#  - autotiles        : Array of Autotile Filenames
class Tilemap
  VP                = 2    # Number of tiles beyond viewport to refresh
  FA                = 64   # Alpha value of the color of flash data
  Autotile_Speed    = 20   # Number of frames between autotile animations
  Autotiles = [
    [416,432,512,528], [ 64,432,512,528], [416, 80,512,528], [ 64, 80,512,528],
    [416,432,512,176], [ 64,432,512,176], [416, 80,512,176], [ 64, 80,512,176],
    [416,432,160,528], [ 64,432,160,528], [416, 80,160,528], [ 64, 80,160,528],
    [416,432,160,176], [ 64,432,160,176], [416, 80,160,176], [ 64, 80,160,176],
    [384,400,480,496], [384, 80,480,496], [384,400,480,176], [384, 80,480,176],
    [224,240,320,336], [224,240,320,176], [224,240,160,336], [224,240,160,176],
    [448,464,544,560], [448,464,160,560], [ 64,464,544,560], [ 64,464,160,560],
    [608,624,704,720], [ 64,624,704,720], [608, 80,704,720], [ 64, 80,704,720],
    [384,464,480,560], [224,240,704,720], [192,208,288,304], [192,208,288,176],
    [256,272,352,368], [256,272,160,368], [640,656,736,752], [ 64,656,736,752],
    [576,592,672,688], [576, 80,672,688], [192,272,288,368], [192,208,672,688],
    [576,656,672,752], [256,272,736,752], [192,272,672,752], [  0, 16, 96,112]
  ]
  DEFAULTS = {:tileset => nil, :autotiles => [], :map_data => nil,
              :flash_data => nil, :priorities => nil, :visible => true,
              :eek:x => 0, :eek:y => 0, :zoom => 1.0, :is_a_plane => true,
              :manager => nil}
 
  # Public Instance Variables
  attr_reader   :gosuwindow     # Instance of Gosu::Window it's drawing to
  attr_reader   :tileset        # Tileset Image
  attr_reader   :autotiles      # Array of Autotile Images
  attr_accessor :map_data       # 3D Table of Tile Settings
  attr_reader   :flash_data     # 3D Table of Sprite Flash Colors
  attr_accessor :priorities     # 3D Table of Tileset Priorities
  attr_accessor :visible        # Tilemap Visibility
  attr_accessor :eek:x             # Bitmap Offsets
  attr_accessor :eek:y             # Bitmap Offsets
  attr_accessor :zoom           # Zoom Fraction
  attr_accessor :is_a_plane     # Loops around edges
 
  # Object Initialization
  def initialize(manager = nil, opts = {})
    @gosuwindow = $GameWindow
    DEFAULTS.merge!(opts).each do |sym, val|
      instance_variable_set("@#{sym}", val)
    end
    @manager           = manager unless manager.nil?
    @autotiles_cache   = @autotiles
    self.flash_data    = @flash_data
    @tiles             = []
    @frame             = 0
    (@manager.nil? ? @gosuwindow.draw_manager : @manager).manage(self)
  end
 
  def dispose
    @disposed = true
  end
 
  def disposed?
    @disposed == true
  end
 
  # Get "viewport"
  def viewport
    @manager
  end
 
  # Set "viewport"
  def viewport=(manager)
    (@manager.nil? ? @gosuwindow.draw_manager : @manager).unmanage(self)
    (manager.nil? ? @gosuwindow.draw_manager : manager).manage(self)
    @manager = manager
  end
 
  # Get Tile Size
  def tilesize
    (zoom * 32).to_i
  end
 
  # Set Tile Size
  def tilesize=(tilesize)
    self.zoom = tilesize / 32.0
  end
 
  # Set tileset graphic. Recaches the internal tileset.
  def tileset=(tileset)
    @tiles[384..-1] = Gosu::Image.load_tiles(@gosuwindow, tileset, 32, 32, true)
    @tileset = tileset
  end
 
  # Set list of autotiles. Recaches the internal tileset.
  def autotiles=(autotiles)
    autotiles.each_with_index do |autotile, i|
      Autotiles.each_with_index do |tiles, j|
        key = (i + 1) * 48 + j
        @tiles[key] = []
        # Draws Auto-Tile Rects
        for f in 0..(autotile.width / 96)
          bmp = Gosu::Image.new(@gosuwindow,
                                TexPlay::EmptyImageStub.new(32, 32), true)
          tiles.each_with_index do |pos, k|
            opts = {:crop => [x1 = pos%96+f*96, x2=pos / 96, x1 + 16, x2 + 16]}
            bmp.splice(k % 2 * 16, k / 2 * 16, autotile, opts)
          end
          @tiles[key][f] = bmp
        end
      end
    end
    @autotiles = @autotiles_cache = autotiles
  end
 
  # Set flash data. For speed we maintain a separate list of GOSU::Color
  # object for actual display.
  def flash_data=(flash_data)
    @flash_data = flash_data
    if flash_data.nil?
      @flash_data_colors = nil
    else
      @flash_data_colors = Hash.new
      for y in 0...flash_data.ysize
        for x in 0...flash_data.xsize
          hex = @flash_data[x, y]
          @flash_data_colors[[x, y]] =
            Gosu::Color.rgba((hex<<8)*16, ((hex<<4)%16)*16, (hex%16)*16, FA))
        end
      end
    end
  end
 
  # Dispose
  def dispose
    @visible = false
    super
  end
 
  # Update autotile animation
  def update
    @frame += 1
  end
 
  # Per-frame draw function
  def draw
    return if @map_data.nil? or @priorities.nil? or !@visible
   
    self.autotiles = @autotiles if @autotiles != @autotiles_cache
    ts = tilesize
    frame = @frame / Autotile_Speed
    xmax = (((@viewport.nil? ? @gosuwindow : @viewport.rect).width + @ox) / ts)
    ymax = (((@viewport.nil? ? @gosuwindow : @viewport.rect).height + @oy) / ts)
    ydts = @oy / ts
    yrange = ydts - VP...ymax + VP
    xrange = (@ox / ts) - VP...xmax + VP
   
    for y in yrange
      ytop = y - ydts
      if y < 0 or y >= @map_data.ysize
        next unless @is_a_plane
        y %= @map_data.ysize
        y += @map_data.ysize if y < 0
      end
      draw_y = y * ts - @oy
     
      for x in xrange
        if x < 0 or x >= @map_data.xsize
          next unless @is_a_plane
          x %= @map_data.xsize
          x += @map_data.xsize if x < 0
        end
        draw_x = x * ts - @ox
       
        for layer in 0...@map_data.zsize
          id = @map_data[x, y, layer]
          next if id == 0
          z = (ytop + z) * 32 + 64 unless (z = @priorities[id]) == 0
          tile = (tile.is_a?(Array) ? @tiles[id][frame % tile.size] : @tiles[id])
          tile.draw(draw_x, draw_y, z, @zoom, @zoom)
        end
        unless @flash_data.nil?
          c = @flash_data_colors[[x, y]]
          @gosuwindow.draw_quad(draw_x, draw_y,    c, draw_x+ts, draw_y,    c,
                                draw_x, draw_y+ts, c, draw_x+ts, draw_y+ts, c)
        end
      end # for x
    end # for y
  end
end
[/rgss]

The whole $GameWindow and @manager bit relates to the drawing hierarchy I set up; the manager object basically functions as a viewport. The code for the Manager class is below, but you probably will just take that part out:

[rgss]# Manager class
# Instances of this class keep track of a series of objects (which may
# optionally be sorted by block; see Enumerable#sort!) and may delegate certain
# actions to those objects.
class Manager
  def initialize(&sort)
    @managed = []
    @sort = sort
  end
  def manage(*objects)
    @managed |= objects
  end
  def unmanage(*objects)
    @managed.clear if objects.first == :all
    @managed -= objects
  end
  def delegate(manage_method = nil, &block)
    @managed.sort!(&@sort)
    if block_given?
      @managed.each(&block)
      return
    end
    return if manage_method.nil?
    @managed.each do |object|
      object.__send__(manage_method) if object.respond_to?(manage_method)
    end
  end
end
[/rgss]

This also does the rendering with the GOSU library instead of directly with OpenGL. `Gosu::Image.load_tiles(...)` slices the tileset into a number of different textures in the graphics card, one for each tile, which is probably...very wrong. The autotiles are the same way. The @tiles array should probably contain UV coordinates instead of pointers to individual textures; the autotiles are compiled from four 16x16 tiles instead of being 32x32 tiles themselves. Similarly, the flash data is drawn as a quad but should probably be colors applied to the vertices instead. Inside the draw method, you can probably guess what tile.draw does (tile being a Gosu::Image, which is a loaded texture).

In hindsight, this probably isn't all that helpful. Actually, everything you need to reverse engineer the Tilemap is in the RMXP help file! Well, almost everything. The autotiles are not properly documented, but at least my code will help you with that. I really don't think there's any better way to handle it than an int[48][4]; there are 48 possible configurations, each of which referencing 4 corners. I suppose an int[48][4][2] might alleviate the need to convert the integer into a coordinate pair. There's also the animation to worry about, which I have handled.

What was actually a lot more difficult to figure out was the way that RGSS serializes its internal classes. I've figured it out and written pure Ruby that will load and save to the same data formats:

[rgss]class Color < Gosu::Color
  def initialize(red, green, blue, alpha = 255)
    super(alpha.to_i, red.to_i, green.to_i, blue.to_i)
  end
  def set(red, green, blue, alpha = 255)
    self.red, self.green, self.blue, self.alpha = red, green, blue, alpha
  end
  def blend(color)
    self.clone.blend!(color)
  end
  def blend!(color)
    self.red  =(self.red  *(255-color.alpha)/255)+(color.red  *color.alpha/255)
    self.green=(self.green*(255-color.alpha)/255)+(color.green*color.alpha/255)
    self.blue =(self.blue *(255-color.alpha)/255)+(color.blue *color.alpha/255)
    self.alpha += (color.alpha * (255 - self.alpha) / 255)
    self
  end
  def to_a
    [red / 255.0, green / 255.0, blue / 255.0, alpha / 255.0]
  end
  def dup
    self.class.new(self.red, self.green, self.blue, self.alpha)
  end
  def clone
    self.class.new(self.red, self.green, self.blue, self.alpha)
  end
  # Dump to String (compatible with RGSS Color dump format)
  def _dump(marshal_depth = -1)
    [self.red, self.green, self.blue, self.alpha].pack('E4')
  end
  # Load from String (compatible with RGSS Color dump format)
  def self._load(data)
    new(*data.unpack('E4'))
  end
  White    = new(255, 255, 255)
  Shadow   = new(0, 0, 0, 160)
  Clear    = new(0, 0, 0, 0)
  Normal   = White
  Disabled = new(255, 255, 255, 128)
  System   = new(192, 224, 255)
  Crisis   = new(255, 255, 64)
  Knockout = new(255, 64, 0)
end
 
class Tone
  attr_reader :red, :green, :blue, :gray
  def initialize(red, green, blue, gray = 0)
    self.red, self.green, self.blue, self.gray = red, green, blue, gray
  end
  def red=(value)
    @red = [[-255, value].max, 255].min
  end
  def green=(value)
    @green = [[-255, value].max, 255].min
  end
  def blue=(value)
    @blue = [[-255, value].max, 255].min
  end
  def gray=(value)
    @gray = [[0, value].max, 255].min
  end
  def blend(tone)
    self.clone.blend!(tone)
  end
  def blend!(tone)
    self.red += tone.red
    self.green += tone.green
    self.blue += tone.blue
    self.gray += tone.gray
    self
  end
  # Dump to String (compatible with RGSS Tone dump format)
  def _dump(marshal_depth = -1)
    [@red, @green, @blue, @gray].pack('E4')
  end
  # Load from String (compatible with RGSS Tone dump format)
  def self._load(data)
    new(*data.unpack('E4'))
  end
end
 
# Rewritten the Table class. This is slower than RGSS but more portable.
 
class Table
  # Initialize the table with variable length arguments
  def initialize(xsize, ysize = 1, zsize = 1)
    resize(xsize, ysize, zsize)
  end
 
  # Set the size of (and number of) the dimensions; clears the table
  def resize(xsize, ysize = 1, zsize = 1)
    @data = Array.new(xsize * ysize * zsize, 0)
    @xsize, @ysize, @zsize = xsize, ysize, zsize
  end
 
  # Returns the size at a specific depth, or 1 if out of range
  def xsize ; @xsize ; end
  def ysize ; @ysize ; end
  def zsize ; @zsize ; end
 
  # Retrieve data
  def [](x, y = 0, z = 0)
    # Return nil if out of range
    return nil if (i = get_index(x, y, z)).nil?
    @data
  end
 
  # Set data
  def []=(*args)
    # Remove the last argument, which is what the data will be set to
    operand = args.pop.to_i
    # Return nil if out of range
    return nil if (i = get_index(*args).nil?)
    @data = operand
  end
 
  # Check arguments for size and within range
  def get_index(x, y, z)
    return nil if x < 0 or x >= xsize
    return nil if y < 0 or y >= ysize
    return nil if z < 0 or z >= zsize
    (z * @ysize * @xsize) + (y * @xsize) + x
  end
 
  # Dump to String (compatible with RGSS Table dump format for Integers)
  def _dump(marshal_depth = -1)
    # First, store information about the size
    size_info = [@sizes.length, *@sizes]
    size_info << 0x01 while size_info.length < 4
    output = size_info.pack('I*')
    # Then, store some empty data
    output += [0xD2].pack('I')
    # All values are integers (shorts), so store them as such
    output += @data.pack('s*')
    output
  end
 
  # Load from String (compatible with RGSS Table dump format for Integers)
  def self._load(data)
    # Get depth of the Table
    depth = data.slice!(0...4).unpack('I')[0]
    # Get size of each dimension
    sizes = data.slice!(0...[12, depth * 4].max).unpack('I*')[0...depth]
    output = Table.new(*sizes)
    data.slice!(0...4) # Skip this weird data
    # Integer format: load each piece of data in the correct order
    output.instance_variable_set:)@data, data.unpack('s*'))
    return output
  rescue RangeError
    # Catches failed attempts to slice existing data due to out of range
    raise RuntimeError, 'Load data for Table is not as long as specified. ' +
                        'The file may be corrupted.'
  end
 
  # Process through each element in order
  def each(&block)
    @data.each do |val|
      args = [val]
      args << i / (@ysize*@zsize) if block.arity > 1
      args << (i % @zsize) / @ysize if block.arity > 2
      args << i % (@ysize*@zsize) if block.arity > 3
      yield *args
    end
  end
end
[/rgss]

I haven't taken a look at these in a long time and I'm starting to be embarrassed by the possibility that they are wrong. But I hope this helps you at least.
 
I dreamt a game and now I kinda want to make it. It was a very small, tightly packed dungeon, but almost everything in it was interactive in a big way - push the right brick and the room transforms with fountains lifting from the floor etc. The objective of the game simply to clear the dungeon.
 

Jason

Awesome Bro

Princess Amy":pq5pn204 said:
I dreamt a game and now I kinda want to make it. It was a very small, tightly packed dungeon, but almost everything in it was interactive in a big way - push the right brick and the room transforms with fountains lifting from the floor etc. The objective of the game simply to clear the dungeon.

This actually sounds almost exactly like a game I was working on last year, it took place in one room, which was only small any way, using RPG Maker VX Ace it fit on one screen, think that's 17x13 tiles or something, whatever the default is... and your objective was to escape this room by interacting with everything in it and trying to solve a puzzle on getting the door open to leave... I actually got about halfway through it before I gave up on the idea since to be honest I didn't think anyone would be interested anyway...

I should really pick up some of my half finished games and see if I can get at the very least one of them finished this year... think that's gonna' be a goal for me.
 
Sounds great.

What stopped me was how difficult it is to animate anything in RMVX Ace. Thought of turning back to RMXP but that just feels like a step backwards again.

Thing is if your game's just one room you want that room to change aesthetically as you go, otherwise it's pretty boring (like the Crimson Room) - although people seem to love that game so nevermind that.
 
What a week...month? What was the last thing I was working on? When was the last thing I was working on?
Oh right, I started doing some requests at the start of the month. But then thumb injury, sickness, traveling; and now the month is over.

Gotta slap my face and git stuff dun. Just say no to distractions!
 
I'm working on this; gist.github.com/felixjones/pmx20.md

I want it to become the standard model format to be used at work, it comes with almost everything I could see us needing for future projects when it comes to entities. The only thing that is missing is blending for materials, but I'll figure something out there eventually.

It doesn't cover 3D scenes at all, but frankly there isn't a single format that is good for that right now. I'm still on the side of BSP/portal engines for scene rendering.

I'm also trying to abstract the Miku Miku Dance specifics of the format, so the Toon texture stuff will become Phong Map when I figure all that out. Sphere maps are now environment maps.

If anyone here has knowledge of the PMX format or Miku Miku Dance, please help me out with getting some of those notes written up.
 
Can't be worse than my stables. Which were just large doors for a recoloured Post Town. They weren't even stable doors; I didn't think it through much.
 
PYMtoNr.png

IGNORE THE SHADOWS AND THE SHOP-LIKE SIGN NOT CONNECTED TO THE BUILDING.

They are so shitty. I can't wait until Star fixes them up. At least I'm giving him start when it comes to doing these graphics, right? Right...? XD

I want to do the UI and practice my graphic design, but I can't yet. Or maybe I will, just because. o.o
 

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