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.

Scripter's corner : Snippets, Classes, Tips and more

e

Sponsor

Scripter's Corner

There seems to be a growing amount of capable scripters around the forums, people who pour their heart and souls into well concocted delights of logic and arithmetic in the hopes to further the cause of video game designers and creators in general; yet, these people, this community, has no real way to share information between themselves. Workarounds, hacks, snippets, bug history, jokes, etc; we're slaves to the system, bound to our Hollywood (Submitted RGSS/2 Scripts forums) and our technical help center (RGSS Help forum), forced, like Sisyphus, to labor again and again, reinventing the wheel day after day, toiling under the unflinching stare of our monitors to fulfill other's needs (no, not like that you pervert!).

Well, I say, no more! This fancy introduction was my attempt at making you (un)comfortable, and hopefully I have (not)succeeded. So, after all this talk, I can already hear you yelling out into the cold, lonely night : "But what is the Scripter's Corner, etheon?".

It is, as I've said in a rather long and bizarre series of incoherent and overly descriptive sentences, a place where fellow scripters (coders, programmers, rubyists, Priests of the Why, etc.) can gather 'round and share their experiences. And by that, I mean anything, literally anything that could prove useful to others: your most recent discoveries, in-depth explanations of how certain classes work, tips on original and useful ways to use built-in classes and modules, code snippets, your own classes and modules which, although not scripts in themselves, can be useful to others. That, and much, much more. Found a bug, and then a work around? Post it! Made a small library that handles common physics, such as gravity, acceleration and force? Post it! Found a quick, simple way to determine whether or not there are any events currently running? By all means, post it!

Hopefully, by now, you have a fair idea of where I'm heading with this. This is a global effort, a community effort, which will persist only if everyone chips in a little of their work. I understand some of you might feel that your hard earned work is not to be shared, because it would be stolen, and I respect that; however, I believe that sharing information amongst ourselves can only bolster the global quality of the submitted scripts and, in the long run, can be nothing short of beneficial for the community.

Now, there are no real requirements. I'll be starting off the dance, and I hope more of you will join me. I'm looking at you, Yautja! :shades:

Note : When posting your libraries/classes/modules or snippets, please make sure to share with us not only the code, but how it can/should be used and such; programmers will only use something if it is useful (though non-useful stuff can still be interesting).

I'll start off with a little class I've been working on today : my own implementation of the simple CircularList.

Class : CircularList
A circular list is a data structure which is part of the List family. More importantly, lists are a type of collections, very much like arrays. For more on lists, please see Wikipedia.

Circular lists are, as I've said, data structures; in other words, they're useful collections to handle a variable amount of related data, such as strings, integers or objects. Since in Ruby everything is an Object, this matters little to us. The difference between circular lists and lists in general is that a circular list, although it has a beginning, has no real end; it wraps around and comes back at the beginning. Now, why would we need such a thing, you wonder; I needed it, for instance, to create a generic polygon class that would handle most transformations and calculations. As such, I stored the summits' coordinates (x,y) into a collection, and I needed it to wrap on itself (so that the last summit would be connected to the first).

Long story short, here's the code. Not very well commented, I'll grant you, but eh, sue me.

Code:
class CircularList

  #-----------------------------------------------------------------------

  # Private Class : Node

  #-----------------------------------------------------------------------

  class Node

    #-----------------------------------------------------------------------

    # Public Instance Variables

    #-----------------------------------------------------------------------

    attr_accessor :forward, :back, :object

    #-----------------------------------------------------------------------

    # * Initialize

    #     list : a reference to the list it is part of

    #     object : the data associated with the node

    #     forward : the next node in the list

    #     back : the previous node in the list

    #-----------------------------------------------------------------------

    def initialize(list, object, forward = nil, back = nil)

      @list = list

      @object = object

      @forward = forward

      @back = back

    end

    #-----------------------------------------------------------------------

    # * Insert

    #     object : the object to insert

    #     node  : the node after/before which to insert the object

    #     before : if TRUE, insert before, if FALSE, insert after; def : false

    #-----------------------------------------------------------------------

    def insert(node, before = false)

      # Insert before

      if before

        node.forward = self

        self.back, node.back = node, self.back

        node.back.forward = node

      else

      # Insert after

        node.back = self

        self.forward, node.forward = node, self.forward

        node.forward.back = node

      end

    end

    #-----------------------------------------------------------------------

    # * Dispose

    #     Disposes of self. Removes all references to self.

    #-----------------------------------------------------------------------

    def dispose

      @back.forward, @forward.back = @forward, @back

    end

    #-----------------------------------------------------------------------

    # * To S

    #     String representation of our node element.

    #-----------------------------------------------------------------------

    def to_s

      return @object.to_s if @object.respond_to? :to_s

    end

    #-----------------------------------------------------------------------

    # * ===

    #     Tests for object_id equality

    #-----------------------------------------------------------------------

    def ===(node)

      return (self.object_id == node.object_id)

    end

  end

  #-----------------------------------------------------------------------

  # Public Instance Variables

  #-----------------------------------------------------------------------

  attr_reader :start

  #-----------------------------------------------------------------------

  # * Initialize

  #     start : the first node

  #-----------------------------------------------------------------------

  def initialize(start = nil)

    @start = (start.nil?) ? nil : push(start)

  end

  #-----------------------------------------------------------------------

  # * Clear

  #     Clears the entire list and disposes of every node.

  #-----------------------------------------------------------------------

  def clear

    @start = nil

  end

  #-----------------------------------------------------------------------

  # * Empty?

  #     Returns TRUE if the list is empty, FALSE otherwise.

  #-----------------------------------------------------------------------

  def empty?

    return @start.nil? # Theoretically, if there is no head, there is no body.

  end

  #-----------------------------------------------------------------------

  # * First

  #     Returns a reference to the first element of the list.

  #-----------------------------------------------------------------------

  def first

    raise RuntimeError, "Current list is empty" if self.empty?

    return @start.object

  end

  #-----------------------------------------------------------------------

  # * Last

  #     Returns the last object in the circular list.

  #-----------------------------------------------------------------------

  def last

    raise RuntimeError, "Current list is empty" if self.empty?

    return @start.back.object

  end

  #-----------------------------------------------------------------------

  # * Push

  #     Pushes a new object on top of the list.

  #-----------------------------------------------------------------------

  def push(object)

    node = insert(object, @start, true) # Insert BEFORE our head

    

    # Set the new node as the start

    @start = node unless node.nil?

    return node

  end

  #-----------------------------------------------------------------------

  # * Append

  #     Appends an object at the end of the list.

  #-----------------------------------------------------------------------

  def append(object)

    node = (@start.nil?) ? @start : @start.back

    return insert(object, node)  # Insert after our head's prior node

  end

  #-----------------------------------------------------------------------

  # * Insert

  #     object : the object to insert

  #     node  : the node after/before which to insert the object

  #     before : if TRUE, insert before, if FALSE, insert after; def : false

  #-----------------------------------------------------------------------

  def insert(object, node, before = false)

    o = Node.new(self, object)

    

    # If the start is already placed, insert.

    unless @start.nil?

      node.insert(o, before)

    else

      # Since there is no start, clearly we must be set as such.

      @start = o

      

      # Link around to ourself!

      o.back = o

      o.forward = o

    end

    

    return o

  end

  #-----------------------------------------------------------------------

  # * Pretty Print

  #     Prints...prettily?

  #-----------------------------------------------------------------------

  def pretty_print

    retval = ""

    node = @start

    

    self.each { |node| retval << "#{ node }\n" }

      

    print(retval)

  end

  #-----------------------------------------------------------------------

  # * Each

  #     Iterate through each node.

  #-----------------------------------------------------------------------

  def each(&block)

    node = @start

    begin

      yield node

      node = node.forward

    end until node == @start

  end

  #-----------------------------------------------------------------------

  # * Each_Value

  #     Iterate through each objects, not node.

  #-----------------------------------------------------------------------

  def each_value(&block)

    node = @start

    begin

      yield node.object unless node.nil?

      node = node.forward

    end until node == @start

  end

  #-----------------------------------------------------------------------

  # * Extract

  #     object : the value (object) which is contained within a node

  #

  #     Finds the node whose object value is given.

  #     Returns the first ocurrance of said object.

  #-----------------------------------------------------------------------

  def extract(object)

    self.each { |node| return node if node.object == object }

 

    return nil

  end

  #-----------------------------------------------------------------------

  # * Delete

  #     Removes the node whose value is the object given.

  #-----------------------------------------------------------------------

  def delete(object)

    self.each do |node|

      if node.object == object

        # Is this the start?

        if node == @start

          @start = (node == node.forward) ? nil : node.forward

        end

        

        # The union won't get you out of this one!

        node.dispose

      end

    end

  end

  #-----------------------------------------------------------------------

  # * Length, alias size

  #     Returns the number of nodes in the list.

  #-----------------------------------------------------------------------

  def length

    retval = 0

    

    self.each { |node| retval += 1 }

    

    return retval

  end

  alias_method :size, :length

