Announcement
Announcement
| 2nd Quarter Contest Announcement posted! See the Community Announcements section. |
![]() ![]() |
Aug 16 2009, 10:29 PM
Post
#1
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
I thought that I would take some time to teach those who do not know about the powerful programming paradigm known as the "Observer" pattern. Ruby does not have any built in language support for this pattern (some languages do, i.e., C# and Java), but it does provide a module named "Observer" for using it. Instead of showing you how to use that module (there is already documentation for that here), I will be implementing my own solution so that you can see how it all works.
The concept is simple really, but it does require that you understand some basic programming principles before we start. You need to understand the following terms: 1. Method (or function, subroutine, etc.) 2. Class 3. Object (aka, instance of a class) 4. Encapsulation 5. Hash Table If you do not understand one or more of the concepts listed above, please take some time now to read up. You really do need to understand what they mean before going forward. -------------- OK, done reading? Great, let's learn what this is all about. In object oriented programming we strive to achieve encapsulation between our classes. Also know as "information hiding", encapsulation helps to create robust and maintainable code. When we create a class, we would like it to be responsible for one purpose. Whatever that purpose is, our class is the only class in our program which contains the logic for dealing with this responsibility. for example, let's say we are writing a class used to represent an "actor" in a video game. Since this obviously sounds familiar, we will simply be adding to/modifying the Game_Actor class in the RPG Maker VX. This class is responsible for maintaining the state of an actor. It has properties like 'hp', 'mp', 'str' (strength), and so on. It also has methods used by outside users of the class to change the state of the actor. No other class in our program is responsible for actors, only the Game_Actor class. This type of design allows us to keep all of the logic needed to represent an actor in one place. Users of this code can call methods like "hp" to ask the actor "what is your current hp level?". Outside code does not know how the Game_Actor class keeps track of hp, nor does it care. Outside code need only know that the Game_Actor class contains that information, and that is encapsulation. So, how do "events" and the "Observer" pattern fit into all of this? This pattern helps us to increase the level of encapsulation in our classes and reduces the chance of breaking external code by changing the internal implementation of the class. for our purposes, a HUD window will serve as a nice example. We will create a HUD that displays the current hp level of the first actor in the database. Before we create the HUD window, let's make a class that we will use to encapsulate the logic behind an "event". (Not that in this tutorial I use the term "event" to signify something happening in a class, not an in game event. For example, an actor's hp changing would fire an "hp_changed" event). So instead of writing code to explicitly check conditions such as hp changing, you can just ask an object to let you know when it happens. Here is our event class. I have added comments to explain what is going on: Spoiler: Alright, so now we have a class that can attach and detach callback functions mapped to an id. So, how can we use this? We will add an event handler to the Game_Actor class that clients can use to map a callback function. We will add a handler to be used when the actor's hp changes. Again, comments explain what we are doing. Spoiler: OK, so now we have added an event to the Game_Actor class, but how do we use it? Clients (users) of the Game_Actor class will subscribe to the event like so: Spoiler: When the above code runs, a messagebox will be displayed whenever the actor's hp value changes. You do not need to write any logic in that class that checks the actor's hp against some cached value, you are automatically informed of the change by the Game_Actor class. Alright, we are ready to write our HUD window class: Spoiler: And this is the addition we need to add to Scene_Map to make it all work in game. Notice that we do not need to modify the 'update' method in this class. Most huds would expose an update method and, inside of that method, refresh the window if anything has changed. We do not need to do any of that because our hud refreshes itself automatically when a parameter changes due to our event architecture. Spoiler: Well, that's it. This should provide a good basis for understanding event driven programming. Please let me know if you guys find this confusing so that I can improve it. -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Aug 25 2009, 09:53 AM
Post
#2
|
|
![]() Overlord of your face! Yeah, I went there. ![]() Type: Coder Alignment: Neutral Good |
Very nice BigEd
-------------------- Overlord_Dave's super-awesome blog
![]() ![]() EVERYBODY LOOK: modern algebra's Script Searching Tutorial I'm getting tired of seeing him plug this every time someone requests a script that already exists :) BEFORE REQUESTING A CUSTOM BATTLE SYSTEM, THINK! (<-- clicky clicky) My To-do list: Spoiler: My contributions: Spoiler: I did a silly test: Spoiler: (I AM AWAKE AT 4AM TO THE TERRIFYING UNDENIABLE TRUTH THAT THERE IS NOTHING I CAN DO TO STOP THE MONSTER) |
|
|
|
Aug 25 2009, 04:28 PM
Post
#3
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
Very nice BigEd That's a good idea, thanks Dave -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Sep 19 2009, 03:33 AM
Post
#4
|
|
|
inactive. ![]() Type: Coder Alignment: Neutral Good |
If it came from BigEd, this is sure to be good.
I'll read it later. (last post was aug 26, I hope this is not necropost) Thanks! -------------------- |
|
|
|
Dec 3 2009, 01:16 AM
Post
#5
|
|
![]() ![]() |
Dear Stu,
Thank you very much for time taken to send me valuable information. With many thanks, Saku -------------------- |
|
|
|
Dec 3 2009, 06:11 AM
Post
#6
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
Dear Stu, Thank you very much for time taken to send me valuable information. With many thanks, Saku ...What? (Don't answer that) -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Dec 5 2009, 06:35 AM
Post
#7
|
|
![]() Learning RGSS(2) ![]() Type: Coder |
So, is this for scripting inside events? Now this is what I like as I hope to script(in events if possible) my ABS.
Just read the title and was asking, if it's from ED I'm reading. This post has been edited by MysticTrunks: Dec 5 2009, 06:36 AM -------------------- ![]() ![]() ![]() |
|
|
|
Dec 5 2009, 11:35 PM
Post
#8
|
|
![]() No method: 'stupid_title' found for 'nil:NilClass` ![]() Type: Coder Alignment: Chaotic Good |
So, is this for scripting inside events? Now this is what I like as I hope to script(in events if possible) my ABS. Just read the title and was asking, if it's from ED I'm reading. An "Event" in computer science terms is simply a design pattern which helps to loosen coupling between components of your application. If you had two objects, A and B, which each needed to know some detail about the other, you can do so by creating object A such that it were able to tell object B when something relevant happens instead of B asking A constantly if something has occurred. It helps us to write more modular and maintainable code. So, you won't be able to implements something like this directly in an event. You can always use the "Script..." event command to call into any global data object, static object, or anything defined in Game_Interpreter. -------------------- My blog - It's awesome, I assure you
QUOTE While sloppy writing does not invariably mean sloppy thinking, we've generally found the correlation to be strong -- and we have no use for sloppy thinkers. If you can't yet write competently, learn to. - Eric Raymond ---![]() My awards for being so awesome Spoiler: |
|
|
|
Jun 15 2010, 01:44 PM
Post
#9
|
|
![]() Type: Designer Alignment: Neutral Good |
This is great, Ed... since this is a sticky, I'll assume me thanking you isn't necro. Just wanted to say thanks for this, you're awesome.
|
|
|
|
Mar 4 2011, 03:39 AM
Post
#10
|
|
![]() ![]() Type: Artist Alignment: Chaotic Good |
You're the first person that's explained why encapsulation is necessary in a way I can understand, Ed. Thank you.
|
|
|
|
Mar 4 2011, 07:30 PM
Post
#11
|
|
![]() Retributionist. ![]() Type: Writer |
I get that the above post was a necro-, but Ed's still around, and the topic is obviously still helpful.
I remember when this was posted, all of it just sailed over my head. But I actually get it now. Ed, you have some crazy good teaching skills. You keep it simple, but still tell the reader what is actually going on, avoiding (hopefully) those cargo cult programmers. I guess I'm proud that I've improved enough to understand this, however basic it may be. -------------------- ![]() |
|
|
|
![]() ![]() |
| Lo-Fi Version | Time is now: 22nd May 2013 - 10:20 AM |
|
|