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.

Automatic sizing of text boxes

I have a script in which I need to automatically size a text box. Here's a heavily simplified version of it, to indicate what I'm getting at:

class Window_Displayamessage < Window_Base
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    super(0, 0, @messagewidth [[[+ a number]]], @messageheight [[[+ a number]]])
    self.contents = Bitmap.new(width - 32, height - 32)
    self.opacity = 255
    self.back_opacity = 160       
    refresh
  end
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    self.contents.clear
    self.contents.font.color = normal_color
    self.contents.draw_text(4, 0, @messagewidth, @messageheight, $message)
  end
end

I want to know how to be able to figure out @messagewidth and @messageheight.

In my game, for a typical event-called message, text boxes are resized automatically as I'm using the following script:

#==============================================================================
# ** Letter by Letter Message Window
#------------------------------------------------------------------------------
# Slipknot (dubealex.com/asylum)
# Version 1.11
# September 8, 2006
#------------------------------------------------------------------------------
# Thanks to:
#  - Dubealex, for some of the features.
#  - RPG Advocate, for the hexadecimal color.
#==============================================================================

# Loads the maps' names
$data_map_infos = load_data('Data/MapInfos.rxdata')

#==============================================================================
# ** Game_Message
#------------------------------------------------------------------------------
#  This class handles the message data
#==============================================================================

class Game_Message
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :letter_by_letter, :speed, :can_skip, :height, :font,
:sound_enable, :sound, :path, :face_rect, :fit, :skin, :nb_skin,
:nbyo, :eek:pacity, :shadow, :eek:utline, :pause, :autoclose_frames
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
# Letter by letter mode
  @letter_by_letter = true
# Letter by letter mode's speed
@speed = 0
# If this option is false, the player can't skip the message
@can_skip = true
# Height of each line, used within the fit or above event options
@height = 32
# Always resize the message
@fit = true
# Font for the mesage text
@font = Font.default_name
# Folder for the message pictures
@path = 'Graphics/Pictures/'
# Face rect (only the last two numbers are used)
@face_rect = Rect.new(0, 0, 96, 96)
# Skin for the message window, nil = default
@skin = nil
# Skin for the name box, nil = default
@nb_skin = nil
# Name box y offset
@nbyo = 32
# Message window's opacity
@opacity = 160
# Outline text
@outline = false
# Shadow text
@shadow = false
# Show or not the pause graphic
@pause = true
# Frames before the message autoclose
@autoclose_frames = 8
  end
end

#==============================================================================
# ** Game_System
#------------------------------------------------------------------------------
#  Adds Game Message
#==============================================================================

class Game_System
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_reader :message
  #--------------------------------------------------------------------------
  # * Alias Listing
  #--------------------------------------------------------------------------
  alias slipknot_lblms_initialize initialize
  #--------------------------------------------------------------------------
  # * Load Database
  #--------------------------------------------------------------------------
  def initialize
slipknot_lblms_initialize
@message = Game_Message.new
  end
end

#==============================================================================
# ** Game_Event
#------------------------------------------------------------------------------
#  Adds a reader to the Event Name
#==============================================================================

class Game_Event < Game_Character
  #--------------------------------------------------------------------------
  # * Name
  #--------------------------------------------------------------------------
  def name
@event.name
  end
end

#==============================================================================
# ** Spriteset_Map
#------------------------------------------------------------------------------
#  Adds a reader to the Character Sprites
#==============================================================================

class Spriteset_Map
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_reader :character_sprites
end

#==============================================================================
# ** Window_Message
#------------------------------------------------------------------------------
#  Rewrites Window_Message
#==============================================================================

class Window_Message < Window_Selectable
  #--------------------------------------------------------------------------
  # * Alias Listing
  #--------------------------------------------------------------------------
  alias slipknot_lblms_initialize initialize
  alias slipknot_lblms_terminatemessage terminate_message
  #--------------------------------------------------------------------------
  # * Initialize
  #--------------------------------------------------------------------------
  def initialize
slipknot_lblms_initialize
@autoclose = -1
  end
  #--------------------------------------------------------------------------
  # * Terminate Message
  #--------------------------------------------------------------------------
  def terminate_message
slipknot_lblms_terminatemessage
[@name_box, @picture].each do |x|
  x.dispose if x && ! x.disposed?
end
  end
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
  self.opacity = $game_system.message_frame == 0 ? 255 : 0