end

Usage : The class itself is fairly easy to use. I tried to keep a set of methods which are common to all collections, such as the .each, .length, .size and .delete methods. Usually, most method comments are self-explanatory, but in some case they might not be. The each method will work in an array like manner; it will not wrap around. That would've been an endless loop.

Instantiation of a list is done with the following :

Code:
list = CircularList.new([object])

The [object] part can be replaced by any type of object; it will be encapsulated within a Node class, which is an internal class of the CircularList class (it shouldn't be used elsewhere; at list, not until someone makes a List module). My node class goes both way; you can go forward or backward, as you please. Just don't use it directly; instantiation requires a reference to its list.

Method : events_running?
Someone asked the following question in the RGSS Help forums : "How do you check if there are any events currently running?"

I was quite puzzled by this, and after some research, I found a very, very simple solution, which was buried beneath layers of (weird) code.

Code:
$game_system.map_interpreter.running?

It will return true if there are any events currently running; a better version, although with a slight overhead, would be :

Code:
def events_running?

   return $game_system.map_interpreter.running?

end

Seems useless, but it would allow a scripter to add code before or after the following line, which might eventually be useful. Always think flexibility, kids!

Any questions, I'll be glad to help. I have a couple more classes and modules to share; for instance, I'm working on a basic Geometry module which will allow of some of the most common 2D shapes manipulations, including ellipses, (ir)regular polygons, circles, etc.
 

khmp

Sponsor

Good job starting a topic like this etheon! My bit of contribution is actually a request someone asked of me once. I hope he doesn't mind me posting it here. For those that read the first four letters of the alias_method, loip is not the requester. It stands for Lorem Ipsum, filler text, not a user. Coincidentally there is also a user by that name, again nothing to do with him/her.

Class : Window Base
Its an extension of Window_Base. You ever see those screens with the Windows that slide onto the screen shifting between translucent to opaque. That's exactly what this can accomplish. Small little features like these are just eye candy but can add a nice touch to a project.

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

class Window_Base < Window
  #--------------------------------------------------------------------------
  # * Alias Methods
  #--------------------------------------------------------------------------
  alias_method :loip_windowslide_window_base_initialize, :initialize
  alias_method :loip_windowslide_window_base_update, :update
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     x      : window x-coordinate
  #     y      : window y-coordinate
  #     width  : window width
  #     height : window height
  #--------------------------------------------------------------------------
  def initialize(x, y, width, height)
    # Call the old initialize code.
    loip_windowslide_window_base_initialize(x, y, width, height)
    #------------------------------------------------------------------------
    # @sliding
    #   - the boolean that tells update to slide.
    #------------------------------------------------------------------------
    @sliding = false
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    # Call the old update code.
    loip_windowslide_window_base_update
    # If the user has called slide_to at least once. Let it slide.
    if @sliding
      # If the window is active continue moving it to the slide location.
      if self.active && (((self.x - @tx).abs > @xtol) || 
                        ((self.y - @ty).abs > @ytol))
        # Increment position by frame based velocity.
        self.x += @xvel
        self.y += @yvel
        self.opacity += @delta_opacity
      # If the window isn't active continue moving it to the original location.
      elsif !self.active && (((self.x - @ox).abs > @xtol) || 
                            ((self.y - @oy).abs > @ytol))
        # Decrement position by frame based velocity.
        self.x -= @xvel
        self.y -= @yvel
        self.opacity -= @delta_opacity
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Slide To Location - When Active
  #     x             : window x-coordinate destination.
  #     y             : window y-coordinate destination.
  #     frames        : amount of frames it takes to reach destination.
  #     final_opacity : the final opacity when window gets to destination.
  #--------------------------------------------------------------------------
  def slide_to(x, y, frames, final_opacity = 255)
    #------------------------------------------------------------------------
    # @tx
    #   - the location on the x axis where you want the window to end up.
    # @ty
    #   - the location on the y axis where you want the window to end up.
    # @ox
    #   - the location on the x axis where the window starts.
    # @oy
    #   - the location on the y axis where the window starts.
    # @delta_opacity
    #   - the change in opacity from start to finish. (*based on frames)
    # @xvel
    #   - the x speed it uses to reach start to finish. (*based on frames)
    # @yvel
    #   - the y speed it uses to reach start to finish. (*based on frames)
    # @xtol
    #   - the tolerance of x units from start to finish. (*based on frames)
    # @ytol
    #   - the tolerance of y units from start to finish. (*based on frames)
    #------------------------------------------------------------------------
    # Just enable the boolean so the update doesn't skip sliding.
    @sliding = true
    # Save the values.
    @tx = x
    @ty = y
    @ox = self.x
    @oy = self.y
    @delta_opacity = (final_opacity - self.opacity) / frames
    # Figure out the velocity vector to slide location from original position.
    @xvel = (@tx - @ox) / frames
    @yvel = (@ty - @oy) / frames
    # The tolerance is meant to determine how much space is left over if the
    # number does not divide evenly.
    @xtol = (@tx - @ox) % frames
    @ytol = (@ty - @oy) % frames
  end
end

Usage : Sliding does not occur if you don't want it to so no worries if you install this script. The most important addition is calling the method slide_to. It requests four parameters. First is the x coordinate, that when the window is active will attempt to get to. Next is the y coordinate where the window will attempt to get to. Then the number of frames you would like it to take to reach start to finish and vice versa. Last is the opacity of the window when it reaches its final destination. Lastly and most importantly the active boolean of the window dictates when and where its moving. When it reaches a location it will not move until the active flag changes.

Sorry for stealing your post's formatting style etheon. Trying to keep it consistent.
 

e

Sponsor

Pretty neat. You know, games weren't really made for the usability people; every gamer wants eye candy, and (s)he wants it now! Well, you do have the weird ASCII gamers, but...eh, what can I say? They're ASCII addicts. ASCII art? Pfah! ;)

Anyway, I haven't quite gotten around to making animations and the likes; I've actually never tinkered much with "smooth" animation, in any language, and I'm not too sure about where to begin. Making a huge number of a graphical updates on every tick always seems so inefficient to me, but I suppose it's not. You know, I reckon that, if you know your way around the various graphical classes, perhaps you could share a little tips on animating objects with RGSS...efficiently? I know I'd appreciate!

Also, I'm not sure I understand what your "tolerance" variables are. Care to explain how they're used, and why?
 

