Хорошо! Хорошо!:  0
Плохо! Плохо!:  0
Страница 1 из 3 123 ПоследняяПоследняя
Показано с 1 по 10 из 21

Тема: Скрипт нахождения пути

  1. #1
    Хранитель Форума Аватар для Валера
    Информация о пользователе
    Регистрация
    15.04.2008
    Адрес
    Москва
    Сообщений
    14,006
    Записей в дневнике
    3
    Репутация: 166 Добавить или отнять репутацию

    Скрипт нахождения пути

    Это великолепная дема демонстрирует скрипт нахождения пути из А в В в любом лабиринте. Демок там 5 - на все случаи жизни. Даже если пути нет, чар приходит в ближайшую к цели точку ( хотя это по всей видимости другой скрипт ). А потому, прошу знающих людей разобраться в нем и описать применение.
    ( хотелось бы так же понять, на каком алгоритме он работает )

    http://www.excalistia.com/RPG/AStar14.zip
    205кб

    форум:
    http://www.hbgames.org/forums/viewtopic.php?t=55274.0

    Качать абсолютно всем и использовать в играх!

    Спойлер И как тебе не стыдно-то, а, Валера?:


    Оборона форта: http://rghost.ru/8kLGxFtD2
    Сделать, чтоб все происходило, как я хочу, - вот, собственно, и весь мейкер!
    Адский Рейд: http://rpgmaker.su/vbdownloads.php?d...downloadid=106

  2. #2

    По умолчанию

    1) Открываешь редактор скриптов и, над Main, содаешь новый скрипт (кнопка Insert). Называешь его как угодно, в соедржимое вставляешь:
    Спойлер Код:
    Код:
    #==============================================================================
    # ** A* Pathfinding Script
    #------------------------------------------------------------------------------
    #  Script by            :   RPG (aka Cowlol)
    #  Last Update          :   September 26th, 2008
    #  Version              :   1.4
    #  Contact Information  :   ArePeeGee on AIM
    #------------------------------------------------------------------------------
    #  A relatively efficient implementation of the A* pathfinding algorithm.
    # For more information on A*, check the following links:
    #     1. http://www.policyalmanac.org/games/aStarTutorial.htm
    #     2. http://www-cs-students.stanford.edu/~amitp/gameprog.html
    #------------------------------------------------------------------------------
    # Usage:
    #
    #   First you have to add this script somewhere under the default scripts
    #   (and above main), I assume you know how to add a new script section and 
    #   paste this script there. I don't know much about the SDK thing, but I 
    #   don't think you'll get any trouble when using this scripts with others.
    #   Note that this script overwrites the move_type_custom method of the
    #   Game_Character class, any other script that modifies that method 
    #   might clash with this one.
    #
    #   Now, simply create a new instance of the A_Star_Pathfinder class and 
    #   supply it with the required parameters. For example, to have the player
    #   move to tile (x, y), you could  add a script command to a new event
    #   page and type something like:
    #
    #       goal_node = Node.new(x, y)             
    #       path = A_Star_Pathfinder.new(goal_node)
    #
    #   The constructor can take more information, such as the character
    #   that will move according to the result of pathfinding (Game_Character, 
    #   map event ID, or -1 for player (default)), and whether the character 
    #   should follow the path right away (true by default, if you set it to 
    #   false, you'll have to call generate_path yourself). 
    #   Here's another example of usage:
    #
    #       node = Node.new(x, y)
    #       char = event_id         # same as char = $game_map.events[event_id]
    #       path = A_Star_Pathfinder.new(node, char, false)
    #       # do other stuff
    #       path.generate_path      # and then generate the path
    #
    #   As an alternative, you can call the constructor without parameters,
    #   and call the setup, calculate_path, and generate_path manually.
    #   This gives you more control because the setup method allows you to
    #   specify more options such as methods to call when the path is
    #   reached or couldn't be found (implemented as Proc objects). 
    #   You can also specify size of the goal area instead of looking for only 
    #   one tile, and indicate whether the character should get as close as 
    #   possible to goal if no path was found. Here's an example of such usage:
    #
    #     path = A_Star_Pathfinder.new
    #     goal = Node.new(1, 0)
    #     char = $game_player
    #     range = 1
    #     get_close = true
    #     # lambda creates Procs, an alternative is Proc.new { ... }
    #     reach = lambda { p "REACHD GOAL!!!" }
    #     fail = lambda { p "COULDN'T FIND PATH!!!" }
    #     path.setup(goal, char, range, get_close, reach, fail)
    #     path.calculate_path
    #     path.generate_path
    #
    #   If you used earlier versions of this script, you were able to supply
    #   a limit on the number of pathfinding iterations performed (in case
    #   you don't want the script to run for too long); this is now specified
    #   by manually changing the constant ITERATION_LIMIT in A_Star_Pathfinder
    #   class. A value of -1 (default) means no such limit is used.
    #
    #   Another interesting constant is COLLISION_WAIT, which is set to 5 by
    #   default. This constant controls a feature that allows the character
    #   to generate a new path if it was interrupted when following a path.
    #   For example, a path might have been generated but as the character
    #   was following it another character moved to block the path, setting
    #   COLLISION_WAIT to any value bigger than -1 forces the generation of
    #   another path to get around blocking characters, with the value of
    #   the constant used to add some waiting between such attempts.
    #
    #   A related constant is COLLISION_LIMIT which imposes a limit on the
    #   maximum number of times the character should try to find a new path
    #   in case of collision, this is important because rare map design
    #   and event movement conditions could result in the character stuck
    #   trying to generate new paths forever. If you don't care for that,
    #   you could set it to -1 to disregard the limit.
    #
    #   Starting with version 1.4, characters could be supplied with
    #   multiple paths. Once the first path is reached or couldn't be
    #   found, the next path is generated and followed, and so on. 
    #   If you'd rather new paths were followed immediately instead of
    #   waiting for current path to be followed, you'll have to stop
    #   all pending paths by calling the stop_all_paths method on the
    #   character and then creating the new path. You could also just
    #   stop following the current path by calling the stop_path method.
    #
    #------------------------------------------------------------------------------
    # Version Information:
    #   - Version 1.4:
    #       - Characters can now follow multiple paths. Generating another
    #         path while character is following a path adds the new path
    #         to a list so that it would be followed after all pending paths
    #         were followed, instead of overwriting current path.
    #       - Characters now get stop_path and stop_all_paths methods to
    #         stop following current path and follow the next one in the
    #         list, and to stop all paths and empty the list. stop_all_paths
    #         could be used to imitate the old behavior of overwriting
    #         current path and following new one immediately.
    #       - Fixed various bugs caused by the introduction of multiple paths.
    #       - You can now supply integer values in place of Game_Character,
    #         the value is the event map ID or -1 for the player character.
    #   - Version 1.3:
    #       - Changed collision_wait and limit variables to COLLISION_WAIT
    #         and ITERATION_LIMIT constants that are changed manually in
    #         the script and that apply to all pathfinding. I think this
    #         makes the script neater.
    #       - Added a COLLISION_LIMIT constant to control number of path-
    #         finding attempts on collision.
    #       - Added the ability to specify a goal area instead of single node,
    #         this is achieved by supplying the setup method with a range of
    #         tiles around the goal, with 0 (the default) disabling that
    #         feature. Pathfinding succeeds as soon as a tile within the
    #         goal's range (see in_range? method) is found.
    #       - Character can now try to get as close as possible to tile
    #         if a path wasn't found.
    #       - Switched to a zlib/libpng license for terms of use, which
    #         is mostly a more formal version of the old terms.
    #   - Version 1.2:
    #       - Fixed a recursive bug with tiles with directional passability,
    #         thanks to Twin Matrix for pointing it out!
    #       - Removed the cache pass feature because it was useless.
    #   - Version 1.1:
    #       - Added a mechanism to prevent the generated path from being
    #         blocked by other characters. When such a collision happens,
    #         the character will try to generate a new path to goal. This
    #         can be turned off if you set collision_wait to -1
    #       - Now you can provide blocks to be called when the path is
    #         reached or if no path is found.
    #       - Added the cache pass flag to allow generating passability
    #         information in a Table for faster access. This was inspired
    #         by a post by Anaryu at 
    #         http://www.rmxp.org/forums/showthread.php?t=16589
    #       - Better documentation
    #   - Version 1.0:
    #       - Initial release version.
    #------------------------------------------------------------------------------
    # Known bugs:
    #
    #   - Slower than Eivien's pathfinder at:
    #       http://www.rmxp.org/forums/showthread.php?t=24242
    #     this is not really a bug but I'm interested in learning more
    #     about efficient implementations of A*.
    #   - Found a bug or have some ideas for the next version? Tell me on AIM!
    #------------------------------------------------------------------------------
    # Terms of Use:
    #
    #   I'm releasing this script under a zlib/libpng license which means
    #   you can use it any way you like as long as you don't claim it as
    #   your own and keep the following notice unchanged. If you have any
    #   questions or problems regarding this script, feel free to contact me.
    #
    #   Copyright (c) 2007 Firas Assad
    #
    #   This software is provided 'as-is', without any express or implied
    #   warranty. In no event will the authors be held liable for any damages
    #   arising from the use of this software.
    #
    #   Permission is granted to anyone to use this software for any purpose,
    #   including commercial applications, and to alter it and redistribute it
    #   freely, subject to the following restrictions:
    # 
    #   1.  The origin of this software must not be misrepresented; you must 
    #       not claim that you wrote the original software. If you use this 
    #       software in a product, an acknowledgment in the product 
    #       documentation would be appreciated but is not required.
    #
    #   2.  Altered source versions must be plainly marked as such, and 
    #       must not be misrepresented as being the original software.
    #
    #   3.  This notice may not be removed or altered from any 
    #       source distribution.
    #
    #
    #==============================================================================
    
    #==============================================================================
    # ** Node
    #------------------------------------------------------------------------------
    #  A node represents part of the path generated by the pathfinder,
    #  It corrosponds to a single tile
    #==============================================================================
    
    class Node
      #--------------------------------------------------------------------------
      # * Public Instance Variables
      #--------------------------------------------------------------------------
      attr_accessor :x                        # x coordinate of current node
      attr_accessor :y                        # y coordinate of current node
      attr_accessor :parent                   # parent node
      attr_accessor :g                        # cost of getting to this node
      attr_accessor :h                        # distance to goal (heuristic)
      #--------------------------------------------------------------------------
      # * Object Initialization
      #--------------------------------------------------------------------------
      def initialize(x, y, parent = nil, g = 0, h = 0)
        @x = x
        @y = y
        @parent = parent
        @g = g
        @h = h
      end
      #--------------------------------------------------------------------------
      # * The Total 'Cost' at This Node (AKA f(n))
      #--------------------------------------------------------------------------
      def cost
        # f(n) = g(n) + h(n)
        return @g + @h
      end
      #--------------------------------------------------------------------------
      # * Two Nodes Are Equal If They Are on the Same Tile
      #--------------------------------------------------------------------------
      def ==(other)
        return false unless other and other.is_a?(Node)
        return true if other.x == @x and other.y == @y
        return false
      end
      
      #--------------------------------------------------------------------------
      # * Check If Another Node is Within This Node's Range
      #--------------------------------------------------------------------------
      def in_range?(other, range)
        # Get absolute value of difference
        abs_sx = (@x - other.x).abs
        abs_sy = (@y - other.y).abs
        return abs_sx + abs_sy <= range
      end
    end
    
    #==============================================================================
    # ** A_Star_Pathfinder
    #------------------------------------------------------------------------------
    #  This class generates a path using the A* algorithm. Not a very good
    #  implementation but I'm still proud of it.
    #==============================================================================
    
    class A_Star_Pathfinder
      #--------------------------------------------------------------------------
      # * Constants
      #--------------------------------------------------------------------------
      ITERATION_LIMIT = -1        # Maximum number of loop iterations before
                                  # the pathfinder gives up. -1 for infinity.
      COLLISION_WAIT = 5          # No. of frames to wait before attempting to
                                  # find another path in case of collision; 
                                  # -1 disables such collision behevior. 
      COLLISION_LIMIT = 30        # Maximum number of attempts to find another
                                  # path in case of collision. -1 for infinity.
      #--------------------------------------------------------------------------
      # * Public Instance Variables
      #--------------------------------------------------------------------------
      attr_reader   :goal_node                # the goal!
      attr_reader   :character                # character looking for a path
      attr_reader   :found                    # was the path found?
      attr_reader   :route                    # the route returned after 
                                              # calling generate_path
      attr_accessor :range                    # Size of goal area
      attr_accessor :get_close                # If true and no path is found then
                                              # get as close as possible.
      attr_accessor :reach_method             # Proc called when goal is reached
      attr_accessor :fail_method              # Proc called when no path is found
      attr_accessor :original_goal_node       # goal node before pathfinding
      attr_accessor :collision_counter        # counter for the number of times path
                                              # was obstructed and re-generated
      
      #--------------------------------------------------------------------------
      # * Object Initialization
      #     goal_node : A Node representing the end point
      #     char      : The character that'd use the result of pathfinding,
      #                 either a Game_Character, event ID, or -1 for player
      #     run       : If true, the path is also generated
      #--------------------------------------------------------------------------
      def initialize(goal_node = nil, char = -1, run = true)
        # If no goal node is provided, this acts as a default constructor that
        # takes no parameters and does nothing. Useful if you do things manually
        unless goal_node
          return
        end
        # Setup variables
        setup(goal_node, char)
        # Find the optimal path
        calculate_path
        # We're done, time to generate the path
        generate_path if run
      end
      
      #--------------------------------------------------------------------------
      # * Setup Initial Values for Variables
      #     goal_node : A Node representing the end point
      #     character : The character that'd use the result of pathfinding
      #     range     : The size of the goal area, 0 means the goal is the
      #                 node, 1 means within 1 tile around goal, etc.
      #     close     : If true the character will try to get as close as
      #                 possible to path if it isn't found.
      #     reach     : A Proc that will be called when the goal is reached
      #     fail      : A proc that will be called if no path was found
      #--------------------------------------------------------------------------
      def setup(goal_node, character, range = 0, close = false,
                reach = nil, fail = nil)
        @original_goal_node = Node.new(goal_node.x, goal_node.y)
        @goal_node = Node.new(goal_node.x, goal_node.y)
        @character = A_Star_Pathfinder.int_to_character(character)
        unless @character
          raise "A_Star_Pathfinder Error : Invalid Character"
        end
        @start_node = Node.new(@character.x, @character.y)
        @range = range
        @get_close  = close
        @reach_method = reach
        @fail_method = fail
    
        @open_list = Array.new            # List of nodes to be checked,
                                          # implemented as a binary heap
        @open_list.push(@start_node)    
        @closed_list = Hash.new           # Set of nodes already checked, this
                                          # is a hash of arrays of [x, y] pairs
                                          # representing map tiles
        @found = false
        # Saves node with lowest h in case we want to get close to it
        @nearest = Node.new(0, 0, 0, -1)
      end
      
      #--------------------------------------------------------------------------
      # * Search For the Optimal Path
      #--------------------------------------------------------------------------
      def calculate_path
        iterations_counter = 0
        # Only do calculation if goal is actually passable, unless we only
        # need to get close or within range
        if @character.passable?(@goal_node.x, @goal_node.y, 0) or 
           @get_close or
           @range > 0
          until @open_list.empty?
            iterations_counter = iterations_counter + 1
            # Prevents script hanging
            Graphics.update if (iterations_counter % 200 == 0) 
            # If we hit the iteration limit, exit
            if ITERATION_LIMIT != -1 and iterations_counter >= ITERATION_LIMIT
              @found = false
              break
            end
            # Get the node with lowest cost and add it to the closed list
            @current_node = find_lowest_cost
            @closed_list[[@current_node.x, @current_node.y]] = @current_node
            if @current_node == @goal_node or 
               (@range > 0 and @goal_node.in_range?(@current_node, @range))
              # We reached goal, exit the loop!
              @original_goal_node = @goal_node
              @goal_node = @current_node
              @found = true
              break
            else # if not goal
              # Keep track of the node with the lowest cost so far
              if @current_node.h < @nearest.h or @nearest.h < 1
                @nearest = @current_node
              end
              # Get adjacent nodes and check if they can be added to the open list
              adjacent_nodes = get_adjacent_nodes(@current_node)
              for adj_node in adjacent_nodes
                if skip_node?(adj_node)
                  # Node already exists in one of the lists, skip it
                  next
                end
                # Add node to open list following the binary heap conventions
                heap_add(@open_list, adj_node)
              end
            end
          end
        end
        # If no path was found, see if we can get close to goal
        unless @found
          if @get_close and @nearest.h > 0
            @goal_node = @nearest
            setup(@goal_node, @character, @range, @get_close, @reach_method, 
                     @fail_method)
            calculate_path
          else
            # Call the fail method if one is provided
            if @fail_method
              @fail_method.call
            end
          end
        end
      end
      
      #--------------------------------------------------------------------------
      # * Return Game_Character Object From Integer Arguments
      #     char  : If -1, the player character is returned; for other integers,
      #             map event with ID matching the integer is returned
      #--------------------------------------------------------------------------  
      def self.int_to_character(char)
        if char.is_a?(Integer)
          if char == -1
            char = $game_player
          else
            char = $game_map.events[char]
          end
        end
        return char
      end
      
      #--------------------------------------------------------------------------
      # * Add an Item to the Binary Heap (for open_list). This is Based on
      #   Algorithm Here: http://www.policyalmanac.org/games/binaryHeaps.htm
      #     array : Array to add the node to
      #     item  : Item to add!
      #--------------------------------------------------------------------------
      def heap_add(array, item)
        # Add the item to the end of the array
        array.push(item)
        # m is the index of the 'current' item
        heap_update(array, array.size - 1)
      end
      
      #--------------------------------------------------------------------------
      # * Make Sure the Item at Index is in the Right Place
      #     array : Array to update
      #     index : Index of the item
      #--------------------------------------------------------------------------
      def heap_update(array, index)
        m = index
        while m > 0
          # If current item's cost is less than parent's
          if array[m].cost <= array[m / 2].cost
            # Swap them so that lowest cost bubbles to top
            temp = array[m / 2]
            array[m / 2] = array[m]
            array[m] = temp
            m /= 2
          else
            break
          end
        end
      end
      
      #--------------------------------------------------------------------------
      # * Remove an Item from the Binary Heap (for open_list) & Return it.
      #   This is Based on Algorithm Here: 
      #   http://www.policyalmanac.org/games/binaryHeaps.htm
      #     array : Array to remove the node from
      #--------------------------------------------------------------------------
      def heap_remove(array)
        if array.empty?
          return nil
        end
        #Get original first element
        first = array[0]
        # Replace first element with last one
        last = array.slice!(array.size - 1)
        if array.empty?
          return last
        else
          array[0] = last
        end
        v = 0   # Stores a smaller child, if any
        # Loop until no more swapping is needed
        while true
          u = v
          # If both children exist
          if 2 * u + 1 < array.size
            v = 2 * u if array[2 * u].cost <= array[u].cost
            v = 2 * u + 1 if array[2 * u + 1].cost <= array[v].cost
          # If only one child exists
          elsif 2 * u < array.size
            v = 2 * u if array[2 * u].cost <= array[u].cost
          end
          # If at least one child is less than parent, swap them
          if u != v
            temp = array[u]
            array[u] = array[v]
            array[v] = temp
          else
            break
          end
        end
        # Return the original first node (which was removed)
        return first
      end
      
      #--------------------------------------------------------------------------
      # * Can We Skip This Node? (because it already exists in a list)
      #     node : Node to check
      #--------------------------------------------------------------------------
      def skip_node?(node)
        skip_node = false
        copy = @open_list.index(node)
        if copy
          #If the existing copy is 'better' than the new one, skip new node
          if @open_list[copy].cost <= node.cost
            skip_node = true
          else
            # Otherwise swap them, making sure heap is in right order
            @open_list[copy] = node
            heap_update(@open_list, copy)
            skip_node = true
          end
        end
        # The closed list is a hash so this is relatively easier
        if @closed_list[[node.x, node.y]]
          # If the existing copy is 'better' than the new one
          if @closed_list[[node.x, node.y]].cost <= node.cost
            skip_node = true
          else
            # Update the existing node
            @closed_list[[node.x, node.y]] = node
          end
        end
        # Return the result
        return skip_node
      end
    
      #--------------------------------------------------------------------------
      # * Find Node With Lowest Cost on the Open List
      #--------------------------------------------------------------------------
      def find_lowest_cost
        # Just return top of the heap
        return  heap_remove(@open_list)
      end
      
      #--------------------------------------------------------------------------
      # * Distance Between Two Points (Heuristic)
      #--------------------------------------------------------------------------
      def distance(x1, y1, x2, y2)
        # A simple heuristic value (Manhattan distance)
        return ((x1 - x2).abs + (y1 - y2).abs)
      end
      
      #--------------------------------------------------------------------------
      # * Get a List of Adjacent Nodes
      #     node : The 'center' node
      #--------------------------------------------------------------------------
      def get_adjacent_nodes(node)
        # Array to hold the nodes
        nodes = Array.new
        # Right
        new_x = node.x + 1
        new_y = node.y
        add_node(nodes, new_x, new_y, node)
        # Left
        new_x = node.x - 1
        new_y = node.y
        # Down
        add_node(nodes, new_x, new_y, node)
        new_x = node.x
        new_y = node.y + 1
        add_node(nodes, new_x, new_y, node)
        # Up
        new_x = node.x
        new_y = node.y - 1
        add_node(nodes, new_x, new_y, node)
        return nodes
      end
      
      #--------------------------------------------------------------------------
      # * Add a Node to an Array
      #--------------------------------------------------------------------------
      def add_node(array, x, y, parent)
        direction = get_direction(x, y, parent.x, parent.y)
        if @character.passable?(parent.x, parent.y, direction)
          # The cost of movement one step to a new tile is always 1
          g = parent.g + 1
          # The heuristic is simply the distance
          h = distance(x, y, @goal_node.x, @goal_node.y)
          new_node = Node.new(x, y, parent, g, h)
          array.push(new_node)
        end
      end
    
      #--------------------------------------------------------------------------
      # * Get Direction From a Point to Another
      #--------------------------------------------------------------------------
      def get_direction(x1, y1, x2, y2)
        # If first point is to the ... of the second
        if x1 > x2        # right
          return 6
        elsif x1 < x2     # left
          return 4
        elsif y1 > y2     # bottom
          return 2
        elsif y1 < y2     # top
          return 8
        end
        # Otherwise they are on the same position
        return 0
      end
    
      #--------------------------------------------------------------------------
      # * Generate the Path by Following Parents and Return it 
      #   as RPG::MoveRoute
      #     follow : If true the path is assigned to the character as a
      #     forced move route.
      #--------------------------------------------------------------------------
      def generate_path(follow = true)
        # There's no path to generate if no path was found
        if !@found
          return
        end
        # Create a new move route that isn't repeatable
        @route = RPG::MoveRoute.new
        @route.repeat = false
        # Generate path by starting from goal and following parents
        node = @goal_node
        code = 0    # Movement code for RPG::MoveCommand
        while node.parent
          # Get direction from parent to node as RPG::MoveCommand
          direction = get_direction(node.parent.x, node.parent.y, node.x, node.y)
          case direction
            when 2 # Up
              code = 4
            when 4 # Left
              code = 3
            when 6 # Right
              code = 2
            when 8 # Down
              code = 1
          end
          # Add movement code to the start of the array
          @route.list.unshift(RPG::MoveCommand.new(code)) if code != 0
          node = node.parent
        end
        # If the path should be assigned to the character
        if follow and !@route.list.empty?
          @character.add_path(self)
        end
        # Return the constructed RPG::MoveRoute
        return @route
      end
    end
    
    #==============================================================================
    # ** Game_Character 
    #------------------------------------------------------------------------------
    #  Just make the move_route variables public
    #==============================================================================
    
    class Game_Character
      attr_accessor :move_route_forcing
      attr_accessor :move_route
      attr_accessor :a_star_paths         # all the paths assigned to character
      #--------------------------------------------------------------------------
      # * Object Initialization
      #--------------------------------------------------------------------------
      # Alias to add path variable, 'stack level too deep' error is prevented
      # since $@ (error locations) is set after player presses F12
      unless $@
        alias :a_star_pathfinder_old_initialize :initialize
      end
      def initialize
        a_star_pathfinder_old_initialize
        @a_star_paths = []
      end
      #--------------------------------------------------------------------------
      # * Add a Path to be Followed by Character
      #-------------------------------------------------------------------------- 
      def add_path(path)
        path.collision_counter = 0
        @a_star_paths.push(path)
        if @a_star_paths.size == 1
          force_move_route(path.route)
        end
      end
      
      #--------------------------------------------------------------------------
      # * Stop Custom Movement
      #--------------------------------------------------------------------------
      def stop_custom_movement
        if @move_route
          @move_route_index = @move_route.list.size
          # The move route is no longer forced (moving ended)
          @move_route_forcing = false
          # Restore original move route
          @move_route = @original_move_route
          @move_route_index = @original_move_route_index
          @original_move_route = nil
        end
      end
      
      #--------------------------------------------------------------------------
      # * Follow Next Path, if Any
      #--------------------------------------------------------------------------   
      def follow_next_path
        stop_custom_movement
        @a_star_paths.shift
        if @a_star_paths[0]
          # Setup path again to reflect any changes since original creation
          goal = @a_star_paths[0].original_goal_node
          char = @a_star_paths[0].character
          range = @a_star_paths[0].range
          close = @a_star_paths[0].get_close
          reach = @a_star_paths[0].reach_method
          fail = @a_star_paths[0].fail_method
          @a_star_paths[0].setup(goal, char, range, close, reach, fail)
          @a_star_paths[0].calculate_path
          @a_star_paths[0].generate_path(false)
          force_move_route(@a_star_paths[0].route) if @a_star_paths[0].found
        end
      end
      
      #--------------------------------------------------------------------------
      # * Stop Following Current Path
      #-------------------------------------------------------------------------- 
      def stop_path
        if @a_star_paths[0]
          stop_custom_movement
          follow_next_path 
        end
      end
      
      #--------------------------------------------------------------------------
      # * Stop All Generated Paths
      #-------------------------------------------------------------------------- 
      def stop_all_paths
        stop_custom_movement
        @a_star_paths = []
      end
      
      #--------------------------------------------------------------------------
      # * Move Type : Custom (move event, pattern, etc.)
      #   Note: The script overwrites this method, which _might_ lead to
      #   compatibility problems with other scripts. You can remove this
      #   method to fix any such problem, but the character won't be able
      #   to detect the need to recalculate the path.
      #--------------------------------------------------------------------------
      def move_type_custom
        # Interrupt if not stopping
        if jumping? or moving?
          return
        end
        # For each move command starting from the index
        while @move_route_index < @move_route.list.size
          # Get the move command at index
          command = @move_route.list[@move_route_index]
          # If command code is 0 (end of list)
          if command.code == 0
            # If [repeat action] option is ON
            if @move_route.repeat
              # Reset move route index to the top of the list
              @move_route_index = 0
            end
            # If [repeat action] option is OFF
            unless @move_route.repeat
              # If move route is forced and not repeating
              if @move_route_forcing and not @move_route.repeat
                # The move route is no longer forced (moving ended)
                @move_route_forcing = false
                # Restore original move route
                @move_route = @original_move_route
                @move_route_index = @original_move_route_index
                @original_move_route = nil
    ### CODE ADDED HERE ############################################################
                # If there was a path to follow and we reached goal
                if @a_star_paths[0]
                  if self.x == @a_star_paths[0].goal_node.x and 
                      y == @a_star_paths[0].goal_node.y
                    # Call the reach method if one was provided
                    if @a_star_paths[0].reach_method
                      @a_star_paths[0].reach_method.call
                    end
                  end
                  follow_next_path 
                end
    ### ADDED CODE ENDS ############################################################
              end
              # Clear stop count
              @stop_count = 0
            end
            return
          end # if command.code == 0
          # For move commands (from move down to jump)
          if command.code <= 14
            # Branch by command code
            case command.code
            when 1  # Move down
              move_down
            when 2  # Move left
              move_left
            when 3  # Move right
              move_right
            when 4  # Move up
              move_up
            when 5  # Move lower left
              move_lower_left
            when 6  # Move lower right
              move_lower_right
            when 7  # Move upper left
              move_upper_left
            when 8  # Move upper right
              move_upper_right
            when 9  # Move at random
              move_random
            when 10  # Move toward player
              move_toward_player
            when 11  # Move away from player
              move_away_from_player
            when 12  # 1 step forward
              move_forward
            when 13  # 1 step backward
              move_backward
            when 14  # Jump
              jump(command.parameters[0], command.parameters[1])
            end
            # If movement failure occurs when [Ignore if can't move] option is OFF
            if not @move_route.skippable and not moving? and not jumping?
    ### CODE ADDED HERE ############################################################
              # If there's a path but it couldn't be followed (probably because
              # another character blocked it)
              if @a_star_paths[0] and 
                 A_Star_Pathfinder::COLLISION_WAIT >= 0 and
                 (A_Star_Pathfinder::COLLISION_LIMIT < 0 or
                 @a_star_paths[0].collision_counter <= A_Star_Pathfinder::COLLISION_LIMIT)
                # Setup path again to update starting location.
                # original goal node is used because pathfinding changes
                # the goal node to current node
                goal = @a_star_paths[0].original_goal_node
                char = @a_star_paths[0].character
                range = @a_star_paths[0].range
                close = @a_star_paths[0].get_close
                reach = @a_star_paths[0].reach_method
                fail = @a_star_paths[0].fail_method
                counter = @a_star_paths[0].collision_counter
                # Find another path to goal
                @a_star_paths[0] = A_Star_Pathfinder.new
                @a_star_paths[0].setup(goal, char, range, close, reach, fail)
                @a_star_paths[0].calculate_path
                @a_star_paths[0].generate_path(false)
                @a_star_paths[0].collision_counter = counter + 1
                force_move_route(@a_star_paths[0].route) if @a_star_paths[0].found
                # Wait a bit before starting to follow the new path
                @wait_count = A_Star_Pathfinder::COLLISION_WAIT
                return
              elsif @a_star_paths[0]
                # If collision wait is -1 or reached collision limit,
                # stop character and call any fail method
                @move_route_index = @move_route.list.size
                if @a_star_paths[0].fail_method 
                  @a_star_paths[0].fail_method.call
                end
                follow_next_path 
              end
    ### ADDED CODE ENDS ############################################################
              return
            end
            # Advance index
            @move_route_index += 1
            return
          end # if command.code <= 14
          # If waiting
          if command.code == 15
            # Set wait count (from provided parameter)
            @wait_count = command.parameters[0] * 2 - 1
            @move_route_index += 1
            return
          end # if command.code == 15
          # If direction change (turning) command
          if command.code >= 16 and command.code <= 26
            # Branch by command code
            case command.code
            when 16  # Turn down
              turn_down
            when 17  # Turn left
              turn_left
            when 18  # Turn right
              turn_right
            when 19  # Turn up
              turn_up
            when 20  # Turn 90° right
              turn_right_90
            when 21  # Turn 90° left
              turn_left_90
            when 22  # Turn 180°
              turn_180
            when 23  # Turn 90° right or left
              turn_right_or_left_90
            when 24  # Turn at Random
              turn_random
            when 25  # Turn toward player
              turn_toward_player
            when 26  # Turn away from player
              turn_away_from_player
            end
            @move_route_index += 1
            return
          end # if command.code >= 16 and command.code <= 26
          # If other command (commands that don't 'return')
          if command.code >= 27
            # Branch by command code
            case command.code
            when 27  # Switch ON
              $game_switches[command.parameters[0]] = true
              $game_map.need_refresh = true
            when 28  # Switch OFF
              $game_switches[command.parameters[0]] = false
              $game_map.need_refresh = true
            when 29  # Change speed
              @move_speed = command.parameters[0]
            when 30  # Change freq
              @move_frequency = command.parameters[0]
            when 31  # Move animation ON
              @walk_anime = true
            when 32  # Move animation OFF
              @walk_anime = false
            when 33  # Stop animation ON
              @step_anime = true
            when 34  # Stop animation OFF
              @step_anime = false
            when 35  # Direction fix ON
              @direction_fix = true
            when 36  # Direction fix OFF
              @direction_fix = false
            when 37  # Through ON
              @through = true
            when 38  # Through OFF
              @through = false
            when 39  # Always on top ON
              @always_on_top = true
            when 40  # Always on top OFF
              @always_on_top = false
            when 41  # Change Graphic
              # Can't change into a tile
              @tile_id = 0
              @character_name = command.parameters[0]
              @character_hue = command.parameters[1]
              # Update direction
              if @original_direction != command.parameters[2]
                @direction = command.parameters[2]
                @original_direction = @direction
                @prelock_direction = 0
              end
              # Update frame
              if @original_pattern != command.parameters[3]
                @pattern = command.parameters[3]
                @original_pattern = @pattern
              end
            when 42  # Change Opacity
              @opacity = command.parameters[0]
            when 43  # Change Blending
              @blend_type = command.parameters[0]
            when 44  # Play SE
              $game_system.se_play(command.parameters[0])
            when 45  # Script
              result = eval(command.parameters[0])
            end
            # Update move_route_index and move to next move command in the list
            @move_route_index += 1
          end # if command.code >= 27
        end # while @move_route_index < @move_route.list.size
      end 
    
    end

    Далее будут исключительно действия с короткими пояснениями для простой работы со скриптом. Все лишнее убрано.

    2) Ведем игрока или событие к точке на карте:
    В событии управляющем движением персонажа (например автостарт), вставляем команду "Script..." (последняя кнопка на последней закладке) со следующим содержимым:
    Код:
    $path = A_Star_Pathfinder.new(Node.new(19, 14), -1)
    19 - координата X конечной точки
    14 - координата Y конечной точки
    -1 - двигаем игрока. 1, 2, 3, ..., 999 - двигаем событие на карте.
    Примечание: Игрок или событие будет искать кратчайший путь до конечной точки, уклоняясь от встречных событий. Но есть несколько "но": Если скрипт решит, что пройти к точке невозможно или наберет определенное количество столкновений движений остановится. Порой - даже не начавшись. Ввиду экзотичности алгоритма поиска пути не рекомендуется создавать длинные маршруты, например путешествие из одного конца карты 500х500 в другой - на пустой карте расчет длится порядка 1.2с. На загруженной - соответственно дольше. Передвижение событий в условии большого количества других событий весьма проблематично - скрипт довольно часто прекращает свою работу или отказывается ее начинать, считая, что событие зашло в тупик. С игроком чуть лучше, однако будьте осторожны с узкими проходами, по которым движутся события.

    3) Преследуем игрока или событие
    В событии управляющем движением персонажа или события (параллельное или автостарт), вставляем команду "Script..." (последняя кнопка на последней закладке) со следующим содержимым:
    Код:
    ch = 3
    tg = A_Star_Pathfinder.int_to_character(-1)
    $path = A_Star_Pathfinder.new
    $path.setup(Node.new(tg.x, tg.y),ch, 2)
    $path.character.stop_all_paths  
    $path.calculate_path
    $path.generate_path
    -1 - указатель на преследуемого: -1 для игрока, 1, 2, 3, ..., 999 - для событий на карте
    3 - указатель на преследователя: -1 для игрока, 1, 2, 3, ..., 999 - для событий на карте
    2 - расстояние на котором преследователь будет бегать за преследуемым (минимум: 1)
    Примечание: Переделал изначальный скрипт - теперь преследователями могут быть не только события, но и сам игрок. За последствия не ручаюсь. Кроме того очень интересный эффект достигается при увеличении дистанции следования на сложных картах. Только учтите - скрипт не отодвигает преследующего на заданное расстояние, только пододвигает. Так что можно ненароком запереть себя в углу - будьте осторожны!

    3, 4: Подойти поближе и запустить скрипт по достижении конечной точки:
    Там нет ничего сложного, можете и сами глянуть в скрипты для примера. Описывать их сейчас нет времени, но там, по уму, нужно поправить основной скрипт, что бы не нужно было писать кучу лишнего кода.
    Последний раз редактировалось Equilibrium Keeper; 24.08.2009 в 18:01.

  3. #3
    Хранитель Форума Аватар для Валера
    Информация о пользователе
    Регистрация
    15.04.2008
    Адрес
    Москва
    Сообщений
    14,006
    Записей в дневнике
    3
    Репутация: 166 Добавить или отнять репутацию

    По умолчанию

    Можно ли заставить ивент пройти заданное количество клеток до цели и остановиться ожидая новых указаний?
    В обычном режиме: как культурно прервать перемещение ивента? Иными словами - остановить скрипт.

    Спойлер И как тебе не стыдно-то, а, Валера?:


    Оборона форта: http://rghost.ru/8kLGxFtD2
    Сделать, чтоб все происходило, как я хочу, - вот, собственно, и весь мейкер!
    Адский Рейд: http://rpgmaker.su/vbdownloads.php?d...downloadid=106

  4. #4

    По умолчанию

    "После некоторого количества клеток" придется либо править скрипт, либо считать самому. А что касается обрыва работы, опять же вставляешь скрипт:
    Код:
    $path.character.stop_all_paths

  5. #5

    По умолчанию

    Посмотрел, весьма интересный скрипт.
    Ещё интересней будет соединить его со скриптом управления мышью) хотя наверняка такое уже где-то сделали.

  6. #6

    По умолчанию

    Можно ссылку на скрипт?
    Только не очень понимаю - если уже есть скрипт управления мышью, то зачем прикручиать еще и этот? Или тот годится только для блуждания по менюшкам? Ну, тогда их можно довольно просто совместить (придется только пересчитать пиксели в клетки, но зная формулы из game_map и tilemap - это не проблема.

  7. #7

    По умолчанию

    Управление мышкой:
    http://www.amaranthia.com/downloads/Mouse-System.zip
    наиболее грамотный из тех что мне попадались
    там и управление менюшками и своя система нахождения пути, но до первой стенки) т.е. он идёт в лоб

    там в методе mouse_press? отдельно проверяются координаты события (т.е. клетки где стоит) и просто путь, но уже в своих координатах. в общем формулы для события:
    Код:
          # get the icon to display
          if @mouse_status[id][0] <= 0
            $hoverx = (pos[0] + $game_map.display_x / 4) / 32
            $hovery = (pos[1] + $game_map.display_y / 4) / 32
            icon = check_event($hoverx,$hovery)
          end
    это и есть определение клетки. Заменяем это
    Код:
          # move character when mouse is pressed
          if @mouse_status[id][0] > 0
            $mousex = pos[0] + $game_map.display_x / 4
            $mousey = pos[1] + $game_map.display_y / 4
            $move = 1
          end
    например так:
    это и будет просто определение клетки.
    Код:
          # move character when mouse is pressed
          if @mouse_status[id][0] > 0
            $destx = (pos[0] + $game_map.display_x / 4) / 32
            $desty = (pos[1] + $game_map.display_y / 4) / 32
            $move = 1
          end
    Потом сажаем вызов патчфайдинга в метод ниже
    Код:
      def update
    в
    Код:
    if $move == 1
    убрав всё что там в этом условии и написав, допустим
    Код:
    e = Node.new($destx, $desty)
    $path = A_Star_Pathfinder.new(e)
    Как-то так. Может что забыл.
    В принципе я похимичил и всё получилось, но я делал это всё внутри ещё одного проекта с SBABS, у меня герой не хотел идти дальше 2-3 клеток, возможно мешались события сопартийцев. Надо попробовать в чистом проекте.

    upd: в чистом тоже самое, персонаж просто встаёт и всё, не реагирует. что-то не так.

  8. #8

    По умолчанию

    Сдается мне, что проблема в $destx = (pos[0] + $game_map.display_x / 4) / 32.
    Дело все в том, что персонаж можешь находиться и не по центру экрана, а скраю - если действие происходит в углу карты. Или я ошибаюсь? Или там это вообще отменяется?
    Кроме того, если пихать патч в апдейт, следует еще и очищать уже существующее действие, по принципу скрипта следования.
    Ну и наконец не
    Код:
    e = Node.new($destx, $desty)
    $path = A_Star_Pathfinder.new(e)
    а
    e = Node.new($destx, $desty)
    $path = A_Star_Pathfinder.new(e, -1)
    Ибо иначе кто будет ходить? о.О

  9. #9

    По умолчанию

    Ибо иначе кто будет ходить? о.О
    ну там по-умолчанию уже прописано -1, можно не указывать:

    def initialize(goal_node = nil, char = -1, run = true)

    подробнее пока не разбирался, разгадка где-то близко )

  10. #10
    Хранитель Форума Аватар для Валера
    Информация о пользователе
    Регистрация
    15.04.2008
    Адрес
    Москва
    Сообщений
    14,006
    Записей в дневнике
    3
    Репутация: 166 Добавить или отнять репутацию

    По умолчанию

    Наконец две умные головы сошлись вместе! Почаще вас надо сводить.

    Спойлер И как тебе не стыдно-то, а, Валера?:


    Оборона форта: http://rghost.ru/8kLGxFtD2
    Сделать, чтоб все происходило, как я хочу, - вот, собственно, и весь мейкер!
    Адский Рейд: http://rpgmaker.su/vbdownloads.php?d...downloadid=106

Страница 1 из 3 123 ПоследняяПоследняя

Информация о теме

Пользователи, просматривающие эту тему

Эту тему просматривают: 1 (пользователей: 0 , гостей: 1)

Социальные закладки

Социальные закладки

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •  
Скрипт нахождения пути