RMRK is retiring.
Registration is disabled. The site will remain online, but eventually become a read-only archive. More information.

RMRK.net has nothing to do with Blockchains, Cryptocurrency or NFTs. We have been around since the early 2000s, but there is a new group using the RMRK name that deals with those things. We have nothing to do with them.
NFTs are a scam, and if somebody is trying to persuade you to buy or invest in crypto/blockchain/NFT content, please turn them down and save your money. See this video for more information.
Concepts in RGSS/2/3 ~ Questions and Discussion

0 Members and 1 Guest are viewing this topic.

*
Rep:
Level 97
2014 Most Unsung Member2014 Best RPG Maker User - Engine2013 Best RPG Maker User (Scripting)2012 Most Mature Member2012 Favorite Staff Member2012 Best RPG Maker User (Scripting)2012 Best MemberSecret Santa 2012 ParticipantProject of the Month winner for July 20092011 Best Veteran2011 Favourite Staff Member2011 Most Mature Member2011 Best RPG Maker User (Scripting)2011 Best Use of Avatar and Signature Space2010 Most Mature Member2010 Favourite Staff Member
Concepts in RGSS/2/3
Questions and Discussion


This topic arose out of a discussion concerning metaprogramming in the Gradual Leveling thread (that is also the context for the first couple of concepts) and I felt it would be a good idea to have a general thread where we could mutually explore neat concepts and useful tricks available in Ruby. While there is a lot of documentation for Ruby out there, another focus of this thread should be on how they can be used to make better scripts in the context of RPG Maker, and as such I hope that it can become a useful resource for scripters.

This is not a place to learn how to script nor to ask specific questions on how to accomplish a particular goal (things like that will be split from this thread and given its own topic) - rather it is meant for more general conceptual matters. If you want to know how to use a particular concept you've seen in other scripts or what the logic behind certain bits of code are, then feel free to ask them here. It should ultimately be not just about how it works and how to use it but also why it works. On the other side, if you have a good understanding of a particular concept that is not widely used and think it would be a good idea to share it with others, then post it here. Naturally, none of us (or at least very few) are professionals, myself included, and as such, the idea should be to create a friendly atmosphere of mutual exploration and dialogue which, hopefully, will leave us all better scripters. If anyone (probably me) makes mistakes or does something you do not understand, feel free to correct him/her or ask for clarification. No question is too stupid. Well, some are, but we'll be polite about it and just split it out of the thread :P

Also, be creative - it is meant to be a discussion, not necessarily a lecture - feel free to introduce a concept and have others work out its potential uses in RGSS/2.



Concepts Discussed
« Last Edit: July 26, 2012, 12:39:12 AM by modern algebra »

*
Rep:
Level 97
2014 Most Unsung Member2014 Best RPG Maker User - Engine2013 Best RPG Maker User (Scripting)2012 Most Mature Member2012 Favorite Staff Member2012 Best RPG Maker User (Scripting)2012 Best MemberSecret Santa 2012 ParticipantProject of the Month winner for July 20092011 Best Veteran2011 Favourite Staff Member2011 Most Mature Member2011 Best RPG Maker User (Scripting)2011 Best Use of Avatar and Signature Space2010 Most Mature Member2010 Favourite Staff Member
A fun little trick that you might find helpful is that when you have a lot of methods that are virtually identical (as you have with base_atk, base_agi, etc...), you can use this in order to do it all at once (and it makes it so that if you ever need to change the method, you only have to do it once)

Code: [Select]
stats = [:base_maxhp, :base_maxmp, :base_atk, :base_def, :base_spi, :base_agi]
stats.each_index { |i|
  alias_method ("tds_gradual_leveling_game_actor_#{stats[i]}".to_sym, stats[i])
  define_method (stats[i]) {
    return self.send ("tds_gradual_leveling_game_actor_#{stats[i]}") + gradual_parameter (i)
  }
}

