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.

Interactive World Map

Hey everyone! I've been editing a script by SephirtohSpawn in order to mimick the Interactive World Map type system used in Final Fantasy Tactics. Everything is going good as of now but I've run into a road block. As you can see from the beginning of this video:

http://www.youtube.com/watch?v=inNMM_ijf4A&NR=1

There are lines that connect the different sites. When you press on one of the sites, the character moves across that line. I have no clue what the best method of doing that would be. Any thoughts?

Btw, here's the current progress of the edited script:

[rgss]#==============================================================================
# Title: Interactive World Map
# Version: 1.0
# Author: The Law G14 and SephirothSpawn
#==============================================================================
 
 
#==============================================================================
# ** Scene_Title
#------------------------------------------------------------------------------
#  This class performs title screen processing.
#==============================================================================
 
class Scene_Title
  #--------------------------------------------------------------------------
  # * Alias Listing
  #--------------------------------------------------------------------------
  alias law_world_map_scene_title_command_new_game command_new_game
  #--------------------------------------------------------------------------
  # * Command: New Game
  #--------------------------------------------------------------------------
  def command_new_game
    # Add game globe global variable
    $game_globe = Game_Globe.new
   
    # CUSTOMIZATION STARTS HERE
    $game_globe.add_location(Location.new("Woods", 1, 310, 195, 9, 12))
    # CUSTOMIZATION ENDS HERE
   
    # Call original method
    law_world_map_scene_title_command_new_game
  end
end
 
 
#==============================================================================
# ** Location
#------------------------------------------------------------------------------
#  This class handles locations on the world map.
#==============================================================================
 
class Location
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :name                     # Location name
  attr_accessor :map_id                   # Map ID
  attr_accessor :x_loc                    # X Coordinate of Location
  attr_accessor :y_loc                    # Y Coordinate of Location
  attr_accessor :tele_x                   # X Coordinate of Teleport Location
  attr_accessor :tele_y                   # Y Coordinate of Teleport Location
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize(name, map_id, x_loc, y_loc, tele_x, tele_y)
    # Assign parameters to instance variables
    @name = name
    @map_id = map_id
    @x_loc = x_loc
    @y_loc = y_loc
    @tele_x = tele_x
    @tele_y = tele_y
  end
end
 
 
#==============================================================================
# ** Game_Globe
#------------------------------------------------------------------------------
#  This class handles the world map
#==============================================================================
 
class Game_Globe
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :world                      # World
  attr_accessor :locations                  # Locations
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    @world = "Gai"
    @locations = []
  end
  #--------------------------------------------------------------------------
  # * Add Location
  #     location : new location within the World
  #--------------------------------------------------------------------------
  def add_location(location)
    @locations.push(location)
  end
  #--------------------------------------------------------------------------
  # * Remove Location
  #     location : name of the location within World
  #--------------------------------------------------------------------------
  def remove_location(location)
    # If location exists
    if @locations.include?(location)
      # Delete that location
      @locations.delete(location)
    end
  end
end
 
 
#==============================================================================
# ** Sprite
#------------------------------------------------------------------------------
#  The sprite class. Sprites are the basic concept used to display characters,
#  etc. on the game screen.
#==============================================================================
 
class Sprite
  #--------------------------------------------------------------------------
  # * Sets Sprites Coordinates
  #--------------------------------------------------------------------------
  def set_coords(x = 0, y = 0, z = 0, opacity = 255)
    # Set values based on parameters
    self.x = x
    self.y = y
    self.z = z
    self.opacity = opacity
  end
end
 
 
#==============================================================================
# ** Window_Base
#------------------------------------------------------------------------------
#  This class is for all in-game windows.
#==============================================================================
 
class Window_Base < Window
  #--------------------------------------------------------------------------
  # * Sets Windows Coordinates
  #--------------------------------------------------------------------------
  def set_coords(x = 0, y = 0, z = 0, opacity = 255)
    self.x = x
    self.y = y
    self.z = z
    self.opacity = opacity
  end
end
 
 
#==============================================================================
# ** Scene_Globe
#------------------------------------------------------------------------------
#  This class performs world map processing
#==============================================================================
 