khmp

Sponsor

Tolerance. Because I wanted to avoid computing the distance between where it was and where it should be I created tolerance. This variable lets me know how much is left over. If I'm close enough to tolerance stop moving. There might be a noticeable gap between where you actually want it to go and where it is on the screen.

Animation. As a school assignment using a C++ win32 console project we had to make an animation class which contained a dynamic array of frames which were also classes. And we only had to prove that it could idle and perform three different actions. I wonder if I still have it. I made a cat that could puke a hairball, scratch the curtains, and die. The first two went back to the idle animation after 2 seconds. The third... well it exited. That is the only experience I have in creating animation more of state machine but animation to me. No textures or 3D models though only a ":3" cout. :lol:
 

e

Sponsor

Seems cool, and I like how it uses the generic distance between points function. However, I'd point out a few things :

1. Using a switch/case for solely two cases is pretty useless. You'd be much better of with an if/else, especially since Ruby does not differentiate between cases and ifs. Once interpreted, it's exactly the same byte code. Hence, using case is useful only with a larger amount of cases that would require more than one elsif.

2. Parameter names. "param1" or "param2" doesn't mean much to me; it confuses me more than anything, and I'll have a harder time trying to remember the syntax of your function. You should, instead, give them names such as "method_flag", "round_flag", "character", etc.

3. Remove the useless param4. Instead, if the method is 2, assume that param3 will be either an array such as [ x, y ] or an hash such as { :x => #, :y => # }. That's not really needed, but it's so when you use method 1, you don't have to do : get_distance(1, -1, 1, nil, 1). See the useless nil? Eh.

4. Why is param5 an integer? Shouldn't it be a boolean? That way, you could replace :

Code:
if param5 == 1
  d = d.round
end

By more elegant and rubyesque :

Code:
d.round if param5

Those are all suggestions; I simply think it'd improve the readability. All in all though, pretty useful method!
 

e

Sponsor

Eh. Ruby is wonderful for clean, crisp code. I'll admit, though, learning Rubyisms (as they're called) and their conventions takes some practice (and time...lots of time).  :thumb:
 
Is there anyone who could give me tips on how to script Rpg maker xp into an over the head shooter?

Ven: This is not the place for that stuff, bub.
 

e

Sponsor

That wouldn't be here; you should probably ask this in the RGSS Help forum. This topic is for scripters who want to share ideas and code, and not for requesting help.
 

Zeriab

Sponsor

Great idea etheon ^_^

I have looked at your circular list and in general it looks nice ^^
Notes on your circular list:
Why do you say the Node class is private when it is public?
The comments for the insert methods in the Node class talks about an object argument that is not present in the actual method.
You should include the Enumerable class in the CircularList class, especially since the class you already have written an each method. A simple include Enumerable and you get loads of sweet functions for free: http://www.ruby-doc.org/docs/Programmin ... rable.html

Here are a few contributions:
Yes it is somewhat similar to the Circular list, but there are differences.
This is a script I have submitted for MACL and the code bears the marks of it.
It is a double linked list data structure. I chose double linked over single linked because there are only very few cases where single linked lists performs significantly better than double linked lists, while there are many cases where double linked lists perform significantly better than single linked lists.
I have chosen to hide the inner structure which means the list is best suited for stacks and/or queues.
It does mean that you can't insert an element before another element in constant time unless it's at the head or tail since you can't retrieve the elements contained in the list. (Unless you use instance_eval)
In fact, there is no method in the API for inserting a value at an arbitrary index.
The constrains also means that you can be more sure the integrity in the list is preserved.

Use the linked list if you need a big queue or stack with many add and remove actions.
Code:
#==============================================================================
# ** Linked_List (v1.0)                                       By Zeriab
#------------------------------------------------------------------------------
# Represents a doubled linked list.
# Has a reference to the tail and the head of the list
#==============================================================================

