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.

Putting windows on viewports.

khmp

Sponsor

RGSS/XP:
Code:
#==============================================================================
# ** Window_Base
#------------------------------------------------------------------------------
#  This class is for all in-game windows.
#==============================================================================

class Window_Base < Window
  #--------------------------------------------------------------------------
  # * Object Initialization                                        !OVERRIDE!
  #     x        : window x-coordinate
  #     y        : window y-coordinate
  #     width    : window width
  #     height   : window height
  #     viewport : the viewport you want the window to be a part of.
  #--------------------------------------------------------------------------
  def initialize(x, y, width, height, viewport = nil)
    super(viewport)
    @windowskin_name = $game_system.windowskin_name
    self.windowskin = RPG::Cache.windowskin(@windowskin_name)
    self.x = x
    self.y = y
    self.width = width
    self.height = height
    self.z = 100
  end
end

RGSS2/VX:
Code:
#==============================================================================
# ** Window_Base
#------------------------------------------------------------------------------
#  This is a superclass of all windows in the game.
#==============================================================================

class Window_Base < Window
  #--------------------------------------------------------------------------
  # * Object Initialization                                        !OVERRIDE!
  #     x        : window x-coordinate
  #     y        : window y-coordinate
  #     width    : window width
  #     height   : window height
  #     viewport : the viewport that the window is contained within.
  #--------------------------------------------------------------------------
  def initialize(x, y, width, height, viewport = nil)
    super(viewport)
    self.windowskin = Cache.system("Window")
    self.x = x
    self.y = y
    self.y = y
    self.width = width
    self.height = height
    self.z = 100
    self.back_opacity = 200
    self.openness = 255
    create_contents
    @opening = false
    @closing = false
  end
end

Now any window that inherits from Window_Base has the ability to be assigned a viewport. For example:
Code:
class Window_HUD < Window_Base
  def initialize(viewport = nil)
    super(0, 0, 100, 100, viewport)
    # .. other code
  end
end

I wonder if the viewport can be assigned after creation though. Oh also be aware that disposal of a viewport disposes of the contents within it. So if the window is disposed before the viewport is disposed the viewport will throw an error about the window already being disposed and vice versa. So be aware of that fact. If this is RGSS2/VX than disposing of the viewport does not dispose of the assets within it. But you didn't make mention of what program you are using so I don't know if that applies to you. In any case the practice is the same with the both. The only thing that will change would be disposal of the window.

Good luck with it numbnuts! :thumb:
 
It doesn't seem to work. Am I using it right?

Code:
  def initialize(x, y, width, height, actor, viewport = nil)
    super(x, y, width, height, viewport)

Code:
      @skillviewport = Viewport.new(0, 33, 640, 416)
      @skillstatus_window = Window_SkillStatus.new(0, -106 - 33, 640, 64, @actor, @skillviewport)
      @skill_window = Window_Skill_Menu.new(0, 518 - 33, 640, 312, @actor, @skillviewport)
     
 

khmp

Sponsor

You are getting too many argument errors yes? I believe the problem lies in the constructor of those two windows. But you do have the general idea of how it works. You need to alter what their initialize methods' take in. For example Window_SkillStatus:

RGSS/XP:
Code:
#==============================================================================
# ** Window_SkillStatus
#------------------------------------------------------------------------------
#  This window displays the skill user's status on the skill screen.
#==============================================================================

class Window_SkillStatus < Window_Base
  #--------------------------------------------------------------------------
  # * Object Initialization                                        !OVERRIDE!
  #     actor : actor
  #--------------------------------------------------------------------------
  def initialize(actor, viewport = nil)
    super(0, 64, 640, 64, viewport)
    self.contents = Bitmap.new(width - 32, height - 32)
    @actor = actor
    refresh
  end
end

And then when you go to create the window.
Code:
@skillviewport = Viewport.new(0, 33, 640, 416)
@skillstatus_window = Window_SkillStatus.new($game_party.actors[xxx], @skillviewport) # Where xxx is the position of the party member.

You'll need to override the initialize of that Window_Skill_Menu class as well just like we have it above. Add an additional parameter called viewport into the list of "in" parameters. Changing:
Code:
  def initialize(x, y, width, height, actor)