class Scene_Globe
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize(meno_calling = false)
    # Parameters
    @world_name = $game_globe.world
    @menu_calling = meno_calling
    # Extra Instance Variables
    @locations = $game_globe.locations
  end
  #--------------------------------------------------------------------------
  # * Main Processing
  #--------------------------------------------------------------------------
  def main
    # Main Phase
    @phase = 0
    # Cursor Coordinate Setup & On Location Check
    @x = 320
    @y = 240
    # Map Sprite
    @map1 = Sprite.new
    @map1.bitmap = RPG::Cache.picture("Maps/Globe")
    # Cursor
    @cursor = Sprite.new
    @cursor.bitmap = RPG::Cache.picture("Maps/Hand")
    @cursor.set_coords(@x - 16, @y - 16, 5)
    # Teleport Map
    @tele_map = Sprite.new
    @tele_map.set_coords(8, 232)
    # Location Name
    @location = Window_Help.new
    @location.set_coords(0, 0, 15, 150)
    @location.visible = true
    @location.set_text(@world_name, 1)
    # Teleport Command Window
    @teleport_window = Window_Command.new(160, ["Travel", "Cancel"])
    @teleport_window.set_coords(472, 376, 10, 150)
    @teleport_window.active = @teleport_window.visible = false
    # Exit Command
    @exit_window = Window_Command.new(200, ["Rockdale Town?", "Cancel"])
    @exit_window.index = 1
    @exit_window.set_coords(432, 376, 15, 150)
    @exit_window.active = @exit_window.visible = false
    # Quick Arrays Setup
    @objects = [@map1, @cursor, @char, @char_sprite, @tele_map, @location,
    @teleport_window, @exit_window]
    # Execute transition
    Graphics.transition
    # Main loop
    loop do
      # Update game screen
      Graphics.update
      # Update input information
      Input.update
      # Call update method
      update
      # Abort loop if screen is changed
      if $scene != self
        break
      end
    end
    # Prepare for transition
    Graphics.freeze
    # Dispose objects
    @objects.each {|x| x.dispose}
  end
  #--------------------------------------------------------------------------
  # * Update
  #--------------------------------------------------------------------------
  def update
    # Update Objects
    @objects.each {|x| x.update}
    # Updates Current Phase
    case @phase
    when 0 # ~ Normal Mode~
      [@tele_map, @teleport_window, @exit_window].each {|x| x.visible = false}
      @cursor.visible = true
      normal_mode
    when 1 # ~ Teleport ~
      [@cursor, @exit_window].each {|x| x.visible = false}
      @tele_map.visible = @teleport_window.visible = @teleport_window.active = true
      teleport
    when 2 # ~ Exit ~
      [@tele_map, @teleport_window].each {|x| x.visible = false}
      @cursor.visible = @exit_window.visible = @exit_window.active = true
      exit
    end
    # Update cursor positioning
    @cursor.set_coords(@x-16, @y-16, 5)
  end
  #--------------------------------------------------------------------------
  # * Normal Mode
  #--------------------------------------------------------------------------
  def normal_mode
    # Set teleport state to false
    teleport = false
    # Check if cursor is on available location
    for i in 0...@locations.size
      loc = @locations
      ox1, oy = @x - @map1.x, @y
      x, y = loc.x_loc, loc.y_loc
      a1 = ox1 < x + 16 ? true :false
      a2 = ox1 > x - 16 ? true :false
      a3 = oy < y + 16 ? true :false
      a4 = oy > y - 16 ? true :false
      if a1 && a2 && a3 && a4
        location = loc
        @location.set_text(loc.name, 1)
        teleport = true
      end
    end
    # Set Help window text and change cursor tone accordingly
    a = teleport ? [Tone.new(255, 0, 0, 255), true] : [Tone.new(0, 0, 0, 255), false]
    @cursor.tone = a[0]
    if a[1] == true
      @location.set_text(loc.name, 1)
    else
      @location.set_text(@world_name, 1)
    end
    # Input Processing    
    if Input.trigger?(Input::B)
      @temp_phase = 0
      @phase = 2
    elsif Input.trigger?(Input::C) && teleport
      @temp_loc = location
      @tele_map.bitmap = RPG::Cache.picture("Maps/#{location.name}")
      @temp_phase = 0; @phase = 1
    end
    # Directional Keys
    if Input.press?(Input::RIGHT)
      @x +=3
    end
    if Input.press?(Input::LEFT)
      @x -=3
    end
    if Input.press?(Input::DOWN)
      @y +=3
    end
    if Input.press?(Input::UP)
      @y -=3
    end
  end
  #--------------------------------------------------------------------------
  # * Teleport
  #--------------------------------------------------------------------------
  def teleport
    teleport = true
    if @temp_loc.map_id == nil
      teleport = false
      @teleport_window.disable_item(0)
    end
    if Input.trigger?(Input::B)
      @teleport_window.active = false
      @phase = @temp_phase
    elsif Input.trigger?(Input::C)
      case @teleport_window.index
      when 0
        if teleport
          $game_map.setup(@temp_loc.map_id)
          $game_player.moveto(@temp_loc.tele_x , @temp_loc.tele_y)
          $scene = Scene_Map.new
          $game_map.refresh
        else
          $game_system.se_play($data_system.buzzer_se)
        end
      when 1
        @teleport_window.active = false
        @phase = @temp_phase
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Exit
  #--------------------------------------------------------------------------
  def exit
    # If press Cancel Button
    if Input.trigger?(Input::B)
      @exit_window.active = false
      @phase = @temp_phase
    # If press Confirm Button
    elsif Input.trigger?(Input::C)
      # Branch based on index
      case @exit_window.index
      when 0
        $scene = Scene_Map.new
      when 1
        @exit_window.active = false
        @phase = @temp_phase
      end
    end
  end