class Linked_List
 #==========================================================================
 # * PUBLIC VISIBILITY
 #==========================================================================
 attr_reader :size
 
 # A Linked List can be considered as a enumerable collection
 # The head is considered as the first element and the tail the last
 include Enumerable
 
 #--------------------------------------------------------------------------
 # * Name      : Object Initialization
 #   Info      : Initializes the list by clearing it
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #--------------------------------------------------------------------------
 def initialize
   # Clears the list
   clear
 end
 #--------------------------------------------------------------------------
 # * Name      : Concatenation
 #   Info      : Returns a new array list by concatenating the two lists
 #               together to produce a third list.
 #   Author    : Zeriab
 #   Call Info : One Argument - The list to concatenate with.
 #   Comments  : Slow compared to the dangerous concatenation (.concat!)
 #--------------------------------------------------------------------------
 def +(list)
   return self.dup.concat!(list.dup)
 end
 #--------------------------------------------------------------------------
 # * Name      : Equality
 #   Info      : Tells whether the lists are equal or not
 #   Author    : Zeriab
 #   Call Info : One Argument - The list to concatenate with.
 #   Comments  : Two lists are equal if their heads and tails are equal on
 #               the Object level
 #--------------------------------------------------------------------------
 def ==(list)
   return self.head == list.head && self.tail == list.tail
 end
 #--------------------------------------------------------------------------
 # * Name      : Set nth element
 #   Info      : Sets the value of the nth element in the list
 #   Author    : Zeriab
 #   Call Info : Two Arguments - Numeric n for specifying which element
 #               Object value to replace the value of the nth element
 #   Comments  : Returns true if the change succeeds otherwise false
 #--------------------------------------------------------------------------
 def []=(n,value)
   element = get_element(n)
   # If nil is returned from get_element the element was not found
   if element.nil?
     return false
   else
     # Changes the value of the element
     element.value = value
     return true
   end
 end
 #--------------------------------------------------------------------------
 # * Name      : Add Value
 #   Synonyms  : enqueue(value), insert(value), push(value), <<(value)
 #   Info      : Adds a value to the list as the new head
 #   Author    : Zeriab
 #   Call Info : One Argument - Object value to be added
 #   Comments  : Returns the modified list.
 #               Values in the list does not have to be distinct
 #--------------------------------------------------------------------------
 def add(value)
   element = Linked_List_Element.new
   element.value = value
   insert_element(element)
   self.size += 1
   return self
 end
 # Synonyms
 def enqueue(value) add(value) end
 def insert(value) add(value) end
 def push(value) add(value) end
 def <<(value) add(value) end
   
 #--------------------------------------------------------------------------
 # * Name      : Clear
 #   Info      : Clears the list (no elements present afterwards
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #--------------------------------------------------------------------------
 def clear
   self.size = 0
   self.tail = nil
   self.head = nil
 end
 #--------------------------------------------------------------------------
 # * Name      : Concatenation!
 #   Info      : Adds the given list to the tail
 #   Author    : Zeriab
 #   Call Info : One Argument - The list to concatenate with.
 #   Comments  : Alters the given list as well.
 #--------------------------------------------------------------------------
 def concat!(list)
   if list.size <= 0
     # The given list is empty
     return self
   elsif self.size <= 0
     # This list is empty
     self.tail = list.tail
     self.head = list.head
     self.size = list.size
     return self
   end
   # Neither list are empty
   self.tail.next_element = list.head
   list.head.previous_element = self.tail
   self.size += list.size
   self.tail = list.tail
   return self
 end
 #--------------------------------------------------------------------------
 # * Name      : Duplication
 #   Info      : Duplicates the list
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #   Comments  : Duplicates the list by creating a new instance of the same
 #               class and adding the values in the same order as they are
 #               in this list
 #--------------------------------------------------------------------------
 def dup
   list = self.class.new
   self.each {|value| list.unshift(value)}
   return list
 end
 #--------------------------------------------------------------------------
 # * Name      : Clone
 #   Info      : Clones the list
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #   Comments  : New list elements are created. The values are not cloned
 #--------------------------------------------------------------------------
 def clone
   list = super
   list.clear
   self.each {|value| list.unshift(value)}
   return list
 end
 #--------------------------------------------------------------------------
 # * Name      : Delete
 #   Info      : Deletes the first occurrence of a value from the list
 #   Author    : Zeriab
 #   Call Info : One Argument - The value to delete.
 #   Comments  : Complexity of O(n), where n is the number of elements in the
 #               list, since a search is needed
 #               Returns nil if the value does not exist in the list
 #--------------------------------------------------------------------------
 def delete(value)
   # Searches for the first element containing the value
   element = self.search(value)
   if element.nil?
     # No element was found, so nil is returned
     return nil
   else
     # Deletes the element from the list and returns its value
     self.delete_element(element)
     self.size -= 1
     return element.value
   end
 end
 #--------------------------------------------------------------------------
 # * Name      : Each
 #   Info      : The each method required by the Enumerable module
 #   Author    : Zeriab
 #   Call Info : Refer to the documentation on the Enumerable module
 #--------------------------------------------------------------------------
 def each
   # Return if there are no elements in the list
   return if self.head.nil?
   # Yield the first value of the list (The head)
   element = self.head
   yield element.value
   # Run through the list until the tail is fount or the next element of the
   # list is nil
   while element != self.tail && element.next_element != nil
     # Retrieve and yield the next element in the list
     element = element.next_element
     yield element.value
   end
 end
 #--------------------------------------------------------------------------
 # * Name      : Empty?
 #   Info      : Checks if the list is empty.
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #   Comments  : A list is empty if it has size 0
 #--------------------------------------------------------------------------
 def empty?
   return self.size <= 0
 end
 #--------------------------------------------------------------------------
 # * Name      : First
 #   Synonyms  : peek
 #   Info      : Retrieves the first value of the list (head)
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #   Comments  : Returns nil if the list is empty
 #--------------------------------------------------------------------------
 def first
   return nil if self.empty?
   return self.head.value
 end
 # Synonyms
 def peek() return first end
 
 #--------------------------------------------------------------------------
 # * Name      : Get
 #   Synonyms  : [](n) - Like with an array
 #   Info      : Gets the nth element in the list (0 = first value)
 #   Author    : Zeriab
 #   Call Info : One Argument - The value to delete.
 #   Comments  : Returns nil if the list does not have enough elements
 #--------------------------------------------------------------------------
 def get(n)
   element = get_element(n)
   # Return nil if there is not nth element in the list
   if element.nil?
     return nil
   end
   return element.value
 end
 # Synonyms
 def [](n) get(n) end
 
 #--------------------------------------------------------------------------
 # * Name      : Inspect
 #   Info      : Returns a string representation of the list
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #--------------------------------------------------------------------------
 def inspect
   return self.join(" => ")
 end
 #--------------------------------------------------------------------------
 # * Name      : Join
 #   Info      : Returns a string created by converting each value in the
 #               list to a string, separated by the given separator string.
 #   Author    : Zeriab
 #   Call Info : Zero to One Argument - The separator string, by default ""
 #--------------------------------------------------------------------------
 def join(separator = "")
   self.to_a.join(separator)
 end
 #--------------------------------------------------------------------------
 # * Name      : Last
 #   Info      : Retrieves the last value of the list (tail)
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #   Comments  : Returns nil if the list is empty
 #--------------------------------------------------------------------------
 def last
   return nil if self.empty?
   return self.tail.value
 end
 
 # Synonym for size
 def length() return size end
   
 #--------------------------------------------------------------------------
 # * Name      : Map!
 #   Synonyms  : collect!(&block)
 #   Info      : Invokes block once for the value each element list.
 #   Author    : Zeriab
 #   Call Info : One block - The block to call with each value
 #   Comments  : Replaces the elements values with the value returned
 #               by the block.
 #--------------------------------------------------------------------------
 def map!(&block)
   # Return if there are no elements in the list
   return if self.head.nil?
   element = self.head
   element.value = block.call element.value
   while element != self.tail && element.next_element != nil
     element = element.next_element
     element.value = block.call element.value
   end
   return self
 end
 # Synonym
 def collect!(&block) map!(&block) end
 
 #--------------------------------------------------------------------------
 # * Name      : Number of Items
 #   Info      : Finds the number of non-nil values in the list
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #--------------------------------------------------------------------------
 def nitems
   number = 0
   # Return if there are no elements in the list
   return number if self.head.nil?
   element = self.head
   number += 1  if !element.value.nil?
   while element != self.tail && element.next_element != nil
     element = element.next_element
     number += 1  if !element.value.nil?
   end
   return number
 end
 #--------------------------------------------------------------------------
 # * Name      : Pop
 #   Synonyms  : dequeue
 #   Info      : Deletes the head of the list and returns its value.
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #   Comments  : Returns nil if the list is empty
 #--------------------------------------------------------------------------
 def pop
   # Return nil if the list is empty
   if self.head.nil?
     return nil
   end
   self.size -= 1
   return delete_element(self.head).value
 end
 # Synonyms
 def dequeue() return pop end
 
 #--------------------------------------------------------------------------
 # * Name      : Replace
 #   Info      : Replaces the list to have the contents of the given list
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #--------------------------------------------------------------------------
 def replace(list)
   list_temp = list.clone
   self.head = list_temp.head
   self.tail = list_temp.tail
   self.size = list_temp.size
   return self
 end
 #--------------------------------------------------------------------------
 # * Name      : Shift
 #   Info      : Deletes the tail of the list and returns its value.
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #   Comments  : Returns nil if the list is empty
 #--------------------------------------------------------------------------
 def shift
   # Return nil if the list is empty
   if self.head.nil?
     return nil
   end
   self.size -= 1
   return delete_element(self.tail).value
 end
 #--------------------------------------------------------------------------
 # * Name      : To_s
 #   Info      : Returns list.join
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #--------------------------------------------------------------------------
 def to_s
   return join
 end
 #--------------------------------------------------------------------------
 # * Name      : Unshift
 #   Info      : Adds an object to the tail of the list
 #   Author    : Zeriab
 #   Call Info : No Arguments
 #--------------------------------------------------------------------------
 def unshift(value)
   element = Linked_List_Element.new
   element.value = value
   insert_element_tail(element)
   self.size += 1
 end
 
 #==========================================================================
 # * PROTECTED VISIBILITY
 #==========================================================================
 protected
 attr_accessor :head
 attr_accessor :tail
 
 ##
 # Sets the size of the list
 #
 def size=(value)
   @size = value if value >= 0
 end
 
 #==========================================================================
 # * PRIVATE VISIBILITY
 #==========================================================================
 private
 
 ##
 # Insert an element into the list.
 # Assumes 'element' is a Linked_List_Element.
 #
 def insert_element(element)
   if head.nil?
     self.head = element
     self.tail = element
     return
   end
   element.next_element = self.head
   self.head.previous_element = element
   self.head = element
   element.previous_element = nil
 end
 
 ##
 # Insert an element into the list at the tail.
 # Assumes 'element' is a Linked_List_Element.
 #
 def insert_element_tail(element)
   if head.nil?
     self.head = element
     self.tail = element
     return
   end
   
   element.previous_element = self.tail
   self.tail.next_element = element
   self.tail = element
   element.next_element = nil
 end
 
 ##
 # Delete the given element from the list
 # Assumes 'element' is a Linked_List_Element.
 #
 def delete_element(element)
   if element.next_element.nil?
     self.tail = element.previous_element
   else
     element.next_element.previous_element = element.previous_element
   end
   
   if element.previous_element.nil?
     self.head = element.next_element
   else
     element.previous_element.next_element = element.next_element
   end
   
   return element
 end
 
 ##
 # Search for an element with the specified value.
 # Return the first element found with the corresponding value
 # Return nil if no element is found.
 #
 def search(value)
   # If the head is nil the list is empty
   if self.head.nil?
     return nil
   end
   # Start with the head
   element = self.head
   loop do
     # Check if the element has the correct value
     if element.value == value
       return element
     end
     # Return nil if the tail has been reached
     if element == self.tail
       return nil
     end
     # Look at the next element in the list
     element = element.next_element
   end
 end
 
 ##
 # Get the object at the nth element in the list
 #
 def get_element(n)
   # Return nil if the list is empty (or n out of the range 0...size)
   if self.head.nil? || n < 0 || n > size
     return nil
   end
   # Searches for the nth element
   element = self.head
   for i in 0...n
     if self.tail == element
       # End of list reached without getting to the nth element
       return nil
     end
     element = element.next_element
   end
   # Returns the found element
   return element
 end
 
 ##
 # Represents an element in the linked list.
 #
 class Linked_List_Element
   attr_accessor :value
   attr_accessor :previous_element
   attr_accessor :next_element
 end
