Creation Asylum Banner
X   Site Message
(Message will auto close in 2 seconds)

Welcome Guest ( Log In | Register )

"Don't judge a book by it's cover -- judge it by the movie."

lucksta
Quick Jump: The AsylumV | EntertainmentV | Game MakingV | Creative ForumV | Technology & ComputersV | TranslationsV

> RGSS Graphics for Noobs - Part 1, Tired of being 'the weakest link'?
Khatharr
post Aug 10 2008, 08:07 PM
Post #1


Look what the Khat dragged in...
Group Icon


Celestial

Member 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.



Want to learn RGSS?
My secret identity


It's come to my attention that people still use scripts I wrote and forgot about a long time ago. If you have a question about a script that I wrote or participated in writing please send me a PM with a link to the script's topic and your question. I don't check the topics, so I won't see your question if you don't notify me. (This does not apply to the script-help forum as I check there often.)
Go to the top of the page
 
Thanks+Quote Post

Posts in this topic


Reply to this topicStart new topic
1 User(s) are reading this topic (1 Guests and 0 Anonymous Users)
0 Members:

 



- The Staff Team Lo-Fi Version Time is now: 13th May 2022 - 08:55 PM