to something like this:
Code:
  def initialize(x, y, width, height, actor, viewport = nil)
And then below that line you will see:
Code:
    super(x, y, width, height)
You need to make sure that viewport object is making it into that call to Window_Base's initialize. So add in the viewport object:
Code:
    super(x, y, width, height, viewport)

Lastly be careful with the arguments you are giving those initialize methods. Window_SkillStatus for example only takes two arguments. Actually before the code above it only took one argument and that was the actor. I only say this because I see you are throwing in the x, y, width and height into the window's creation. If you want the position or the size of the window to be different than the default then after you create the window you can have lines like this:
Code:
@skillstatus_window.y = -139
@skillstatus_window.width = 640
@skillstatus_window.height = 64

I hope that helps if you have any other questions let me know.

[edit]
Just found out if you're using VX you can alter the Viewport object after creation. Only in XP does it require it at initialize. So I guess there has to be a way to allow the same kind of thing with XP. I'll look into it, than all this work will be moot.  :lol:
 
@khmp: The only way I can think of re-assigning the object a viewport would be to dispose the object, and recreate it on the viewport. I am going to try something out, and actually make something that reads all child classes of Window_Base, auto-adds the viewport setting, and then figure this out. And the Plane, RPG::Weather, Tilemap, Sprite classes

Want a challenge? I want to see you do the same. lol. If you are up to it. ;)
 

khmp

Sponsor

SephirothSpawn":1b6y9voi said:
@khmp: The only way I can think of re-assigning the object a viewport would be to dispose the object, and recreate it on the viewport. I am going to try something out, and actually make something that reads all child classes of Window_Base, auto-adds the viewport setting, and then figure this out. And the Plane, RPG::Weather, Tilemap, Sprite classes

Want a challenge? I want to see you do the same. lol. If you are up to it. ;)

:staresblankly: ...I don't even understand what you said. Do you mean?
Code:
@test = Window_Base.new(0, 0, 32, 32, Viewport.new(0, 0, 32, 32))
And that line would be totally legal without overriding Window_Base's initialize? Yes then. Don't post a solution until I've thought about it some more please. Not smarts as u.
 
Come on, I am running on less than 2 hours of sleep and a hang over and I can follow... XD

As you said from what you posted, you have to make it so in every window class that inherits from Window_Base, you have to define the viewport. Why not make this automatic for all windows with a plug and play? By reading the child classes of Window_Base, aliasing each classes initialize method, adding a viewport argument, and sending it back to the original initialize method.

Now as for re-assigning the object's viewports, you just need to add a viewport method, dispose the object from the current viewport and recreate it in the new viewport. Honestly this part still seems to be a bit complicated and I am just throwing junk out there. I am not sure it's possible, but dammit, we can try!
 

khmp

Sponsor

SephirothSpawn":1j209587 said:
Come on, I am running on less than 2 hours of sleep and a hang over and I can follow... XD

As you said from what you posted, you have to make it so in every window class that inherits from Window_Base, you have to define the viewport. Why not make this automatic for all windows with a plug and play? By reading the child classes of Window_Base, aliasing each classes initialize method, adding a viewport argument, and sending it back to the original initialize method.

Now as for re-assigning the object's viewports, you just need to add a viewport method, dispose the object from the current viewport and recreate it in the new viewport. Honestly this part still seems to be a bit complicated and I am just throwing junk out there. I am not sure it's possible, but dammit, we can try!

Code:
# Still no success.

ObjectSpace.each_object(Class) do |c|
  if c.ancestors.include?(Window)
    c.class_eval do
      "alias orig_initialize initialize;" +
      "def initialize(*args, viewport = nil);" +
        "orig_initialize(*args, viewport);" +
      "end;"
    end
  end
end

Years of Ruby experience... Less than one year.
Getting chastised by SephirothSpawn... Priceless.

[edit]
numbnuts sorry for the thread hijacking. If you have any questions on what is posted above let me know. This thread is still geared towards finding a solution to your problem.
 