end

The next is something I have submitted as a script, but it really fits better to this thread than as a separate script.
Here's a litte addition to the module I have made ^^
Code":i29xge8f said:
Code:
class Module
  def attr_sec_accessor(sym, *args, &block)
    if block.nil? # default actions
      args = [0] if args.size == 0 #default = 0
      if args.size < 2 # One pair
        attr_writer sym
        attr_sec_reader sym, *args
      else # loads of methods followed by a default value.
        default = args[-1]
        syms = [sym].concat(args)
        syms.pop
        for sym in syms
          attr_writer sym
          attr_sec_reader sym, default
        end
      end
    else # when a block is given
      # currently just pair sematics
      args.unshift(sym)
      i = 0
      while i < args.size
        attr_writer args[i]
        attr_sec_reader args[i], args[i+1]
        i += 2
      end
    end
  end
  
  def attr_sec_reader(sym, default = 0)
    sym = sym.id2name
    string = "def #{sym};" +
             "  @#{sym} = #{default}  if @#{sym}.nil?;" +
             "  @#{sym};" +
             "end;"
    module_eval(string)
  end
end

Simple Version":i29xge8f said:
Code:
class Module
  def attr_sec_accessor(sym, default = 0)
    attr_writer sym
    attr_sec_reader sym, default
  end
  
  def attr_sec_reader(sym, default = 0)
    sym = sym.id2name
    string = "def #{sym};" +
             "  @#{sym} = #{default}  if @#{sym}.nil?;" +
             "  @#{sym};" +
             "end;"
    module_eval(string)
  end
end

You can use it like this:
Code:
attr_sec_accessor :method, default
It works pretty much like the attr_accessor except that you can only do method creation per call.
method is the method name.
default is the default value the method returns. (If it has not been written to yet.)

An usage example:
Code:
class Foo
  attr_sec_accessor :visible, false
  attr_sec_accessor :data, 'Data_Foo.new'
end

This way you'll have that the default variable of visible is false.
If it is called before you write to the variable you will get 'false' rather than 'nil'
One thing to note is that lazy instantiation is used.
Let us assume that Data_Foo is a heavy object. It is only created if you try to read what the data attribute contains before you have written to it. More specifically. If the value of data attribute is nil then it is change to be what the default value is.

The code in the example is converted to this:
Code:
class Foo
  def visible=(value)
    @visible = value
  end
  def visible
    @visible = false  if @visible.nil?
    @visible
  end
  
  def data=(value)
    @data = value
  end
  def data
    @data = Data_Foo.new  if @data.nil?
    @data
  end
end
One thing to notice is that you must put '' around Data_Foo.new to make it a string. Otherwise you will get an error in almost all cases.

The greatest use will probably be if you want to add information to hidden classes. Let's for an example take RPG::Actor.
You can use its initialize method to set the default value by default because it won't be called automatically.
Other than that it will hopefully make the coder shorter and easier to understand.

Naturally. Only use this functionality where it is needed or where it can have a positive use ^_^

*hugs*
~ Zeriab

I hope the best for this topic ^^
*hugs*
- Zeriab
 

e

Sponsor

That's pretty neat Zeriab. I hadn't really thought about using the Enumerable module as a mix-in for my CircularList; the only time I've created such a structure was in C, so I kind of just modified my existing logic to fit Ruby. Good idea though; I'll play around with it.

As for me specifying my Node class is private, believe me, I know it isn't. But, theoretically, the programmer shouldn't use it directly; instead, let the CircularList, or any List which uses the node, deal with it. It was more of a warning not to use it directly.

Oh, and as for the object parameter in the insert method, it must be a remnant of a previous version and I simply forgot to remove the comment :tongue:

Nice list, by the way. And I have to agree with you: double-linked list are usually a much better and safer choice their single-linked counterparts.
 

khmp

Sponsor

Another new addition to Scripter's Corner. Well its for anyone that can wield it really. I would have released it to Submitted RGSS/RGSS2 Scripts but its somewhat incomplete and I would rather much hear if any of you more knowledgeable types have any advice or notes for me on this subject matter.

History:
Probably the first month I was here I suggested to myself to make a script that would join maps together. I quote myself in saying it would take me two weeks. That was Septmember '07. I think jbrist asked me about it but I'm not sure if that's correct. Regardless I think after I thought about it for about 5 minutes total I dropped it. I had almost completely forgotten about it until a week ago when I found a text file containing the psuedo code. I then spent the past 4 days of this week working hard, and the last 2 trying to figure out why I had eventing bugs and various other crashes. Note that there is a big difference between:
Code:
save_data(Game_Map, filename)
and
Code:
save_data(RPG_Map, filename)

I know I'm not the sharpest tool in the shed but that took way too long to find. Once that was finished it clicked together and it's alpha is done. I say alpha only because it doesn't do start position events. It's hard coded that you start at the top left of the first piece. It also leaves trash lying around until next run time. Maps I mean. I should probably have more error/valid data checking. Events are renamed in most cases so if you have events that depend upon other names someone ensure they are only copied once.

class : Map_Joiner
What practical use would this serve? Well let's say you want to design a game like .hack//. In .hack// games, for those that don't know, maps are constructed via a keyword entry system. Different keywords generate different results. So rather than create a million and one maps. You would have pieces that could be assembled differently to obtain unique results during run time. This class does not task itself with how the map is assembled. It only assembles the pieces algorithm tells it to. It's up to the user of this script to determine how the algorithm method decides what pieces are placed where.

Zed Code:
Code:
#==============================================================================
# Script Name: Map Joiner
# Author: khmp
# Version: 0.0.1
# Date Last Modified: 3/14/2008
#==============================================================================

=begin
Psuedo-ASCII art per Sir. etheon's request.  
    / \
   /___\
  (/^@^\)
   \_-_/         <- Is it a face or a lawn gnome from behind?
┌────────────────────────────────────â”
 

e

Sponsor

Looks good. I can't really see when I'd use such a thing, although I suppose it could work for things like dungeon generators? I have no idea what algorithm I'd use. Perhaps some kind of weight/cost or classification of the "tiny" maps. Eh.

Oh, and you should raise a NotImplementedError in methods to override. Just to make sure the scripter gets it :thumb:
 

e

Sponsor

You'll excuse me for the double post, but I've redone my Circular list structure; it's now a class "Circular" within the "List" module, which also contains the "Element" class. It also includes the Enumerable module, and I think I've generally adapted it to Ruby in a much better way.