It sacrifices readability a little bit, but if, for instance, you had made a mistake, you would only need to change it the once instead of six times. I find it is a useful trick, personally. In this case, you didn't need to pass arguments, but if you had wanted to, you would just put |arg1, arg2, ..., argn| after the { in the define_method () line. I haven't figured out a way to pass a block through like that though (except with eval, but that's neither here nor there). You can also pass the arguments through the send method like so:
Code: [Select]
self.send ("tds_gradual_leveling_game_actor_#{stats[i]}", arg1, arg2, ..., argn)

While in this case it would only be for your own convenience (it wouldn't improve performance in any way), a better way to use metaprogramming like this is, in my opinion, to allow for user input (though you would need to take some precautions in that case, like checking to see if the stat they asked for is defined in the first place). While not in this case since there would be a problem with passing the correct index to the gradual_parameter method, you could use this metaprogramming to allow the user to define for which stats you wanted to alter things. This could be useful for compatibility with other scripts that might add new stats. Again, it is not possible in this case without altering the gradual_parameter method, but if it were another script where you were just altering the stats in some other way (like, for instance, multiplying it by 1.25 whenever X state is applied), then you could allow the user to define for which stats the script should apply, and that would allow them to add stats from other scripts.

Also, you don't need the unless $@ for your aliases in this case since they are redefined in the original class. I can explain that if you want.

All in all though, a nice idea and well implemented. Good job.
« Last Edit: August 09, 2011, 01:10:21 PM by modern algebra »

*****
my name is Timothy what's yours
Rep:
Level 79
Hello
2014 Most Missed Member2014 Zero to Hero2014 Best IRC Quote2012 Zero To HeroSecret Santa 2012 ParticipantContestant - GIAW 9For frequently finding and reporting spam and spam bots2011 Zero to Hero
Or, you could use Crtl+H and press replace all. But I think your idea's better...
I tend to not use crazy tricks like that, mainly because I don't quite understand it. But basically that thing you posted, MA, was another way of saying
Code: [Select]
stats = [:base_maxhp, :base_maxmp, :base_atk, :base_def, :base_spi, :base_agi]
for method in stats
  alias_method("tds_gradual_leveling_game_actor_#{stats[method]}".to_sym, stats[method])
  define_method(stats[method]) {
    the rest of that
  }
end
if I am correct?
And, could you explain the $@ thing? I know its purpose, but not when it's appropriate. Tell us a story, great one.
it's like a metaphor or something i don't know

*
Rep:
Level 97
2014 Most Unsung Member2014 Best RPG Maker User - Engine2013 Best RPG Maker User (Scripting)2012 Most Mature Member2012 Favorite Staff Member2012 Best RPG Maker User (Scripting)2012 Best MemberSecret Santa 2012 ParticipantProject of the Month winner for July 20092011 Best Veteran2011 Favourite Staff Member2011 Most Mature Member2011 Best RPG Maker User (Scripting)2011 Best Use of Avatar and Signature Space2010 Most Mature Member2010 Favourite Staff Member
Umm, no - this is one case where the block is necessary and operates differently than that loop would and so was iteration by index (in this case, since it is used as an argument for the gradual_parameter method).

However, I am at work right now. I will try to get back to you when I'm on my lunch break.

I'm not a great one though  >:(
« Last Edit: August 09, 2011, 01:15:55 PM by modern algebra »

*****
my name is Timothy what's yours
Rep:
Level 79
Hello
2014 Most Missed Member2014 Zero to Hero2014 Best IRC Quote2012 Zero To HeroSecret Santa 2012 ParticipantContestant - GIAW 9For frequently finding and reporting spam and spam bots2011 Zero to Hero
*writes everything in notebook*
What in the hell would I do without you?
it's like a metaphor or something i don't know

*
Rep:
Level 97
2014 Most Unsung Member2014 Best RPG Maker User - Engine2013 Best RPG Maker User (Scripting)2012 Most Mature Member2012 Favorite Staff Member2012 Best RPG Maker User (Scripting)2012 Best MemberSecret Santa 2012 ParticipantProject of the Month winner for July 20092011 Best Veteran2011 Favourite Staff Member2011 Most Mature Member2011 Best RPG Maker User (Scripting)2011 Best Use of Avatar and Signature Space2010 Most Mature Member2010 Favourite Staff Member
First, I would just like to remind you that I am not, by any means, a professional. I do this as a hobby, and as such am neither formally trained nor particularly skilled. Basically, when I come across some new problem or concept I just play with it until I find an explanation for its behaviour that satisfies me. However, I am kind of an idiot and am satisfied fairly easily. I am frequently wrong and QED'd to be so when in the company of people who actually know this stuff. So even when I sound like I know what I'm talking about, don't believe it.

Anyway, first things first, it was useful to iterate by index since the gradual_parameter method took it as an argument - I think you just overlooked that so don't think you were asking about that.

More importantly, there is a subtle difference between using a block and a for loop, and that is basically that blocks introduce a new lexical scope, such that variables defined within a block are not available outside of it. In other words:

Code: [Select]
for i in [0, 1, 2]
  a = 0 if !a
  a += i*3
end
p a # -> 9

Code: [Select]
[0, 1, 2].each { |i|
  a = 0 if !a
  a += i*3
}
p a # -> ERROR : a is an undefined local variable

Moreover, when iterating like that, variables newly defined in the block are "redefined" for each iteration. In other words, the a defined in the 0th iteration is not the same a in the 1st iteration. So, if I were to put
Code: [Select]
p a
after a += i*3 (in the block example), you would find that it would be 0 on the 0th iteration, 3 on the 1st iteration, and 6 on the 2nd iteration (not 9). It doesn't carry over and, similarly, the i I define as the iterator is not the same object either.

I always use .each for iterating through an Enumerable because I am a little dense and it allows me to better anticipate the potential values of the variables I work with, thus leading to less mistakes and unforseen side effects. In most circumstances however, the distinction between the two will be irrelevant.

Not in this case though; it matters particularly for what we are doing here since we don't want our methods to respond to alterations of the iterating variable. In other words, this:

Code: [Select]
class Bla
stats = [:a, :b, :c]
for i in 0...stats.size
  define_method (stats[i]) {
    return i, stats[i]
  end
end
end

bla = Bla.new
bla.a # -> 2, :c
bla.b # -> 2, :c
bla.c # -> 2, :c

Since i ends up being 2, that is the value of i anytime you call any of those methods (:a, :b, or :c). As such, you will get 2, :c. We don't want that at all. We want the value of i used when defining our variable to be the one used when running the method. So, that's why a block is necessary:

Code: [Select]
class Bla2
stats = [:a, :b, :c]
stats.each_index { |i|
  define_method (stats[i]) {
    return i, stats[i]
  end
}
end

bla = Bla2.new
bla.a # -> 0, :a
bla.b # -> 1, :b
bla.c # -> 2, :c

I have a feeling that was a terrible explanation. Sorry.

As for $@, I have to get back to work, but I will write that up when I finish.
« Last Edit: July 30, 2012, 10:07:02 PM by modern algebra »

*****
Rep:
Level 84
This text is way too personal.
Bronze - GIAW 11 (Hard)Silver - GIAW Halloween
First of all, great script TDS! I like how clear cut your script is and how elegant it's code is. It's a really nice concept too!

@Pacman: Those that only use the simplest method are losing their freedom! Practise everything and anything and your specialities will improve nonetheless. If you don't like that method, you can always revert back to your old one.

@MA: That was a great explanation and I learned a lot! I think that there's a hiccup in your code though. define_method is calling a Proc in your case, so the end should be replaced with a brace (I think?) In other words:

Code: [Select]
class Bla
  stats = [:a, :b, :c]
  for i in 0...stats.size
    define_method (stats[i]) {
      return i, stats[i]
    }
  end
end


and:

Code: [Select]
class Bla2
  stats = [:a, :b, :c]
  stats.each_index { |i|
    define_method (stats[i]) {
      return i, stats[i]
    }
  }
end

Also, I too would like to learn about "$@". Whenever I try to get a printout of the variable, I just get a bunch of gibberish like:

Code: [Select]
["Section074:17: in `update'", " Section074:17:in `main'", etc..]

Also, so we don't detract from the original intention of this topic, maybe some posts should be moved to Scripts?

*
Rep:
Level 97
2014 Most Unsung Member2014 Best RPG Maker User - Engine2013 Best RPG Maker User (Scripting)2012 Most Mature Member2012 Favorite Staff Member2012 Best RPG Maker User (Scripting)2012 Best MemberSecret Santa 2012 ParticipantProject of the Month winner for July 20092011 Best Veteran2011 Favourite Staff Member2011 Most Mature Member2011 Best RPG Maker User (Scripting)2011 Best Use of Avatar and Signature Space2010 Most Mature Member2010 Favourite Staff Member
Yes, you're right about that - I was just doing it blind and I made that mistake - define_method should end in a brace.

But... back to work. I think I'll split this topic when I return as well.
« Last Edit: August 09, 2011, 06:07:37 PM by modern algebra »

pokeball TDSOffline
***
Rep:
Level 84
-T D S-
Silver - GIAW 11 (Hard)Silver - Game In A Week VII
We should have more topics like these.

Thank you very much for the help Modern, the whole similar methods thing was bothering me a lot, but I could not find a solution for it.

And I would like to know more about the $@.

@cozziekuns: Thanks, but I'm just a hobbyist so my code is probably far from elegant, I just try to do my best to cram as much stuff as possible within as little code as possible and still be able to read it.

*
Rep:
Level 84
Rubber Shark
Special variables in Ruby:
Code: [Select]
$! # latest error message
$@ # location of error
$_ # string last read by gets
$. # line number last read by interpreter
$& # string last matched by regexp
$~ # the last regexp match, as an array of subexpressions
$n # the nth subexpression in the last match (same as $~[n])
$= # case-insensitivity flag
$/ # input record separator
$\ # output record separator
$0 # the name of the ruby script file
$* # the command line arguments
$$ # interpreter's process ID
$? # exit status of last executed child process

*
Rep:
Level 97
2014 Most Unsung Member2014 Best RPG Maker User - Engine2013 Best RPG Maker User (Scripting)2012 Most Mature Member2012 Favorite Staff Member2012 Best RPG Maker User (Scripting)2012 Best MemberSecret Santa 2012 ParticipantProject of the Month winner for July 20092011 Best Veteran2011 Favourite Staff Member2011 Most Mature Member2011 Best RPG Maker User (Scripting)2011 Best Use of Avatar and Signature Space2010 Most Mature Member2010 Favourite Staff Member
I like it too - maybe when I split this I'll make it into a sticky and convert it into a general topic for discussing scripting concepts and asking questions.

Sorry, this is a lot longer than it really should be but I need to run right now and can't make it cleaner and prettier. I'll fix it up when I get back and make sure I didn't make any stupid mistakes.

In essence, you only need to put the condition on aliasing when you are aliasing an inherited method or a method in an otherwise hidden class (by hidden, I mean classes like Bitmap or the RPG module, etc). As far as I know, the following is why

I'll start with the F12 error; I find this part the hardest to explain, but basically, when F12 is pressed, the scripts in the Script Editor are re-interpreted, but the old interpretations aren't deleted in the way they would be if you closed and restarted the program. In other words, if this were our script list:

Sprite_Base
Window_Base
Scene_Base

For the purposes of describing the error, when we press F12 it is as if our script list were instead:

Sprite_Base
Window_Base
Scene_Base
Sprite_Base
Window_Base
Scene_Base

With the default scripts, it is perfectly fine to do that as there are no aliases or anything - all it's doing is redefining methods (to be the same that they were). Ie, it's equivalent to doing this:

Code: [Select]
class TDS
  def cozziekuns
    return "cozziekuns is awesome"
  end
  def cozziekuns
    return "cozziekuns is awesome"
  end
end

Redundant, but totally fine. If you call the cozziekuns method, you'll get "cozziekuns is awesome".

Now, let's take the situation where we alias it. So this:

Code: [Select]
class TDS
  def cozziekuns
    return "cozziekuns is awesome"
  end
  alias cozzie_new cozziekuns
  def cozziekuns
    return cozzie_new + " and also handsome"
  end
end

The cozzie_new alias is identical to the original cozziekuns method, so what you get is "cozziekuns is awesome and also handsome". Great. Now in the case of F12, it is effectively:

Code: [Select]
class TDS
  def cozziekuns
    return "cozziekuns is awesome"
  end
  alias cozzie_new cozziekuns
  def cozziekuns
    return cozzie_new + " and also handsome"
  end
end
class TDS
  def cozziekuns
    return "cozziekuns is awesome"
  end
  alias cozzie_new cozziekuns
  def cozziekuns
    return cozzie_new + " and also handsome"
  end
end

But this is also totally fine - since cozziekuns was redefined again to be
Code: [Select]
  def cozziekuns
    return "cozziekuns is awesome"
  end
before the second alias, it is only that which is aliased and so again, there is no recursion error. So that's why the "unless $@" was unnecessary in TDS's script. all of those base_stat methods were redefined back to normal in Game_Actor, so when aliased, it copied a method which had no reference to the alias name.

So, in essence, there is no reason to prevent the re-aliasing; it does no harm, it does no good; it does nothing. It aliases the exact same method it would have if it were the first time going through.

The problem arises when the original method is not redefined, and, as a result, the script aliases the method as altered (that has reference to the alias name). In other words, when this is effectively happening:

Code: [Select]
class TDS
  def cozziekuns
    return cozzie_new + " and also handsome"
  end
  alias cozzie_new cozziekuns

There, the alias is aliasing a method which calls it, ie, it is:

Code: [Select]
  def cozzie_new
    return cozzie_new + " and also handsome"
  end

I think it's fairly obvious why this is a problem - when cozzie_new is called, it can never complete because it is recursive with no way to ever finish. As such you get the "F12 error", which is just that recursion error when it occurs as a result of the reinterpretation of the scripts.

In RGSS/2, there are at least two ways this can happen: (1) when you are aliasing a method in a "hidden" object; and (2) when you are aliasing an inherited method that is not already redefined in the class (ie, only defined in its superclass or inherited module).

The first scenario is pretty straightforward. Unlike the scripts in Scripts.rvdata, the "hidden" classes (Bitmap, Sprite, Window, etc.) aren't reinterpreted when you press F12. As such, this is what is effectively happening (something nonsensical):

Code: [Select]
class Bitmap # The actual method
  def disposed?
    # Whatever
  end
end

class Bitmap # Your edits
  alias ma_disposed_new disposed?
  def disposed?
    return false # if whatever
    return ma_disposed_new
  end
  alias ma_disposed_new disposed? # Reinterpretation
  def disposed?
    return false # if whatever
    return ma_disposed_new
  end
end

As such, ma_disposed_new now calls itself without end when it is called and we have our recursion error.

For the second scenario, consider this:

Code: [Select]
class TDS
  def cozziekuns
    return "cozziekuns is awesome"
  end
end

class Pacman < TDS
  alias cozzie_new cozziekuns
  def cozziekuns
    return cozzie_new + " and also handsome"
  end
end

class TDS
  def cozziekuns
    return "cozziekuns is a pumpkin"
  end
end

t = TDS.new
p = Pacman.new
t.cozziekuns # -> "cozziekuns is a pumpkin"
p.cozziekuns # -> "cozziekuns is awesome and also handsome"

As you can see, the alias in Pacman retains the first definition of cozziekuns from TDS and naturally ignores the second (because the alias is created before the method is ever redefined). From this, it should be apparent why the "re-"interpretaton forced by F12 causes a problem. When it redefines the method in TDS, that has no impact on the Pacman class. As such, on the re-interpretation, when it gets to the alias, the current cozziekuns method in Pacman (since it had already been defined on the first interpretation and was not redefined since) is:
Code: [Select]
  def cozziekuns
    return cozzie_new + " and also handsome"
  end

And so when the alias for cozzie_new happens, it is that method it aliases. So that is why the recursion error happens in that scenario.

Hopefully that explains when a fix is necessary and why.

The Fix

So, in those scenarios is where the "unless $@" fix comes in - putting that will prevent the second aliasing, thus the alias name will still refer to the original definition and the recursion error will not occur. So, then the question is, what is $@ and why does "unless $@" prevent the recursion error?

The answer to that is easier. As Yeyinde mentioned, $@ contains the backtrace for the last exception raised.

When you first start the program, $@ will be nil, which is why "unless $@" will not prevent the initial aliases when first "loading" the scripts. The reason "unless $@" works to avoid stack errors later is because when F12 is pressed, it throws the "Reset" exception to tell the program to reset, which means that the second time the program interprets the scripts, $@ will have the backtrace for the Reset exception and therefore will not be nil, so the "unless $@" will be true and the alias will not happen again.

Personally, I think the reason hardly anybody knows why or when checking "unless $@" is necessary is precisely because it's checking $@ which, though reliable and related to the ultimate cause of the recursion error, doesn't really give the reader of the script much of an indication of why the recursion error happens in the first place. I think it's bad form to use it and instead use:
Code: [Select]
unless self.method_defined? (:alias_name)
It's a little uglier/longer and a little slower (totally negligible) than just checking $@, but I think it is more helpful.
« Last Edit: August 10, 2011, 08:39:38 PM by modern algebra »

*****
my name is Timothy what's yours
Rep:
Level 79
Hello
2014 Most Missed Member2014 Zero to Hero2014 Best IRC Quote2012 Zero To HeroSecret Santa 2012 ParticipantContestant - GIAW 9For frequently finding and reporting spam and spam bots2011 Zero to Hero
Damn you Yeyinde and your knowledge!
@MA, that was a fantastic explanation. I understood completely why I was wrong. And TDS, it is a good script. MA just makes it easier for you to edit.
it's like a metaphor or something i don't know

*****
my name is Timothy what's yours
Rep:
Level 79
Hello
2014 Most Missed Member2014 Zero to Hero2014 Best IRC Quote2012 Zero To HeroSecret Santa 2012 ParticipantContestant - GIAW 9For frequently finding and reporting spam and spam bots2011 Zero to Hero
Good idea, MA. This thread will be a good reference.
Here's something I hear misinterpreted a lot: thinking class variables are the same as instance variables or global variables. They're different.
We know that when creating an object, we use the class as a blueprint and create an instance with it. This means we can have multiple at the same time and work with them. We know that a global variable begins with a $ and is accessible from anyway, both getter and setter. And an instance variable begins with an @ sign and is open to all methods within an instance of a class.
A class variable begins with two @ signs (@@) and is accessible throughout ALL instances of a class, not just the one. I can't think of a practical example to demonstrate this with, so if anyone could that would be fantastic. I just wanted to clear this up.
The one other thing I want to make sure everyone knows, and I know that everyone on this thread does, are attrs.
Code: [Select]
class Thing
  def initialize(n)
    @n = n
  end
  def n
    return @n
  end
end
The n method can be expressed much quicker with one line:
Code: [Select]
class Thing
  attr_reader :n
  def initialize(n)
    @n = n
  end
end
This is a 'getter' method, which will return the value of the variable.
Code: [Select]
class Thing
  def initialize(n)
    @n = n
  end
  def n=(i)
    @n = i
    return @n
  end
end
The n= method can also be expressed in one line:
Code: [Select]
class Thing
  attr_writer :n
  def initialize(n)
    @n = n
  end
end
Finally, both methods can be written either as this:
Code: [Select]
class Thing
  def initialize(n)
    @n = n
  end
  def n
    return @n
  end
  def n=(i)
    @n = i
    return @n
  end
end
Or, much more easily:
Code: [Select]
class Thing
  attr_accessor :n
  def initialize(n)
    @n = n
  end
end
Now nobody has an excuse.
it's like a metaphor or something i don't know

*****
Rep:
Level 84
This text is way too personal.
Bronze - GIAW 11 (Hard)Silver - GIAW Halloween
Code: [Select]
class Foo
  def initialize
    @@variable = 5
    @variable = 6
  end
end

class Bar < Foo
  def variable_class
    p @@variable
  end
  def variable_instance
    p @variable
  end
end

b = Bar.new
b.variable_class # => 5
b.variable_instance # => nil

I think this is what you were getting at, Pacman.

*
? ? ? ? ? ? ? ? ? The nice kind of alien~
Rep:
Level 92
Martian - Occasionally kind
Pressing F12 behaves differently in RGSS and RGSS2.
In both cases it seems that a Reset exception is create if it does not exist which may look like this:
Code: [Select]
class Reset < Exception
 
end

It then throws this error which you can handle if you want. For RGSS either Graphics.update or Graphics.transition or throws it.
For RGSS2 it looks like the exception can be thrown at any given point.

Assuming you don't handle the exception it looks like the editor simply starts over from the first section keeping all global variables, classes and etc, although it may dispose visual elements. I haven't checked what happens if you store a reference to one in a global variable.

The most visible error caused by the behavior of reseting without properly cleaning the enviroment is the endless recursion case due to it's game crashing nature, but it can easily cause more subtle behaviors simply because the assumptions you have about a global variable at the start of the game may not hold after you press F12. Just consider new game plus style script. Who knows how they can malfunction. In RGSS2 it's even worse because there is a much higher chance of a global variable getting corrupted.

For RGSS2 I would always recommend using an F12 cleaner which simply starts a new instance of the game and closes the current one.

*hugs*

*****
Rep:
Level 84
This text is way too personal.
Bronze - GIAW 11 (Hard)Silver - GIAW Halloween
That was very informative, Zeriab. Thanks for the input!

Anyway, I've been playing around with the sort method for arrays; specifically when you make a comparison without a block.
Code: [Select]
array.sort do |a, b|
#stuff
end
My question is: how exactly does ruby sort the array? I know it has something to do with <=>, but what does it do with the value obtained by said operator?

pokeball TDSOffline
***
Rep:
Level 84
-T D S-
Silver - GIAW 11 (Hard)Silver - Game In A Week VII
Well, since no one has tried I'll give it a try.

This is a complete shot in the dark.

From what I know <=> compares by a -1 1 +1 sort of system.

Code: [Select]
1 <=> 0   »   1  More
1 <=> 1   »   0  Same
1 <=> 2   »  -1  Less

So it would seem that Ruby sorts the arrays based on that, by going through the values on the array and putting them in an order based on their comparative value of the arguments |a, b|.

This is just what I understand about it and someone else could probably explain it better.

*****
Rep:
Level 84
This text is way too personal.
Bronze - GIAW 11 (Hard)Silver - GIAW Halloween
This isn't really a question more so it is a curious observation, but I haven't found any documentation on it (I guess because no one has been stupid enough to do it before). The problem is, what happens if I declare two sprite classes in the same variable? Example:

Code: [Select]
sprite = ::Sprite.new
sprite.bitmap = Bitmap.new(32, 32)
sprite.bitmap.fill_rect(0, 0, 32, 32, Color.new(255, 255, 255))
sprite.x = 0

sprite = ::Sprite.new
sprite.bitmap = Bitmap.new(32, 32)
sprite.bitmap.fill_rect(0, 0, 32, 32, Color.new(255, 255, 255))
sprite.x = 50

loop do
  Graphics.update
end

In this code, the variable sprite represents a 32*32 pixel block of white. I inserted a sprite into the variable sprite, and then inserted another variable. I thought that the second sprite would override the first sprite, but something odd happens. Instead, you have a ghost sprite object representing the initial bitmap. In this example, you have 2 squares instead of one on the right. So the first sprite declared becomes a ghost sprite that can't be interacted with; changing values of the variable sprite will only result in the changing properties of the second sprite, and not our ghost sprite.

Any ideas why this happens?

pokeball TDSOffline
***
Rep:
Level 84
-T D S-
Silver - GIAW 11 (Hard)Silver - Game In A Week VII
My take on it is that the first sprite object still exists in memory as an object marked to be removed by something called Garbage Collection which usually takes care of removing stuff like that. I don't personally know a whole lot about it though. I just know that Zeriab once told me not to directly call it, as I was doing so in a battle system to prevent a battle system from crashing the game.

If you add this to your script "GC.start" before the loop you will see that it will remove the previous sprite.

I hope someone can expand more on Garbage Collection as I would like to know more about it.

*
? ? ? ? ? ? ? ? ? The nice kind of alien~
Rep:
Level 92
Martian - Occasionally kind
The important part is that you loose the reference to the first sprite. What the sprite then references to is irrelevant. I cut down the code to the issue:
Code: [Select]
sprite = ::Sprite.new
sprite.bitmap = Bitmap.new(32, 32)
sprite.bitmap.fill_rect(0, 0, 32, 32, Color.new(255, 255, 255))
sprite.x = 0
foo = sprite

# Loose the reference to the sprite
sprite = nil
# Loose the other reference as well
foo = nil

When you have no references either directly or indirectly you have the garbage cleaner taking care of disposing the object eventually. How exactly it does it is transparent and it doesn't really matter except in rare situations. The only reason you notice something is that the visual element disappears whenever the sprite is disposed, not when you loose the last reference to it.

As TDS say you can force the garbage cleaner to start now though generally speaking it often slows down rather than speeds up the game. There are cases where calling it can make sense which typically are when removing the references to large numbers of objects or objects taking up significant memory. I could imagine it being beneficial to call GC after clearing the RPG::Cache for example. (They may already do that in the method, I haven't checked).
 
In your case it is way more desirable to dispose the sprite explicitly rather than rely on the garbage cleaner.

P.s. If you maintain a reference to the sprite it will not disappear. You can try using a global variable to see the effect. Yes, you don't have to update a sprite for it to be present. You only have to update a sprite if it changes.
P.s.s You can disable the garbage cleaner with GC.disable which is a great way to turn your game into a memory hog. (Can be useful when debugging)

*hugs*
« Last Edit: September 22, 2011, 04:44:04 PM by Zeriab »

**
Rep: +0/-0Level 65
RMRK Junior
Umm... I don't know where to ask this, so I do it here:
Are the VX-Scripts compatible to VXA-Projects?
Is there any study, about the incompatibility?
English isn't my first language, but I hope you understand what I mean.

****
Rep:
Level 71
Only some are, this is because VX uses RGSS 2 and VXA uses RGSS 3. So ya there is definitely a study on it.  ;)
If you have any more questions please ask them in the script section.

*****
Rep:
Level 84
This text is way too personal.
Bronze - GIAW 11 (Hard)Silver - GIAW Halloween
Hey guys, I have a random question that's been bugging me about ruby. What exactly does this code do?

Code: [Select]
module Cozziekuns
class << self # This is the part I need clarification on
 
end
end

Specifically, I want to know what exactly the << operator does, and what exactly it is doing. Is it transforming the module into a static class?

Tsunokiette away!

*
Rep:
Level 97
2014 Most Unsung Member2014 Best RPG Maker User - Engine2013 Best RPG Maker User (Scripting)2012 Most Mature Member2012 Favorite Staff Member2012 Best RPG Maker User (Scripting)2012 Best MemberSecret Santa 2012 ParticipantProject of the Month winner for July 20092011 Best Veteran2011 Favourite Staff Member2011 Most Mature Member2011 Best RPG Maker User (Scripting)2011 Best Use of Avatar and Signature Space2010 Most Mature Member2010 Favourite Staff Member
Basically, it's syntax which sets self to the metaclass of the module, thus allowing you to operate on the metaclass. The metaclass is an instance of Class and is like any other class in that you can give it methods, but it only attaches to the object itself (in this case, Cozziekuns), and it is effectively invisible (ie. Cozziekuns.class #=> Class). All modules and classes have a metaclass.

As an answer to your particular question: no, it is not transforming the module into a static class - it is allowing you to operate on its metaclass, which is distinct from the module. Cozziekuns remains a module and can be used as a module, and when a class includes a module, the metaclass methods are not included (since it's a Class). Maybe an example helps:

Code: [Select]
module Cozziekuns
  class << self
    def box
      p "I am a box"
    end
  end
  def bump
    p "I am a bump"
  end
end

class ModernAlgebra
  include Cozziekuns
end

p Cozziekuns.box #=> "I am a box"
p ModernAlgebra.new.bump #=> "I am a bump"
p ModernAlgebra.box #=> Undefined method: box

Yehuda Katz wrote a useful article on metaprogramming that is worth your time: http://yehudakatz.com/2009/11/15/metaprogramming-in-ruby-its-all-about-the-self/

It doesn't mention modules specifically, but the metaclass of a module operates in the same way as the metaclass of a class. I also highly recommend that you watch this presentation by Dave Thomas: http://scotland-on-rails.s3.amazonaws.com/2A04_DaveThomas-SOR.mp4
« Last Edit: July 26, 2012, 12:40:27 AM by modern algebra »