What you have works for finding sub-classes works, just has to look at A LOT of classes (especially if you have a lot of classes loaded).

I found one method that reads sub-classes when you define them, but you have to have it before the class is defined. So you have to insert the code below Window_Base.

Code:
class Window_Base
  def Window_Base.inherited(sub_class)
    # magic here
  end
end

Now whenever every class is created the inherited method is called and throws in the sub_class.

Now, I was basically planning on using a long block of string code and just using eval on each block with each class name sent.

For the sake of the time being, I would just put this below Window_Base.

Code:
class Window_Base
  @@sub_classes = []
  def Window_Base.inherited(sub_class)
    @@sub_classes << sub_class
  end
end

Now, below Scene_Debug (where else would you put something?), have something like this:
Code:
class Window_Base
  @@sub_classes.each do |sub|
    # more magic and sparkle
  end
end

Ok. I had the next part planned, but was distracted and now I can't even think of it. I have done this before on one of those damn scripts.... Hate it when that happens. I am going to bed. If you think of something, run with it! lol
 

EDIT: And the more I think about it, I am starting to feel like an idiot or something. I knew what I was talking about at first. I swear!

EDIT: I can't sleep! I am more frustrated than Ray Charles playing Marco Polo. I had the answer and then... I got hit with the short bus.
 
@khmp

As long as a solution to my problem is closer to being found then I don't mind lol I'm just beginning to get a little confused about what everyone's talking about lol

There is one thing I wondering about.

Code:
@skill_window.viewport

For some reason this is considered an undefined method even though it is listed in the RMXP help as valid method.
 

khmp

Sponsor

numbnuts":1olnlddz said:
@khmp

As long as a solution to my problem is closer to being found that I don't mind lol

There is one thing I wondering about.

Code:
@skill_window.viewport

For some reason this is considered an undefined method even though it is listed in the RMXP help as valid method.

Code:
viewport = @skill_window.viewport
Does exist.
Code:
@skill_window.viewport=
However does not exist. In RGSS2/VX you can assign a viewport at any time after it's initialization whether it already is in one or not.

SephirothSpawn":1olnlddz said:
EDIT: I can't sleep! I am more frustrated than Ray Charles playing Marco Polo. I had the answer and then... I got hit with the short bus.
Was that you that we just hit. You so slow.
 

khmp

Sponsor

It only gets the viewport if the window is assigned one already. If there isn't a viewport it will be nil I assume. In XP there is no method to assign a window to a viewport after initialize.
 

khmp

Sponsor

In all the times a Viewport object is used. The Viewport is assigned to the object, or perhaps I'm thinking about incorrectly, and not the other way around. So there must be a reason why it's:
Code:
sprite = Sprite.new(Viewport.new(0, 0, 640, 480))
And not:
Code:
viewport = Viewport.new(0, 0, 640, 480)
viewport.add(Sprite.new)

Honestly I've never thought about it. I think that question is best fielded by someone who knows this stuff more than me. Sorry that I can't really answer your question.
 

Zeriab

Sponsor

This looks so fun I just had to give it a go.
I found that a window did not have the viewport as an instance variable. My guess is that the .viewport method retrieves the viewport from the engine.
It is also likely that the engine requires the window to have the viewport on creation. There could easily be efficiency benifits with such an approach.
If I am correct about this then adding a .viewport= method to the Window class most bothersome if not impossible without altering the graphics engine itself.

I did found an apparently working solution which builds upon what Seph was messing around with
It is a two part code.
The first part must be placed as the very first thing.

Code:
class Window_Base < Window
  @@sub_classes = [Window_Base]
  def Window_Base.inherited(sub_class)
    @@sub_classes << sub_class
  end
end

Yeah, I know. Almost the same as Seph's. I just changed it so Window_Base also was in it. That's just because I am lazy :P
Here is the second part which must be placed just above main:

Code:
class Window
  alias orig_initialize initialize
  def initialize(*args)
    unless @initialized == true 
      @initialized = true
      orig_initialize(*args)
    end
  end
end