Module : List
Please review the first post of this topic for what exactly is a list.

The following is a module, called List, which contains the following classes : Element and Circular. Theoretically, one could create many types of Lists, not necessarily Circular, and embed them within this module to use the Element class; thus, you could quickly swap from one list type to the other.

Anyway, I think the code is well commented enough. Please remember that the Circular class also includes and works with ALL methods from the Enumerable module.

Code:
#==============================================================================
# ** List
#------------------------------------------------------------------------------
# The list modules contains all list-based data structures and related
# classes.
#==============================================================================
module List
  #============================================================================
  # ** Element
  #----------------------------------------------------------------------------
  # The element class is used to handle encapsulation of several data types
  # to facilitate their integration within any list data type.
  #============================================================================
  class Element
    #--------------------------------------------------------------------------
    # Public Instance variables
    # ============================
    # list    : the list containing our element
    # back    : the element prior to this one within the list
    # forward : the element after this one within the list
    # object  : the actual data
    #--------------------------------------------------------------------------
    attr_accessor :list, :back, :forward, :object
    #--------------------------------------------------------------------------
    # * Initialize
    # Initializes the Element object.
    #
    #   list    : The list in which it is included.
    #   object  : The object to encapsulate; can be of any type.
    #   back    : Its previous Element.
    #   forward : The following Element.
    #--------------------------------------------------------------------------
    def initialize(list, object, back = nil, forward = nil)
      @object       = object
      @list         = list
      self.back     = back
      self.forward  = forward
    end
    #--------------------------------------------------------------------------
    # * Assert
    # Ensures that the given object is of the given Class.
    #
    #   object  : The object to assert.
    #   type    : The class to match.
    #--------------------------------------------------------------------------
    def assert(object, type)
      if object.is_a? type
        raise ArgumentError, "Invalid argument type. Type of argument must be: #{ type }"
      end
    end
    #--------------------------------------------------------------------------
    # * Insert
    # Inserts an element after/before itself.
    #
    #   element : the node after/before which to insert the object
    #   before  : if TRUE, insert before, if FALSE, insert after.
    #--------------------------------------------------------------------------
    def insert(element, before = false)
      # Insert before
      if before
        element.forward = self
        self.back, element.back = element, self.back
        element.back.forward = element
      else # Insert after
        element.back = self
        self.forward, element.forward = element, self.forward
        element.forward.back = element
      end
    end
    #--------------------------------------------------------------------------
    # * Dispose
    # Disposes of self. Removes all references to self.
    #--------------------------------------------------------------------------
    def dispose
      @back.forward, @forward.back = @forward, @back
	  
	  @forward 	= nil
	  @back 	= nil
    end
    #--------------------------------------------------------------------------
    # * to_s
    # String representation of our element.
    #--------------------------------------------------------------------------
    def to_s
      return @object.to_s
    end
    #--------------------------------------------------------------------------
    # * <=>
    # Safely takes care of comparing our Elements amongst themselves.
    # If the objects within the two compared elements cannot be compared
    # together, it will assume they're "equal".
    #
    #   element : the element it is being compared to
    #--------------------------------------------------------------------------
    def <=>(element)
      # Return nil if our own object doesn't support <=>
      return nil unless @object.respond_to? :<=>
      
      # Is it an Element?
      unless element.is_a? Element
        # Assume it isn't, and try directly comparing it to our own object
        return @object <=> element
      else
        # It is; compare both objects
        return @object <=> element.object
      end
    end
  end
  #============================================================================
  # ** Circular
  #----------------------------------------------------------------------------
  # The Circular list is a particular type of List which, upon reaching its
  # ultimate element, will wrap around to start again from the start.
  #============================================================================
  class Circular
    #--------------------------------------------------------------------------
    # Public Instance Variables
    # ==========================
    # start : The starting element of our list. Mandatory, lest ye go mad.
    #--------------------------------------------------------------------------
    attr_reader :start
    #--------------------------------------------------------------------------
    # Included Modules
    #--------------------------------------------------------------------------
    include Enumerable
    #--------------------------------------------------------------------------
    # * Initialize
    # Instantiates our list object.
    #
    #   o : Arguments can be the following:
    #           
    #           1. A collection : Any collection will be converted into a list.
    #           2. An object    : It will be pushed into an Element and set as 
    #                             the starting element.
    #--------------------------------------------------------------------------
    def initialize(o)
      # Assume that, should it respond to the #each method, it is a Collection.
      if o.respond_to? :each
        o.each { |x| push(x) }
      else 
        push(o) unless o.nil?
      end
    end
    #--------------------------------------------------------------------------
    # * Empty?
    # Returns whether the list is empty or not. Assume that if there is no
    # "head" element, it's empty.
    #--------------------------------------------------------------------------
    def empty?
      return @start.nil?
    end
    #--------------------------------------------------------------------------
    # * First
    # Returns a reference to the first element of the list.
    #--------------------------------------------------------------------------
    def first
      return (@start.nil?) ? nil : @start.object
    end
    #--------------------------------------------------------------------------
    # * Last
    # Returns a reference to the last element of the list.
    #--------------------------------------------------------------------------
    def last
      return (@start.nil?) ? nil : ((@start.back.nil?) ? nil : @start.back.object)
    end
    #--------------------------------------------------------------------------
    # * Push
    # Pushes a new object at the end of the list.
    #
    #   object : The object to push at the end.
    #--------------------------------------------------------------------------
    def push(object)
      # Encapsulate
      element = Element.new(self, object)
      
      # Is our list empty?
      if empty?
        @start = element
        element.forward = element
        element.back    = element
      else
        # Set the new last as not our start, but this element
        @start.back.insert(element)
      end

      return self
    end
    #--------------------------------------------------------------------------
    # * Unshift
    # Prepends an object to our list, setting it as head.
    #
    #   object : The object to prepend.
    #--------------------------------------------------------------------------
    def unshift(object)
      element = insert(object, @start, true) # Insert BEFORE our head
      
      # Hook our element before the current head if there's one.
      @start.insert(element, true) unless empty?
      
      # Set the new element as the start
      @start = element
      return self
    end
    #--------------------------------------------------------------------------
    # * Length
    # Returns the number of object within our List.
    #--------------------------------------------------------------------------
    def length
      count = 0
      each { |x| count += 1 }
      
      return count
    end
    #--------------------------------------------------------------------------
    # * Each
    # Iterates through our list once for every item.
    #
    #   block : Yields to the given block.
    #--------------------------------------------------------------------------
    def each(&block)
      each_element { |e| yield e.object }
    end
    #--------------------------------------------------------------------------
    # * Each Element
    # Iterates through our lsit once, but yields the Element object.
    #
    #   block : Yields to the given block.
    #--------------------------------------------------------------------------
    def each_element(&block)
	  # No need to iterate if we're empty.
	  if empty?
	    return
	  end
		
      element = @start
      begin
          yield element
          element = element.forward
      end until element == @start or element.nil?
    end
    #--------------------------------------------------------------------------
    # * Delete
    # Deletes the given object from the list.
    #
    #   object : The object (not the Element object, the actual object)
    #--------------------------------------------------------------------------
    def delete(object)
      each_element do |e|
        if e.object == object
          if e == @start
            @start = (e == e.forward) ? nil : e.forward
          end
          
          # Removes node.
          e.dispose
        end
      end
    end
    #--------------------------------------------------------------------------
    # * Pretty Print
    # Prints out the list in a pretty way.
    #--------------------------------------------------------------------------
    def pretty_print
      retval = ""
      each { |o| retval << "#{ o }\n" }
      print(retval)
    end
    #--------------------------------------------------------------------------
    # * Clear
    # Clears the list before deletion.
    #--------------------------------------------------------------------------
    def clear
	  # Dispose of each Element
	  each_element { |e| e.dispose }
	
      # Set our start as nil
      @start = nil
    end
    #--------------------------------------------------------------------------
    # * Aliases
    # A bunch of commonly used aliases.
    #
    # append    => push
    # prepend   => unshift
    # size      => length
    #--------------------------------------------------------------------------
    alias :append  :push
    alias :prepend :unshift
    alias :size    :length
  end