self.back_opacity = system.opacity
unless system.fit
  self.width, self.height = 480, 160
  skin = system.skin ? system.skin : $game_system.windowskin_name
  self.windowskin = RPG::Cache.windowskin(skin)
  self.contents = Bitmap.new(448, 128)
else
  contents.clear
  contents.font.color = normal_color
  contents.font.size = Font.default_size
end
contents.font.name = system.font
@x = @y = @wait_count = indent = 0
@fit_size
@start_x = 4
@cursor_width = [0, 0, 0, 0]
@x = 8 if $game_temp.choice_start == 0
return if ! (@text = $game_temp.message_text)
@text.gsub!(/\\\\/) { "\000" }
@text.gsub!(/\\[Vv]\[([0-9]+)\]/) { $game_variables[$1.to_i] }
@text.gsub!('\$') { $game_party.gold.to_s }
@text.gsub!(/\\[Nn]\[([0-9]+)\]/) do
    $game_actors[$1.to_i] ? $game_actors[$1.to_i].name : ''
end
@text.gsub!(/\\[Nn][Pp]\[([\d+])\]/) do
    $game_party.actors[$1.to_i] ? $game_party.actors[$1.to_i].name : ''
end
@text.gsub!(/\\[Cc]lass\[(\d+)\]/) do
  $game_actors[$1.to_i] ? $game_actors[$1.to_i].class_name : ''
end
@text.gsub!(/\\[Mm]ap/) { $data_map_infos[$game_map.map_id].name }
gold_set = @text.gsub!(/\\[Gg]/, '')
if @text[/\\[Nn]ame/]
    if @text.sub!(/\\[Nn]ame\[(.*?)\]/, '')
name_text = $1
  elsif @text.sub!(/\\[Nn]ame/, '')
name_text = $game_map.events[$game_system.map_interpreter.event_id].name
  end
end
if @text[/\\[Ff]ace/]
  # Left
  if @text.sub!(/\\[Ff]ace{(.+?)}/, '')
face, face_name = 1, $1
  # Right
  elsif @text.sub!(/\\[Ff]ace\[(.+?)\]/, '')
face, face_name = 2, $1
  end
end
picture = $1 if @text.sub!(/\\[Pp]ic\[(.+?)\]/, '')
if @text[/\\[Pp]/]
  if @text.sub!(/\\[Pp]\[([-1,0-9]+)\]/, '')
event = $1.to_i
  elsif @text.gsub!(/\\[Pp]/, '')
event = $game_system.map_interpreter.event_id
  end