Window_Base.class_eval("@@sub_classes").each do |sub|
code =<<_END_
  class #{sub} < #{sub.superclass}
    alias orig_#{sub}_initialize initialize
    def initialize(*args)
      if args[-1].is_a?(Viewport)
        super(args[-1])
        if args.size > 1
          orig_#{sub}_initialize(*args[0, args.size - 1])
        end
      else
        orig_#{sub}_initialize(*args)
      end
    end
  end
_END_
  eval(code)
end
The modification to the Window class is to prevent the window from getting initialized more than once. It is basically to clean up the mess my generate code can give.
I don't really like this solution but it seems to work.

If you really want this in your game I suggest you alter the actual Window classes themselves though the given approach will automatically give the functionality to new window classes. (From custom scripts)

*hugs*
- Zeriab
 

khmp

Sponsor

Damnit. :lol: Excellent work Mr. Zeriab.

Quick question if you don't mind.
Code:
super(args[-1])

You call super passing in the Viewport object to the parent and that's it. For example if we are Window_Item we need to call super(x, y, width, height[, viewport]). So I'm a little confused on what is actually happening there. Am I missing something?
 
Excellent work Z. I finally figured that out when I got home and looks like you got yours posted first. I almost had something similar to that (the format was a little different, but the same code essentially).

khmp, look at this:
Code:
class Window
  alias orig_initialize initialize
  def initialize(*args)
    unless @initialized == true 
      @initialized = true
      orig_initialize(*args)
    end
  end
end

Why would he have something only pass the original initialize method the first time the super method is called to the window class, when super is suppose to be called only once?

Look here:
Code:
        super(args[-1])
        if args.size > 1
          orig_#{sub}_initialize(*args[0, args.size - 1])
        end

It calls the super method when a viewport is defined, then calls the original method that also calls the super method in the original method (but the second call yields to nothing because the original method in Window isn't called the second time). ;)



I am working on something now that uses this same .inherited method, to so you can read all sub classes to any class with a simple your_class.sub_classes. Why this isn't in the default Ruby library is beyond me.


Got it. With this inserted at the very top of your scripts:
Code:
class Object
  @@subclasses = {}
  def self.subclasses
    return @@subclasses.has_key?(self) ? @@subclasses[self] : []
  end
  def self.add_sub_class(superclass, subclass)
    @@subclasses[superclass] = []  unless @@subclasses.has_key?(superclass)
    @@subclasses[superclass] << subclass unless @@subclasses[self].include?(subclass)
  end
  def self.inherited(subclass)
    self.add_sub_class(subclass.superclass, subclass)
  end
end

You can check any classes subclasses with your_class.subclasses.
 

khmp

Sponsor

numbnuts":2ffbktiq said:
lol This all sounds very foreign to me lol So, did we find out how to assign windows to viewports yet or are you still trying to figure it out and if you did, how do I implement it?

Yeah Mr. Zeriab got it for you.

Insert an empty section right below "Window_Base" in the script listings and paste in it, the code below:
Code:
class Window_Base < Window
  @@sub_classes = [Window_Base]
  def Window_Base.inherited(sub_class)
    @@sub_classes << sub_class
  end
end

Then directly above "Main" create a section and paste in that section, the code below:
Code:
class Window
  alias orig_initialize initialize
  def initialize(*args)
    unless @initialized == true 
      @initialized = true
      orig_initialize(*args)
    end
  end
end

Window_Base.class_eval("@@sub_classes").each do |sub|
code =<<_END_
  class #{sub} < #{sub.superclass}
    alias orig_#{sub}_initialize initialize
    def initialize(*args)
      if args[-1].is_a?(Viewport)
        super(args[-1])
        if args.size > 1
          orig_#{sub}_initialize(*args[0, args.size - 1])
        end
      else
        orig_#{sub}_initialize(*args)
      end
    end
  end
_END_
  eval(code)
end

Now when you create a window. You can optionally include a Viewport object as the last argument in the initialize line. Just like the way I originally described above.

Code:
@test = Viewport.new(0, 0, 640, 240)
@window = Window_Skill.new($game_party.actors[0], @test)

Without having to go through all the classes and manually overriding the initialize of each window. Don't forget to credit Zeriab and SephirothSpawn for their work though.
 

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