end

Usage
I'll elaborate more on this tomorrow, but for now, here's a little tidbit:

Code:
c = List::Circular.new("Hello World!") # Creates a list with the string "Hello World!" as its head element
c.push(1) # Pushes an integer at the end
c.unshift(2) # Prepends the integer 2 at the beginning and sets it as the new head
c.each { |o| print(o) } # Will print out each object contained; not the Element, but the actual objects.
c.pretty_print # Prints out prettily

c.clear # Clears

c = List::Circular.new([1, 2, 3, 4, "Hello"]) # Yes, you can instantiate a list with ANY Enumerable collection!
c.pretty_print # Prints out!
 
etheon":1cah0bu5 said:
Note : I still have a memory leak problem when clearing the list. If anyone has a good solution/idea to remove ALL reference to an element EFFICIENTLY, please post it!

Could it be that the garbage collector isn't reclaiming your list because of the circular reference? Every object in the chain is pointed at by another, so it looks to ruby as if they're all in use?

If so you might find you can fix it by  breaking the circle before you clear it.  You might need to clear the back links as well since it's a double linked list. Shouldn't have any great overhead issues unless you're either clearing the list a lot or else you have very big lists.
 

e

Sponsor

I've updated my Element#dispose and Circular#clear methods; it shouldn't have memory leaks anymore. I think I've pretty much managed to remove all references to my Elements.
 

khmp

Sponsor

I bring a new toy. Math_Vector3, but he is only a tool to get the toy to work properly. Also I owe a lot to a Mr. verballydecapitating for helping me with the Sprite_Float class. Anyway another little addition to a project someone might want to include is a picture that assembles in blocks to form the larger image. You shouldn't need to physically split the image to get this effect thus my reasoning.

Class : Math_Vector3

Code:
#==============================================================================
# ** Math_Vector3
#------------------------------------------------------------------------------
#  This class only holds the information of a 3D Vector along with a few
#  helpful methods for using the class. (!No Cross Product!)
#==============================================================================

class Math_Vector3
  #--------------------------------------------------------------------------
  # * Public Instance Variables
  #--------------------------------------------------------------------------
  attr_accessor :x, :y, :z
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize(x = 0, y = 0, z = 0)
    @x, @y, @z = x.to_f, y.to_f, z.to_f
  end
  #--------------------------------------------------------------------------
  # * Magnitude
  #--------------------------------------------------------------------------
  def magnitude
    return Math.sqrt((@x * @x) + (@y * @y) + (@z * @z))
  end
  #--------------------------------------------------------------------------
  # * Multiply handles dot product and scalar
  #--------------------------------------------------------------------------
  def *(multiply)
    return scale(multiply) if multiply.is_a?(Numeric)
    return dot(multiply) if multiply.is_a?(Math_Vector3)
    return nil
  end
  #--------------------------------------------------------------------------
  # * Addition
  #--------------------------------------------------------------------------
  def +(add)
    return nil unless add.is_a?(Math_Vector3)
    return Math_Vector3.new(@x + add.x, @y + add.y, @z + add.z)
  end
  #--------------------------------------------------------------------------
  # * Subtraction
  #--------------------------------------------------------------------------
  def -(sub)
    return nil unless sub.is_a?(Math_Vector3)
    return Math_Vector3.new(@x - sub.x, @y - sub.y, @z - sub.z)
  end
  #--------------------------------------------------------------------------
  # * Scale
  #--------------------------------------------------------------------------
  def scale(scalar)
    return Math_Vector3.new(@x * scalar, @y * scalar, @z * scalar)
  end
  #--------------------------------------------------------------------------
  # * Dot Product
  #--------------------------------------------------------------------------
  def dot(vec)
    return ((@x * vec.x) + (@y * vec.y) + (@z * vec.z))
  end
  #--------------------------------------------------------------------------
  # * Returns Normalized Math_Vector3
  #--------------------------------------------------------------------------
  def normalize
    mag = magnitude
    return Math_Vector3.new(@x / mag, @y / mag, @z / mag)
  end
  #--------------------------------------------------------------------------
  # * Normalizes Calling Math_Vector3
  #--------------------------------------------------------------------------
  def normalize!
    mag = magnitude
    @x /= mag
    @y /= mag
    @z /= mag
  end
  #--------------------------------------------------------------------------
  # * Returns Array of Math_Vector3
  #--------------------------------------------------------------------------
  def to_a
    return [@x, @y, @z]
  end
  #--------------------------------------------------------------------------
  # * Get Component
  #--------------------------------------------------------------------------
  def [](index)
    case index
    when 0
      return @x
    when 1
      return @y
    when 2
      return @z
    else
      return nil
    end
  end
  #--------------------------------------------------------------------------
  # * Compare
  #--------------------------------------------------------------------------
  def ==(vec)
    return nil unless vec.is_a?(Math_Vector3)
    return true if vec.x == self.x && vec.y == self.y && vec.z == self.z
    return false
  end
  #--------------------------------------------------------------------------
  # * Alias Methods
  #--------------------------------------------------------------------------
  alias_method :length, :magnitude
end

That class has all the standard uses for a 3D Vector nothing special.

Class : Sprite_Float

Code:
#==============================================================================
# ** Sprite_Float < Sprite
#------------------------------------------------------------------------------
#  A sprite whose coordinates are Floating point instead of Fixnum. So movement
#  can be based on floating point math. No more 1.6 = 1 all the time nonsense.
#
#  *Special Thanks: verballydecapitating
#==============================================================================

class Sprite_Float < Sprite
  #--------------------------------------------------------------------------
  # * Alias Methods
  #--------------------------------------------------------------------------
  alias_method :sprite_float_x=, :x=
  alias_method :sprite_float_y=, :y=
  alias_method :sprite_float_z=, :z=
  #--------------------------------------------------------------------------
  # * Object Initialization
  #--------------------------------------------------------------------------
  def initialize
    super()
    @fx = @fy = @fz = 0.0
  end
  #--------------------------------------------------------------------------
  # * x=
  #     x : the new x coordinate
  #--------------------------------------------------------------------------
  def x=(x)
    self.sprite_float_x=(@fx = x.to_f)
  end
  #--------------------------------------------------------------------------
  # * y=
  #     y : the new y coordinate
  #--------------------------------------------------------------------------
  def y=(y)
    self.sprite_float_y=(@fy = y.to_f)
  end
  #--------------------------------------------------------------------------
  # * z=
  #     z : the new z coordinate
  #--------------------------------------------------------------------------
  def z=(z)
    self.sprite_float_z=(@fz = z.to_f)
  end
  #--------------------------------------------------------------------------
  # * x !OVERRIDE!
  #--------------------------------------------------------------------------
  def x
    return @fx
  end
  #--------------------------------------------------------------------------
  # * y !OVERRIDE!
  #--------------------------------------------------------------------------
  def y
    return @fy
  end
  #--------------------------------------------------------------------------
  # * z !OVERRIDE!
  #--------------------------------------------------------------------------
  def z
    return @fz
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    super()
  end
end

Again what's the use of Math_Vector3 containing Floating point numbers if the object that uses them is stuck using Fixnum.