end
 
 
#==============================================================================
#
# *** END OF SCRIPT ***
#
#==============================================================================
[/rgss]
 
There's a few approached regarding that... obviously, moving from point A to point B is the obviously easiest choice here, however pretty unsatisfying if you're looking to follow snakey paths. The next best thing (and probably the solution of choice) is waypoints, aka point A to point B to point C to point D to... you get it ^^ If I look at the map at the beginning of the video and how the paths are drawn, I don't think there's a better way to do this when I take simplicity and amount of work into consideration.
What you need for it is not only town coordinates, but also road coordinates. I'd suggest using a hash like so:

Code:
roads = {

  'newyork_to_washington' => [[112, 160], [123, 189], [128, 214]]

}

Make sure you don't include the town's coordinates, but instead use these from their respective position array that you necessarily have otherwise. Also, make sure you are aware of the Array.reverse method prior to working on it, as it'll come in handy.
At every waypoint change, you can re-calculate the direction the character should be facing, to not end up with a backwards-walking sprite. I'm sure you know how to do that based on two x and y coords.


Now, this is definately the most simple, therefore fastest, and maybe best looking result. Just for completeness' sake, let me mention the third and definately hardest method of them all: Using Bezier curves - in other words, paths. Not sure if you're familiar with it, but basically, you're creating a path by a few stored numbers that you can later on follow. This method uses vector calculations to calculate individual waypoints, meaning your character will be walking on actual round paths.
This method would require either writing a vector method or stealing Ruby's, and then modifying it. It needs quite a bit of work, not taking into account yet that you still have to write the vectors (which is a bit harder to figure out than pixel coordinates).

I'm just mentioning this so you've heard of it and are aware of the possibility. I can definately encourage you to play around with it, as vectors are often very useful... however, for this case, I can't recommend messing with it at all.
 
Thanks for the quick response BlueScope :)

As you said, your first method is the easiest choice, but the results aren't what I'm looking for, especially since I wanted the player to actually follow the path.

Your next option seems pretty satisfying taking simplicity and results into account. It makes a lot of sense too and I'm suprised I didn't come up with that lol If anything, that seems to be the method I'll test out first.

As for your final method where you spoke of Bezier curves, that seems very nice although I'm not familiar with such a method. I'll definetly play around with it, but as of now, I think I'll stick with the first option and see how that plays out.

Thanks for all the help BlueScope! I'll update the topic if I'm able to get somewhere with one of your methods.
 

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