end
@text.gsub!('\$') { $game_party.gold.to_s }
@text.gsub!(/\\[Cc]\[([0-9A-Fa-f #]+?)\]/) { "\001[#$1]" }
@text.gsub!(/\\[Cc]/) { "\001[0]" }
@text.gsub!(/\\[Ii]con{([IiWwAaSs])}\[(\d+)\]/) { change_icon($1, $2.to_i) }
@text.gsub!(/\\[Ii]con\[(.*?)\]/) { "\002[#$1]" }
@text.gsub!('\!') { "\003" }
@text.gsub!('\.') { "\004" }
@text.gsub!(/\\[Ss]\[([Xx\d]+)\]/) { "\005[#$1]" }
@text.gsub!(/\\[Bb]/) { "\006" }
@text.gsub!(/\\[Ii]/) { "\007" }
@text.gsub!(/\\[Ff]\[(.*?)\]/) { "\010[#$1]" }
@text.gsub!(/\\\%\[(\d+)\]/) { "\011[#$1]" }
@text.gsub!('\%') { "\011" }
if @fit_size = (event || system.fit)
  lines_size = [0, 0, 0, 0]
  save, lines = @text.clone, 0
  while (c = @text.slice!(/./m))
if c == "\n"
  lines += 1
  break if lines == 4
  if lines >= $game_temp.choice_start
lines_size[lines] += 16
  end
  next
end
lines_size[lines] += eval_text(c, true)
  end
end
if face
  if @fit_size
mh = system.height
fh = system.face_rect.height
lines = (fh.to_f / mh.to_f).ceil if (lines * mh) < fh
f_x = face == 2 ? 0 : lines_size.max + 16
f_y = (lines * mh) <= fh ? 0 : (lines * mh - fh) / 2
@start_x += system.face_rect.width + 4 if face == 2
indent += system.face_rect.width + 8
  else
f_x, f_y = face == 2 ? 16 : 336, 16
@start_x += system.face_rect.width + 36 if face == 2
  end
  f_bitmap = RPG::Cache.load_bitmap(system.path, face_name)
end
if @fit_size
  @text = save
  self.height = lines * system.height + 32
  self.height += 32 if $game_temp.num_input_variable_id > 0
  self.width = lines_size.max + indent + 40
  windowskin = system.skin ? system.skin : $game_system.windowskin_name
  self.windowskin = RPG::Cache.windowskin(windowskin)
  self.contents = Bitmap.new(self.width - 32, self.height - 32)
  contents.font.name = system.font
end
contents.blt(f_x, f_y, f_bitmap, system.face_rect) if face
if ! event
  h2 = self.height / 2
  self.y = $game_temp.in_battle ? 96 - h2 + system.nbyo :
case $game_system.message_position
when 0 then 96 - h2 + system.nbyo
when 1 then 240 - h2
when 2 then 384 - h2
end
  self.x = 320 - self.width / 2
else
  c = event > 0 ? $game_map.events[event] : $game_player
  mx, my = 636 - self.width, 476 - self.height
  fx = [[c.screen_x - self.width / 2, 4].max, mx].min
  sy = name_text ? system.nbyo + 4 : 4
  ch = [$scene.spriteset.character_sprites[event - 1].bitmap.height /
4 + 4, 48].max
  fy = [[c.screen_y - (ch + self.height), sy].max, my].min
  self.x, self.y = fx, fy
end
if name_text
    @name_box = Window_NameBox.new(x, y - system.nbyo, name_text)
  @name_box.back.opacity = 0 if $game_system.message_frame == 1
end
if picture
  @picture = Sprite.new
  @picture.bitmap = RPG::Cache.load_bitmap(system.path, picture)
  @picture.x = self.x + self.width - @picture.bitmap.width
  @picture.y = self.y - @picture.bitmap.height
end
if gold_set
  @gold_window = Window_Gold.new
  @gold_window.x = 560 - @gold_window.width
  if $game_temp.in_battle
@gold_window.y = 192
  else
@gold_window.y = self.y >= 128 ? 32 : 384
  end
  @gold_window.opacity = self.opacity
  @gold_window.back_opacity = self.back_opacity
end
  end
  #--------------------------------------------------------------------------
  # * Evaluate Text
  #--------------------------------------------------------------------------
  def eval_text(c, read = false)
case c
when "\000"
  c = '\\'
when "\001"
  @text.sub!(/\[(.*?)\]/, '')
  return 0 if read
  h, c = $1, $1.to_i
  contents.font.color = h.slice!(/./) == '#' ? hex_color(h) : text_color(c)
  return
when "\002"
  @text.sub!(/\[(.*?)\]/, '')
  return 24 if read
  y = @fit_size ? system.height * @y + (system.height - 24) / 2 : 32 * @y + 4
  contents.blt(@x + @start_x, y, RPG::Cache.icon($1.to_s), Rect.new(0, 0, 24, 24))
  @x += 24
  return unless @y >= $game_temp.choice_start
  @cursor_width[@y] += 24
  return
when "\003"
  return 0 if read
  @stop = true
  return
when "\004"
  return 0 if read
  @wait_count += 10
  return
when "\005"
  @text.sub!(/\[([x\d]+)\]/, '')
  if $1.downcase == 'x'
contents.font.size = Font.default_size
  else
contents.font.size = [[$1.to_i, 6].max, system.height].min
  end
  return 0
when "\006"
  contents.font.bold = (! contents.font.bold)
  return 0
when "\007"
  contents.font.italic = (! contents.font.italic)
  return 0
when "\010"
  @text.sub!(/\[(.*?)\]/, '')
  if $1.downcase == 'x'
contents.font.name = system.font
  else
contents.font.name = [$1.to_s, system.font]
  end
  return 0
when "\011"
  @text.sub!(/\[(\d+)\]/, '')
  return 0 if read
  @autoclose = $1 ? $1.to_i : system.autoclose_frames
  return
when "\n"
  @y += 1
  @x = 0
  @x = 8 if @y >= $game_temp.choice_start
  return
end
w = contents.text_size(c).width
return w if read
y = @fit_size ? system.height * @y : 32 * @y
if system.outline
  color = contents.font.color.dup
  contents.font.color.set(0, 0, 0, 255)
  contents.draw_text(@x + @start_x + 1, y, w * 2, system.height, c)
  contents.draw_text(@x + @start_x, y + 1, w * 2, system.height, c)
  contents.draw_text(@x + @start_x - 1, y, w * 2, system.height, c)
  contents.draw_text(@x + @start_x, y - 1, w * 2, system.height, c)
  contents.font.color = color
  contents.draw_text(@x + @start_x, y, w * 2, system.height, c)
elsif system.shadow
  color = contents.font.color.dup
  contents.font.color.set(0, 0, 0, 192)
  contents.draw_text(@x + @start_x + 2, y + 2, w * 2, system.height, c)
  contents.font.color = color
  contents.draw_text(@x + @start_x, y, w * 2, system.height, c)
else
  contents.draw_text(@x + @start_x, y, w * 2, system.height, c)
end
@x += w
return if @y < $game_temp.choice_start || @y > 3
@cursor_width[@y] += w
  end
  #--------------------------------------------------------------------------
  # * Finish
  #--------------------------------------------------------------------------
  def finish
if temp.choice_max > 0
  @item_max, self.active, self.index = temp.choice_max, true, 0
end
if temp.num_input_variable_id > 0
  digits_max = temp.num_input_digits_max
  number = $game_variables[temp.num_input_variable_id]
  @input_number_window = Window_InputNumber.new(digits_max)
  input_number.number = number
  input_number.x = x + 8
  input_number.y = y + temp.num_input_start * (@fit_size ? system.height : 32)
end
  end
  #--------------------------------------------------------------------------
  # * Database Icon
  #--------------------------------------------------------------------------
  def change_icon(option, index)
s = case option.downcase
  when 'i'  then $data_items[index]
  when 'w' then $data_weapons[index]
  when 'a'  then $data_armors[index]
  when 's'  then $data_skills[index]
  end
return sprintf("\002[%s]%s", s.icon_name, s.name) if s.name
  end
  #--------------------------------------------------------------------------
  # * Hexadecimal Color
  #--------------------------------------------------------------------------
  def hex_color(string)
return normal_color if string.size != 6
r = g = b = 0
5.times do |i|
  s = string.slice!(/./m)
  v = hex_convert(s.downcase)
  case i
  when 0 then r += v * 16
  when 1 then r += v
  when 2 then g += v * 16
  when 3 then g += v
  when 4 then b += v * 16
  when 5 then b += v
  end
end
return Color.new(r, g, b)
  end
  #--------------------------------------------------------------------------
  def hex_convert(c)
return c.to_i if c[/[0-9]/]
case c
when 'a' then 10
when 'b' then 11
when 'c' then 12
when 'd' then 13
when 'e' then 14
when 'f' then 15
end
  end
  #--------------------------------------------------------------------------
  # * Game Message
  #--------------------------------------------------------------------------
  def system() $game_system.message end
  #--------------------------------------------------------------------------
  # * Game Temp
  #--------------------------------------------------------------------------
  def temp() $game_temp end
  #--------------------------------------------------------------------------
  # * Input Number Window
  #--------------------------------------------------------------------------
  def input_number() @input_number_window end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
if @contents_showing
  super
  if @fade_in
self.contents_opacity += 24
if input_number
  input_number.contents_opacity += 24
end
@fade_in = contents_opacity != 255
return
  end
  if @text
if Input.trigger?(13)
  if @stop
self.pause = @stop = false
return
  end
  @skip = system.can_skip
end
return if @stop
if @wait_count > 0 && ! @skip
  @wait_count -= 1
  return
end
loop do
  if (c = @text.slice!(/./m))
eval_text(c)
if @stop
  self.pause = system.pause
  return
end
@wait_count += system.speed
  else
@text = nil
break
  end
  break if ! @skip
end
return if @text || @autoclose != -1
finish
return
  else
if @autoclose > 0
  @autoclose -= 1
  return
elsif @autoclose == 0
  terminate_message
  @autoclose = -1
  return
end
  end
end
if input_number
  input_number.update
  if Input.trigger?(13)
$game_system.se_play($data_system.decision_se)
$game_variables[$game_temp.num_input_variable_id] = input_number.number
$game_map.need_refresh = true
input_number.dispose
@input_number_window = nil
terminate_message
  end
  return
end
if @contents_showing
  self.pause = ($game_temp.choice_max == 0) & system.pause
  if Input.trigger?(12)
if $game_temp.choice_max > 0 && $game_temp.choice_cancel_type > 0
  $game_system.se_play($data_system.cancel_se)
  $game_temp.choice_proc.call($game_temp.choice_cancel_type - 1)
  terminate_message
end
  end
  if Input.trigger?(13)
if $game_temp.choice_max > 0
  $game_system.se_play($data_system.decision_se)
  $game_temp.choice_proc.call(self.index)
end
terminate_message
  end
  return
end
if ! @fade_out && $game_temp.message_text
  @contents_showing = temp.message_window_showing = true
  @stop = false
  @autoclose = -1
  @skip = (! system.letter_by_letter)
  reset_window
  refresh
  @wait_count, self.visible = 0, true
  return
end
return if ! visible
@fade_out = true
self.opacity -= 48
if self.opacity == 0
  self.visible = @fade_out = false
  $game_temp.message_window_showing = false
end
  end
  #--------------------------------------------------------------------------
  # * Updates Cursor Rectangle
  #--------------------------------------------------------------------------
  def update_cursor_rect
if index >= 0
  n = $game_temp.choice_start + @index
  y = (@fit_size ? system.height : 32) * n
  cursor_rect.set(4 + @start_x, y, @cursor_width.max + 8,
@fit_size ? system.height : 32)
else
  cursor_rect.empty
end
  end
end

#==============================================================================
# ** Window_NameBox
#------------------------------------------------------------------------------
#  This window is used to display the box above the message.
#==============================================================================

class Window_NameBox < Sprite
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_reader :back
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize(x, y, text)
dumb = Bitmap.new(160, 42)
dumb.font.name = system.font
size = dumb.text_size(text).width
dumb.dispose
@back = Window_Base.new(x, y, size + 12, 32)
skin = system.nb_skin ? system.nb_skin : $game_system.windowskin_name
@back.windowskin = RPG::Cache.windowskin(skin)
viewport = Viewport.new(x + 6, y + 5, size, 22)
@back.z = viewport.z = 9999
  super(viewport)
self.bitmap = Bitmap.new(size, 22)
bitmap.font.name = system.font
bitmap.draw_text(0, 0, size, 22, text)
  end
  #--------------------------------------------------------------------------
  # * Game Message
  #--------------------------------------------------------------------------
  def system() $game_system.message end
  #--------------------------------------------------------------------------
  # * Dispose
  #--------------------------------------------------------------------------
  def dispose
@back.dispose
@back = nil
super
  end
end

#==============================================================================
# ** Interpreter
#------------------------------------------------------------------------------
#  Adds a reader to the Event ID and Game Message
#==============================================================================

class Interpreter
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_reader :event_id
  #--------------------------------------------------------------------------
  # * Game Message
  #--------------------------------------------------------------------------
  def message
$game_system.message
  end
end

#==============================================================================
# ** Scene_Map
#------------------------------------------------------------------------------
#  Adds a reader to the Spriteset
#==============================================================================

class Scene_Map
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_reader :spriteset
end

Basically, for the purposes of the first script, I've been trying to extract the relevant information from the second script and paste it into the first. However, after spending a LONG time attempting to do this, I've had no success whatsoever.

Could anyone help me out, please? I'd be very grateful!
 
Here's one way...

Code:
class Window_Displayamessage < Window_Base
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    super(0, 0, messagewidth + 32, messageheight + 32)
    self.contents = Bitmap.new(width - 32, height - 32)
    self.opacity = 255
    self.back_opacity = 160
    self.pause = true
    refresh
  end
  #--------------------------------------------------------------------------
  # * Refresh
  #--------------------------------------------------------------------------
  def refresh
    self.contents.clear
    self.contents.font.color = normal_color
    self.contents.draw_text(0, 0, messagewidth, messageheight, $message)
  end
  def messagewidth
    # Calculate message width
    dummy_bitmap = Bitmap.new(32, 32)
    message_width = dummy_bitmap.text_size($message).width
    dummy_bitmap.dispose
    return message_width
  end
  def messageheight
    # Calculate message height
    dummy_bitmap = Bitmap.new(32, 32)
    message_height = dummy_bitmap.text_size($message).height
    dummy_bitmap.dispose
    return message_height
  end
end


You'll just need to define your rules for how you split up the message into lines,
and build that into your methods before getting the width & height.

Be Well
 
You're quite welcome. :)

I expect there are many other ways to do this. Some better even.
But, since the width & height only get set on creation (refresh), it
should be efficient.
Perhaps one of the Script Squids (Squid = Phreak = Wizard = Expert)
will lay some wizdom on us.  ;)

Be Well
 

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