Now for the biggie. These classes are used to represent parts of an image in individual sprites. This way they can moved around separate from each other and create some assembly effects. Sprite_Chunk is just a solitary sprite that holds part of an image within itself. Sprite_ChunkS err Sprite Chunk System is used to maintain these individual Sprite_Chunk's updating and disposal. It's utilization is pretty simple.

The standard three.
init:
Code:
@sprite_chunk_system = Sprite_ChunkS.new('/Graphics/Pictures/image.png', # Image filename or the bitmap.
  Math_Vector3.new(200, 200, 10), # Where you want the image to start to form.
  10, # The size of chunk width and height
  Math_Vector3.new(0, 0, 0)) # The starting point of the chunks
A word to the wise. Only the first parameter is required. The 4th parameter defaults to nil and if left nil than the pieces will start at random locations on the screen. Also size is despair in a cape. Low numbers mean more sprites that need to be created. Putting a one in there for example is a terrible idea and will eat up too much memory. I capped it at 4 I think.

update:
Code:
@sprite_chunk_system.update

disposal:
Code:
@sprite_chunk_system.dispose

Class : Sprite_Chunk, Sprite_ChunkS

Code:
#==============================================================================
# ** Sprite_Chunk < Sprite_Float
#------------------------------------------------------------------------------
#  A sprite whose coordinates are Floating point instead of Fixnum. Really
#  just a part of an image.
#==============================================================================

if defined?(Math_Vector3) && defined?(Sprite_Float)

class Sprite_Chunk < Sprite_Float
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     chunk       : the part of the bitmap
  #     start_point : the starting point of the pixels
  #     stop_point  : the point you want to end up at
  #     velocity    : the speed multiplier
  #--------------------------------------------------------------------------
  def initialize(chunk, start_point = Math_Vector3.new(0, 0, 999),
                 stop_point = Math_Vector3.new(100, 100, 999),
                 velocity = 10.0)
    super()
    
    # Assign it a position.
    self.x, self.y, self.z = start_point.to_a
    @stop_point = stop_point
    
    # If the points are the same we will have a NaN error. So no vel_vector
    # if start and end points are the same.
    if start_point != stop_point
      # Figure out the vector to the location.
      @vel_vector = stop_point - start_point
      @vel_vector.normalize!
      @vel_vector *= velocity
      @moving = true
    else
      @moving = false
    end
    
    # The image.
    self.bitmap = chunk
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    super()

    if @moving
      # Make a vector out of the sprite's position.
      pos = Math_Vector3.new(self.x, self.y, self.z)
      # Figure out how close we are to the end point. If if less than or equal
      # to the distance than just move it to the stopping point and tell it
      # to stop moving.
      if Math::distance(pos.to_a, @stop_point.to_a) <= @vel_vector.length
        @moving = false
        self.x, self.y, self.z = @stop_point.to_a
      else
        self.x, self.y, self.z = (pos + @vel_vector).to_a
      end
    end
  end
  #--------------------------------------------------------------------------
  # * Move To New Stop Point
  #--------------------------------------------------------------------------
  def move_to(stop_point = Math_Vector3.new(0, 0, 0))
    return if Math_Vector.new(self.x, self.y, self.z) == stop_point
    @moving = true
    @vel_vector = stop_point - Math_Vector.new(self.x, self.y, self.z)
  end
  #--------------------------------------------------------------------------
  # * Change Image
  #--------------------------------------------------------------------------
  def change_chunk(chunk)
    return if chunk.nil? || chunk.type == Bitmap
    self.bitmap = chunk
  end
  #--------------------------------------------------------------------------
  # * Dispose
  #--------------------------------------------------------------------------
  def dispose
    super()
    self.bitmap.dispose unless self.bitmap.disposed?
  end
end

#==============================================================================
# ** Sprite_ChunkS
#------------------------------------------------------------------------------
#  A system of sprites that come together to form a picture.
#==============================================================================

class Sprite_ChunkS
  #--------------------------------------------------------------------------
  # * Object Initialization
  #     image : the image to be used.
  #     stop  : the position where you want the sprite to end up at.
  #     size  : the size of the individual parts *Do not try [1, 2, 3]*
  #     start : the start position of the sprites (If nil it will be random)
  #--------------------------------------------------------------------------
  def initialize(image, stop = Math_Vector3.new(100, 100, 999), size = 10,
                 start = nil) # start is to be treated as another vector.
                              # if left empty start position will be random
                              # for each sprite.
    
    # Decide whether we were handed a bitmap or a filename?
    bitmap = nil
    if image.is_a?(String)
      bitmap = Bitmap.new(image)
    elsif image.is_a?(Bitmap)
      bitmap = image
    else
      return
    end
    
    # Safety check. It lags like a ***** if this number is below 4.
    size = 4 if size < 4
    
    # Figure out the amount of times to loop.
    width = bitmap.width / size
    height = bitmap.height / size
    
    # The current pixel location on the bitmap.
    px = py = 0
    
    # Chunk bucket
    @chunks = {}
    
    for i in 0...width
      for j in 0...height
        # Figure out where the correct stopping location would be.
        to = Math_Vector3.new(px + stop[0], py + stop[1], stop[2])
        # The Sprite_Pixel to be made.
        chunk = Bitmap.new(size, size)
        chunk.blt(0, 0, bitmap, Rect.new(px, py, size, size))
        at = start
        at = Math_Vector3.new(rand(640), rand(480), rand(999)) if at.nil?
        @chunks[@chunks.size] = Sprite_Chunk.new(chunk, at, to)
        # The size of the Sprite must be taken into account.
        py += size
      end
      # The size of the Sprite must be taken into account.
      px += size
      # Reset our height otherwise bad things will happen.
      py = 0
    end
    
    # Dispose of the image.
    bitmap.dispose
  end
  #--------------------------------------------------------------------------
  # * Frame Update
  #--------------------------------------------------------------------------
  def update
    @chunks.each_value { |sprite_chunk| sprite_chunk.update }
  end
  #--------------------------------------------------------------------------
  # * Dispose
  #--------------------------------------------------------------------------
  def dispose
    @chunks.each_value { |sprite_chunk| sprite_chunk.dispose }
  end
end

end
 

Zeriab

Sponsor

Here is a rather useful snippet I just cooked up.
This prevents the F12 key from resetting the game.
For some reason pressing the F12 key now seems to speed up the game in many cases and in other pauses the game.

Code:
#=============================================================================
# ** Reset class (because it won't be defined until F12 is pressed otherwise)
#=============================================================================
class Reset < Exception
  
end
#=============================================================================
# ** Module Graphics
#=============================================================================
module Graphics
  class << self
    #-------------------------------------------------------------------------
    # * Aliases Graphics.update and Graphics.transition
    #-------------------------------------------------------------------------
    unless self.method_defined?(:zeriab_f12_removal_update)
      alias_method(:zeriab_f12_removal_update, :update)
      alias_method(:zeriab_f12_removal_transition, :transition)
    end
    def update(*args)
      begin
        zeriab_f12_removal_update(*args)
      rescue Reset
        # Do nothing
      end
    end
    def transition(*args)
      done = false
      # Keep trying to do the transition
      while !done
        begin
          zeriab_f12_removal_transition(*args)
          done = true
        rescue Reset
          # Do nothing
        end
      end
    end
  end
end

For an extreme example of the speed up issue I mentioned here is a picture:
http://img89.imageshack.us/img89/438/speedboostlx8.png[/img]

I don't know whether it actually went with 410 FPS or if something broke.
 

e

Sponsor

Correct me if I'm wrong, but usually, before doing a transition, you do a Graphics.freeze, right? I'm guessing that would prevent the FPS count from jumping up.

Or I might be wrong. That was just off the top of my head.
 

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