My Assistant
lucksta"Don't judge a book by it's cover -- judge it by the movie."
| Entertainment
| Game Making
| Creative Forum
| Technology & Computers
| Translations
Aug 10 2008, 08:07 PM
Post
#1
|
||
![]() Look what the Khat dragged in... CelestialMember No.: 54 Posts Liked: 0 * Joined: 3-December 04 Posts: 2,115 --> From: Narnia! (Just north of Lantern Waste) RPG Maker Level: Good Class Title: combat muskrat |
Chaptah 1 (Being the first chapter in a series with a number of chapters which is larger than one.)
Okay, I'm amazed and perplexed that someone hasn't written this yet, so I'll do it. One of the great things about RGSS is that it has access to a very easy-to-work-with graphics library. This tutorial will hopefully teach you how to take advantage of that functionality in your scripts and projects. This tutorial is written to be understandable by people who don't know much about scripting, since sprite control is a pretty simple concept anyway. First of all let's take a look at the sprite class itself. Sprite is a built-in class in RGSS. You can't get at the code of Sprite because it's written in C and integrated into RMXP. (You can use aliasing and normal class redefinitions, though.) We can, however, look in the RMXP help file and see everything we need to know to use the class effectively. The help file specifies that a new sprite can be created like so: Sprite.new([viewport]) Where 'viewport' is an optional argument that allows you to assign the sprite to a viewport. In graphics design a 'viewport' is a region in which images are drawn. In RMXP we can use several viewports in order to apply graphics effects to multiple sprites at once. For instance, by moving a viewport back and forth quickly RMXP can simulate an earthquake (shake screen, etc). Generally I don't use viewports because generally I don't use viewport effects, but you can read more about the Viewport class in the helpfile. So let's make us a sprite. Open your script editor and create a new section just above 'main', then enter this: RUBY def make_me_a_picture_dude mysprite = Sprite.new end Now create an event on your map of a bush or a person or whatever makes you happy. Set it to be triggered by the action key and put in an event command that runs this script: RUBY make_me_a_picture_dude Now you can test the method we wrote in the script editor by talking to the event. Great excitement there, eh? Nothing happens. That's because we haven't yet associated our sprite with an actual image. In order to do that we need to set its .bitmap property. RUBY def make_me_a_picture_dude mysprite = Sprite.new mysprite.bitmap = Bitmap.new("Graphics/Icons/001-Weapon01") end There it is! Up in the corner there! We made a picture! ... Wait, where did it go? Your picture probably disappeared after a few seconds. This is because in Ruby (and therefore in RGSS) there's a critter called the 'Garbage Collector' (or 'GC' for short) that roams around in your memory looking for things that you're not using anymore and throwing them out. The GC isn't a bad guy, though. He's there to make sure you don't use up all of your memory with stuff you don't need. He operates according to a simple rule: If you can't reach it then it gets thrown away. In our method, 'make_me_a_picture_dude', we declared our sprite as an object called 'mysprite'. As long as that method is running we can 'reach' the object by referring to it as 'mysprite', just like we did when we set up its .bitmap property. However, once we reach the end of that method the program moves on and we no longer have any way of referring to 'mysprite'. That makes it property of the GC, and after a few seconds he finds it and eats it. We can work around this by changing the 'scope' of the object. In this case our method is a global one (it's not inside a class), so we have two options. We can make 'mysprite' a global object or we can make it an instance object. Since our method is global either way we do it will have the same result. Let's make it an instance object by changing its name to '@mysprite'. An instance object (aka - instance variable) is like a normal one, except that its 'scope' is larger. Let's pause here to discuss scope. RUBY class My_Class def initialize local_var = 0 @instance_var = 1 $global_var = 2 #I can reach all three variables from here. end #I can reach @instance_var and $global_var from here. def another_method #I can reach @instance_var and $global_var from here too. end end #I can only reach $global_var from here. In this script we declare one of each of the three common variable types. A local variable is declared by naming it without any special characters and giving it a value. As you can see, the local variable has the smallest 'scope', meaning we can only reach it from inside the method that it's declared in. That means that once that method is done executing the GC destroys that variable and the object inside of it. The instance variable, however, can be reached from anywhere inside of the class. An instance variable is created just like a local variable, but we add a '@' to the beginning of its name. If we delcare an instance of the class by saying 'my_obj = My_Class.new' then my_obj can contain a copy of '@instance_var' for as long as it exists. Since RMXP uses classes to create scenes you're likely to come across sprites declared as instance variables within those classes. That way the sprite will continue to exist as long as the scene is still going on. Finally, we have the global variable, which can be reached from anywhere in the program. The only reason the GC would ever collect a global variable is if it gets set to 'nil'. A global variable is created by starting a variable's name with '$'. Another way of creating a global variable is to declare it in the global scope (outside of any class or method), but that can get messy really fast. You may be tempted to use global variables for everything after hearing that. Don't do it! Your program will very quickly become a huge mess and you run the risk of using up all too much memory or of causing bugs by methods fighting for control of a global object that you forgot about and used the same name twice. In programming, always declare your variables with the smallest possible scope to prevent problems. You'll be glad you did later. Anyway, since we're not working inside a class yet, we can make our variable an instance variable and it'll work just fine. (It will actually get assigned as a property of the map's instance of 'Interpreter'.) RUBY def make_me_a_picture_dude @mysprite = Sprite.new @mysprite.bitmap = Bitmap.new("Graphics/Icons/001-Weapon01") end Great, so now we have a picture of a sword floating in the corner. Let's do something to it. In the help file you can see that the Sprite class has a lot of properties that all do interesting things. Let's change the sprite's x and y properties first to move it around the screen. You'll find that .x and .y are the properties of the Sprite class that you'll probably have the most business with. In RMXP the screen is 640 pixels wide and 480 pixels tall. The width is described as the 'x' dimension and the height is described as the 'y' dimension. There's also a 'z' dimension that deals with what sprites are 'on top' of other sprites, but we'll deal with that in a minute. When dealing with computer graphics, especially 2D graphics, it's normal for the upper-left corner of the screen to be the 'origin'. That means that the upper-left-most pixel has an x value of 0 and a y value of 0, and those values get larger as you move rightward and downward respectively. Think of it like reading a page from a book. X comes first in the alphabet, so it's the first dimension we deal with. When you read words on a page you read from left to right. X gets bigger from left to right on the screen. Once you're done reading a line you move down one row to the next line. Y is the next letter in the alphabet. Y values get bigger as you move down the screen. Let's move the sword to the middle of the screen. This will require a little math. Nothing beyond basic algebra, so don't have a friggin' panic attack or anything. To center the sprite in the screen we need four peices of information. We need to know the width and height of the screen and we need to know the width and height of the bitmap. We already know the screen properties, we just need to know about the sprite. Fortunately the Bitmap class has methods that will allow us to automatically determine the width and height of the bitmap. They are Bitmap#width and Bitmap#height. (Who would have guessed?) We can thus refer to our image like so: @mysprite.bitmap.width @mysprite.bitmap.height Now let's use that information to center the sprite by setting its .x and .y properties. First we'll figure out what we want x to be. We know that the screen in 640 pixels wide. Half of 640 is 320. But the point we are describing is not the center of our bitmap. The point we're describing is the upper-left corner of our bitmap. If we set x to 320 the sprite's left edge will be centered. That's not what we want. We want the sprite's middle to be centered, so we want a value that's less than 320. By what amount, though? Well, if we subtract the sprite's width from 320 we'll have the opposite problem. The sprite's right-edge will be in the middle of the screen. We want it halfway between those two points, so what we want to do is subtract half of its width. We come up with a simple formula: (screen width / 2) - (image width /2) = centered image Now we translate into Ruby: @mysprite.x = 320 - (@mysprite.bitmap.width / 2) So our sprite is now centered in the x dimension. We can do the same thing for y, but remember that the screen is 480 pixels tall. Half of 480 is 240, so: @mysprite.y = 240 - (@mysprite.bitmap.height / 2) and our script becomes: RUBY def make_me_a_picture_dude @mysprite = Sprite.new @mysprite.bitmap = Bitmap.new("Graphics/Icons/001-Weapon01") @mysprite.x = 320 - (@mysprite.bitmap.width / 2) @mysprite.y = 240 - (@mysprite.bitmap.height / 2) end Go ahead and run it. Sure enough the sword is in the middle of the screen! The great thing about this method is that it will center any sprite, not just the one we're using right now. In fact, let's change the image to something bigger. RUBY def make_me_a_picture_dude @mysprite = Sprite.new @mysprite.bitmap = Bitmap.new("Graphics/Battlers/008-Fighter08") @mysprite.x = 320 - (@mysprite.bitmap.width / 2) @mysprite.y = 240 - (@mysprite.bitmap.height / 2) end Great, now there's a crazy woman with a sword messing everything up. I hate it when that happens. Anyways, let's talk about z. When a sprite has a higher z value you can imagine it as being 'closer' to you. If you draw a sprite with a z value of 0 and then draw another sprite in the same spot with a z of 0 the second one will be drawn on top of the first one because it's more recent. Because of this our crazy sword-woman will disappear if we go to the menu. She's still there. It's just that she's being covered up by the more recent sprites (the windows in the menu) that are being drawn. Even if we come back to the map from the menu we can't see her because the map tiles are more recent now. If you set the .z property of the sprite to 1 she'll stay on top of the tiles even if they're newer. Go ahead and try it. The menu windows still get drawn on top of her, though. That's because Window_Base (the class that all the other windows inherit from) sets its own .z value to 100. Try setting her .z to 101. Now she's on top of the windows but underneath the text. Window text is drawn with a z value that's 2 greater than the window. So if we set her .z to 103 she'll be on top of the text as well. Right. Enough playing around with imaginary women. Let's try drawing a map character. Switch your bitmap file to "Graphics/Characters/152-Animal02" and go ahead and take out the line you used to change the .z so it doesn't drive you nuts. Aaagh! Cats everywhere! What happened? The "Graphics/Characters" folder contains images called 'spritesheets'. They have multiple pictures in each image. That way only one image needs to be loaded for an event, but the event can still be animated and face different directions. So how can we get just one of those pictures? Well, as you can see, the spritesheet is made up of 16 images. It's 4 images wide and 4 images tall. Each one of these images is equal in width and height. That means that each image is 1/4 the height of the bitmap and 1/4 the width of the bitmap. Now how are they arranged in there? Well, each row deals with animating the sprite while it's facing a specific direction. If you picked the first row and showed the images one after another it would look like the cat was walking. The other rows are the same, but the cat is facing in different directions. There's an easy way to remember the order of the directions here. They're actually in the order of the numbers on your numeric keypad. 2 => down 4 => left 6 => right 8 => up So they're in the order down, left, right, up. This is to make the math easier in the process that controls drawing the player on the screen, but you can figure that out later. The Sprite class has a property called .src_rect. That stands for 'source rect'. A 'rect' in graphics is just a set of 4 numbers describing a rectangle. The 4 numbers are [x, y, width, height]. They're almost always in that order too. RMXP actually has a class called 'Rect' that holds these values for us, and .src_rect is an instance of that class. The source rect describes a rectange inside the bitmap that we want to display. Nothing outside of the source rect gets drawn. So let's do the easiest thing and just get our cat to stand still and face downwards. The cat standing still is the first column and the cat facing downwards is the first row. Remember that in graphics everything usually starts from the upper-left corner. We'll let the x and y of our source rect be 0 then. We know the width and height of one cat is 1/4 of the width and height of the image, so we'll use that to determine our source rect using Ruby: RUBY def make_me_a_picture_dude @mysprite = Sprite.new @mysprite.bitmap = Bitmap.new("Graphics/Characters/152-Animal02") src_width = @mysprite.bitmap.width / 4 src_height = @mysprite.bitmap.height / 4 @mysprite.src_rect = Rect.new(0, 0, src_width, src_height) @mysprite.x = 320 - (@mysprite.bitmap.width / 2) @mysprite.y = 240 - (@mysprite.bitmap.height / 2) end As you can see, the cat is not centered. That's because the image is being centered according to the dimensions of the bitmap rather than the those of the source rect. You can correct this by either adjusting the math or by changing the width/height being referred to (from @mysprite.bitmap.width to @mysrpite.src_rect.width, for instance). *yawn* Okay. It's 5:20 in the morning now and I'm getting tired. That's all for this lesson. I'll come back later and add more.
|
|
|
|
||
Khatharr RGSS Graphics for Noobs - Part 1 Aug 10 2008, 08:07 PM
Blackspike tyvm.
nice and easy understandable tutorial. Aug 11 2008, 12:23 PM
Khatharr Thank you. Hope it helps you to become a great sc... Aug 12 2008, 11:06 PM
alwayzconfuzed Wow, amazingly easy to understand, yet, so very he... Aug 14 2008, 04:13 AM
Khatharr I've lived in interesting times. XD Aug 15 2008, 10:38 PM
AlchemistLT O thx you I started learning ruby from your tut th... Nov 5 2008, 01:31 PM
LunC I really love the way you break things down. It w... Nov 15 2008, 06:50 AM
dannyboyk2 Thank you for this tutorial, Khat. I enjoyed your... Dec 3 2008, 05:51 AM![]() ![]() |
The Staff Team |
Lo-Fi Version | Time is now: 13th May 2022 - 08:55 PM |