• Do not use Discord to host any images you post, these links expire quickly! You can learn how to add images to your posts here.
Resource icon
This resource pertains to version 21.1 of Pokémon Essentials.
Pokémon Essentials Version
v21.1 ✅
Also compatible with
  1. v21
Hello, and welcome to my first Tutorial!

So, lots of devs are not very confident about making new screens/interfaces, and that is understandable, as it may be overwhelming at first, but once you get the hang of it, it isn't that bad!

We are going to make our own custom starter selection screen, using features already in Essentials v21!

Chapter 1. Interface Classes
Every Interface is made up of at least one class. Some of them, like the Party and Pokedex Screen, are made up of multiple.

NOTE: It can be confusing sometimes in Essentials to tell the difference between SCENE and SCREEN. In this Tutorial, we won't be covering that, but usually SCENE refers to the graphical aspects of the interface, whereas SCREEN refers to the actions.

In this interface, we will be using ONE class

For this tutorial, the assumptions are that:
- You know a very basic amount about scripting in Essentials
- You know what classes are in Ruby
- You know what variables are in Ruby
- You are using v21 of Essentials, though many of this tutorial also applies to older versions

For our example interface, we will name our class: Starter_Selection
NOTE: Before adding any new interfaces to Essentials, always check that there is not one of the same name in there already, otherwise this will cause confusion

Figure 1:
NOTE: Normally, you would not do it precisely like this, but to understand where all Chapters are linked, it has been done in this format
The final script in full will be at the end!
Code:
Expand Collapse Copy
class Starter_Selection
  def initialize
    add_viewport # Chapter 2
    add_sprites_basic # Chapter 3
    add_overlay # Chapter 4
    draw_screen # Chapter 5
    add_title_text # Chapter 6
    add_other_sprites # Chapter 7
    main_loop # Chapter 8
  end
end

Chapter 2. The Screen and Viewports
Figure 2:
Code:
Expand Collapse Copy
class Starter_Selection
  def add_viewport
    # Section 2.1
    @viewport = Viewport.new(0, 0, Graphics.width, Graphics.height)
    # Section 2.2
    @viewport.z = 99_999
  end
end

Viewport refers to the viewport. Think of this like a window that allows you to see the interface through.
EVERY viewport has four arguments: X, Y, WIDTH, HEIGHT
Leaving the line from Figure 2 as it is allows for the interface to be covering the entire game screen
With every single interface object, X refers to the amount of pixels RIGHT OF the TOP LEFT of the screen, a bit like co-ordinates in Maths
With every single interface object, Y refers to the amount of pixels DOWN FROM the TOP LEFT of the screen
WIDTH and HEIGHT are always in pixels. The standard for Essentials is a width of 512 and a height of 384

2.1. Creating Viewports
Create a viewport with:
Code:
Expand Collapse Copy
Viewport.new(X, Y, WIDTH, HEIGHT)
Once a viewport is created, you cannot easily change the dimensions, but you can create more than one, or remove them and replace them

2.2. Viewport Information
Just two more important things about viewports before we move on:
1) All viewports MUST be disposed when they have finished their use. This is done when closing the interface, which we will cover later
2) It is very helpful to assign a z[\i] value to a viewport straight after creating it. If not, this may cause the screen not to be seen!

Chapter 3: Sprites (Backgrounds)
Figure 3:
Code:
Expand Collapse Copy
class Starter_Selection
  def add_sprites_basic
    # Section 3.1
    @sprites = {}
    # Section 3.2
    bg_exists = pbResolveBitmap("Graphics/UI/Trainer Card/bg_f")
    # Section 3.3
    if bg_exists
      addBackgroundPlane(@sprites, "bg", "Trainer Card/bg_f", @viewport)
    end
    # Section 3.4
    color = Color.new(0, 0, 0, 128)
    # Section 3.5
    @sprites["bg"] = ColoredPlane.new(color, @viewport)
  end
end

All interfaces are built up of sprites[\i]. They are like the building blocks of interfaces.
For this Tutorial, we will cover some basic types of sprites, but there are many, and you can even make your own!

3.1. The Sprite Hash
First, you must tell the interface where to store the sprites, this is done in the first Line of Figure 3
In base Essentials, this is done in a hash, with strings as the keys, and we will stick with that for our interface

Every single sprite is linked to a Viewport
- This is usually done when the sprite is created
- Failing to do this will result in the sprite not appearing, or an error
In almost every case, sprites are linked to the @sprites Hash
- This is done on or after the sprite is created
- Failing to do this will result in the sprite staying on the screen and not updating

3.2. pbResolveBitmap
A helpful but not essential method used to work out if the image presented exists in the game's Graphics folder
NOTE: Anything Graphical in the interface should be somewhere in the Graphics/ folder, and sometimes this is compulsary
Organising your interface's folder is very helpful, and saves time checking
You DO NOT need to define the file extension!

The lines from Figure 3 are taken from the Trainer Card interface. We will put these in the Tutorial for the sake of learning, but it will not be present in the actual interface when it runs!

pbResolveBitmap simply checks if the image exists as presented on the path, and if not, returns false with no errors
It is helpful if you wish to avoid crashes and have a decent failsafe

3.3. Background Planes
Background Planes are useful for presenting a single background. I personally don't tend to use them, but they may be useful
If the background image is too small, the game will "tile" it
If the background image is too large, the game will crop it based on the size of the viewport

The first argument refers to the @sprites Hash from Section 3.1
The second argument is the key that the Hash uses to refer to the sprite
- You can call it what you like
- I strongly encourage you to call it something that makes sense, to avoid confusion later!
- No two sprites may share the same key
The third argument refers to the path that the image is taken from
- In this case, you don't need to put "Graphics/" at the beginning
- However, normally you do need to put it
- Putting the wrong path, or an image that doesn't exists results in the image not appearing, or an error
- The fourth argument refers to the viewport we defined earlier
- Failure to do so will result in an argument error, or the sprite not appearing

3.4. Colors
Color (Or colour, as it is actually spelt!) is an important class used in interfaces. It allows you to... colour things, and is used commonly in text

The first argument is the RED (.red)
- It is ALWAYS an Integer between 0 and 255
- It dictates how "red" the colour is
The second argument is the GREEN (.green)
- It is ALWAYS an Integer between 0 and 255
- It dictates how "green" the colour is (But more like lime green, really)
The third argument is the BLUE (.blue)
- It is ALWAYS an Integer between 0 and 255
- It dictates how "blue" the colour is (But more like cyan, really)
The fourth argument is the OPACITY or ALPHA (.alpha)
- It is an OPTIONAL argument (Default is 255)
- It is ALWAYS an Integer between 0 and 255
- It dictates how opaque the colour is

NOTE: Color.black and Color.white are quite useful if you want a basic black or white and don't want to mess about with brackets/numbers etc.

It is usually good to define your colors at the start of the interface, or it can be easy to lose track

3.5. ColoredPlane
Now that we have established what Color does, we can start using them!
ColoredPlanes are very similar to Background Planes (Section 3.3), except they do not require a path in the Graphics folder, which is useful for this tutorial!
They are quite useful for beginners, but I personally don't use them

NOTE: To see how any sprites work, have a look at them in the All Scripts Search of the Scripts Section in Essentials. You don't have to understand every line straight away, but it is useful to look at the arguments in initialize

The line given in Figure 3 shows how to add a ColoredPlane, and this line is more or less how you add sprites in general when making interfaces

The left hand side is where you tell the interface to add the sprite to the @sprites hash, and where you define the key. These follow the same rules as stated in Section 3.3, under the second argument
Almost all sprites are created with CLASS_NAME.new(ARGUMENTS), as shown in the line
NOTE: It is important not to define a sprite if the name is already taken up in the hash, unless you dispose it first.. This is covered later

With ColoredPlane specifically:
The first argument is the COLOR (Color class)
- It is ALWAYS a Color object
- It can be predefined (as in the example), or defined there and then
The second argument is the viewport as we defined earlier
- Failing to do so will likely result in an argument error

It is not worth defining a .z for backgrounds, as they are almost always "below" every other sprite!
NOTE: Have a look at addBackgroundOrColoredPlane, which is a useful tool as well, and helps you understand everything we've explained a bit more!

Chapter 4: The Overlay
Figure 4:
Code:
Expand Collapse Copy
class Starter_Selection
  def add_overlay
    # Section 4.1
    @sprites["overlay"] = BitmapSprite.new(Graphics.width, Graphics.height, @viewport)
    # Section 4.2
    pbSetSystemFont(@sprites["overlay"].bitmap)
    # Section 4.3
    @sprites["overlay"].z = @sprites["bg"].z + 1
    # Section 4.4
    @sprites["title"] = BitmapSprite.new(Graphics.width, Graphics.height / 4, @viewport)
    pbSetSystemFont(@sprites["title"].bitmap)
    @sprites["title"].z = @sprites["bg"].z + 1
    @sprites["title"].bitmap.font.size = 48
  end
end

The overlay is best described as like a canvas to draw text on. It can also carry images, but this will not be covered in this tutorial
It is in the form of a BitmapSprite, which is an extremely useful sprite, though may be confusing to try and learn about all at once
If you wish to have text on your interface, it is useful to define the overlay first

4.1. BitmapSprite

The first argument is the WIDTH in pixels
The second argument is the HEIGHT in pixels
The third argument is the viewport as we covered in Chapter 2

NOTE: You can move the overlay if you wish, by using .x and .y after defining it, though normally it is easier to position the objects within the overlay rather than the overlay itself. This will be covered later
NOTE: As with all sprites, you can call it what you like, but it is best to call it "overlay" so future you and also others can understand what it is quickly!


4.2. pbSetSystemFont
pbSetSystemFont is a method in Essentials that allows the text on the overlay to always be the standard game font. This stops you having to tell the overlay to do that all the time
Make it a habit to add this line every single time you define an overlay!
By default, this sets the text font size to be the default one
Failure to add this line will result in the text of the overlay to be displayed in the default computer font, and may look weird and different to how it was intended to look

4.3. Defining Zs
In this specific example, you can probably get away with not using this line, but it is important to define the Z argument of every sprite
- The bigger the Z is, the closer it is to the front of the screen, or the person viewing the screen if you prefer to think of it that way
- If a Z is not defined, it is the same as the viewport it is linked to
- If multiple sprites have the same Z, the one defined the most recently will appear to have a higher Z
- It is best to define the Z of a sprite straight after it is created
- If you can't see a sprite, and an error doesn't show up, this is likely the cause of the problem
- A good method is to set the Z relative to a sprite that you definitely want the new sprite to be seen above. The example line does this

4.4. Titles
This is completely optional, but for the sake of this interface, we will add one
This is just another overlay, but we will give it a larger font size
Make sure you change the font size or style AFTER pbSetSystemFont, or else it will reset to default

We have not yet added any text, but rather have set up the canvases. This is best to do so right at the beginning of the interface to get it out of the way

Chapter 5: The Main Screen
Figure 5:
Code:
Expand Collapse Copy
HOENN_STARTERS = [
  :TREECKO, :TORCHIC, :MUDKIP
]
class Starter_Selection
  def draw_screen
    # Section 5.1
    @path = "Graphics/Pokemon/Front/"
    @path_icons = "Graphics/Pokemon/Icons/"
    @p_main_y = 48
    @icons_on_row = 3 # Amount of Pokemon Icons on one horizontal line
    @icon_first_y = @p_main_y + 200 # Y position of top row of Pokemon Icons
    @icon_gap_y = 64 # Space Between each Pokemon Icon (vertical)
    # Section 5.2
    pool = [
      :BULBASAUR, :CHARMANDER, :SQUIRTLE
    ]
    pool += HOENN_STARTERS
    @species = []
    for p in pool
      next unless GameData::Species.exists?(p)
      next unless pbResolveBitmap("#{@path}#{p}")
      next unless pbResolveBitmap("#{@path_icons}#{p}")
      @species.push(p)
    end
    # Section 5.3
    if @species.empty?
      raise "Oopsie! You either haven't defined the starters in the Pokemon PBS file, or do not have any pictures of them in the #{@path} and #{@path_icons} folders!"
    end
    # First, we draw the icons
    # Section 5.4
    @species.each_with_index do |species, i|
      t_x = i % @icons_on_row # The "X co-ord" of that pokemon icon
      t_y = (i / @icons_on_row).floor # The "Y co-ord" of that pokemon icon
      # Section 5.5
      s = "icon_#{i}"
      # Section 5.6
      @sprites[s] = IconSprite.new(0, 0, @viewport)
      # Section 5.7
      @sprites[s].setBitmap(@path_icons + species.to_s)
      # Section 5.8
      @sprites[s].src_rect.width = @sprites[s].src_rect.width / 2
      # Section 5.9
      @sprites[s].x = (Graphics.width / (@icons_on_row + 1)).floor
      @sprites[s].x *= (t_x + 1)
      @sprites[s].x -= @sprites[s].width / 2
      @sprites[s].y = @icon_first_y + t_y * @icon_gap_y
      @sprites[s].z = @sprites["bg"].z + 2
    end
    # Section 5.10
    @index = rand(@species.length)
    # Then, we draw the big pokemon icon
    update_main_icon
  end
 
  def update_main_icon
    species = @species[@index]
    s = "poke"
    # Section 5.11
    @sprites[s].dispose if @sprites[s]
    @sprites[s] = IconSprite.new(0, @p_main_y, @viewport)
    @sprites[s].setBitmap("#{@path}#{species}")
    @sprites[s].x = Graphics.width / 2 - @sprites[s].width / 2
  end
end
First, we have to set up the sprites on our example interface

5.1. Defining everything
Though optional, it is good convention to define the dimensions before starting with creating the sprites
It is best to use class variables, though if you are not planning on using the variable again in another method, local variables can work too
It is strongly recommended to never set a global variable in an interface, though it is common to get one eg. [imath]player or[/imath]PokemonGlobal
In our example, we have defined the following:
- @path. This is the folder directory relative to the Game Folder that contains the big Pokemon sprite we will be using when the cursor is hovering over the current starter selection
- @path_icons. This is another folder directory relative to the Game Folder. This contains the Pokemon icon sprites we will be using to show all available starter Pokemon to pick visually
# NOTE: You can also use PokemonIconSprite for this, but for this tutorial, we will be using the still sprites
- Everything else defined is explained briefly in the example script
- What we are basically doing is telling the script where the X and Y positions of the sprites we are going to create are going to be
- Though optional, I recommend labelling everything in detail until you have a grip of your scripting style
- If you're working in a group, add labels and comments like they are going out of fashion!

5.2. Establishing Sprites
In our interface, we are going to use the Kanto and Hoenn starters
We have defined the Kanto ones there and then, and showed how to add the Hoenn ones using a global constant external to the entire class. There is no solid rule about where to get this information from
In our interface, we have made sure that the developer has defined those starters in the PBS file and that there is an image to later display when we create the sprites later. If not, they are skipped
How you do this sort of thing will change depending on how your interface works, but there is usually some sort of data gathering going on
It is recommended you save the data gathered here in a class variable to avoid making the same calculations later. We have done this in @species

5.3. Adding Failsafes
As with any scripting, adding failsaves are very useful in making your own interface for checking/testing later
The one here ensures that there is at least one starter to pick from!
If we had not added this, there would be an error that would pop up that would not be as clear to the developer or player as this one

5.4. each_with_index Method
This is not directly related to interfaces, but is an EXTREMELY useful method that I don't think enough people know about and I recommend you learn by heart! This saves quite a few lines and is simple once you know it. It works as follows:

ARRAY.each_with_index do |ELEMENT, INDEX|
  • ARRAY is the array that you are iterating
  • ELEMENT is the local variable representing the element of that array being iterated
  • INDEX is the local variable representing the index of the element, an Integer starting at 0 and going up by one each time. It is recommended to have i[\i] as the variable for this
This method is the equivalent of:
Code:
Expand Collapse Copy
for i in 0...ARRAY.length
  ELEMENT = ARRAY[i]
  # Iterating code here!
end
But with less lines, and chances to make mistakes!

5.5. Defining the sprite key
Completely optional, but gives less chance to make mistakes, and simplifies the code when looked at as a whole by reducing the amount of # { " and so on!
What we are doing here is defining the key for the sprite we are about to create
NOTE: The #{i} changes the Integer i into its String form. It is not important to know about this in detail during this tutorial, so long as you get the format correct!

5.6. Icon Sprites
Icon Sprites are an Essential developer's best friend! They are used very commonly, and a lot can be done with them

The first argument is the initial X value of the sprite in pixels
- This is relative to the X value of the viewport it is linked to
- This can be changed later (Section 5.9.)
The second argument is the initial Y value of the sprite in pixels
- This is relative to the Y value of the viewport it is linked to
- This can be changed later (Section 5.9.)
The third argument is the viewport that the sprite is linked to. We defined the one in the example in Chapter 2
And on the left hand side of the line, we added the sprite to the @sprites hash, as outlined in Section 3.5

5.7. Setting Icon Sprite Bitmap
So, we have created our Icon Sprite, and added it to the hash, but at the minute, it is just a blank image, so now we have to add a picture to it
This is done with the setBitmap method
The argument given is simply the path of where the image is
It is recommended to do this before moving or modifying the sprite

5.8 Cropping an Icon Sprite with src_rect
src_rect is a property of a lot of images in Ruby and Essentials. You don't need to know everything about it, but it is useful in some cases

I presume src_rect is short for "source rectangle"

Setting the source rectangle bascially crops the image shown in the Icon Sprite. It also changes the size of the Icon Sprite itself
So, in our example, we are taking the Pokemon Icon Sprite, which by default is two images/frames, and cropping the left frame of the icon
The src_rect has, as with an Icon Sprite, the following properties:
- X value (.x) This is the X position in pixels of the original image that is the top left pixel of the cropped image
- Y value (.y) This is the Y position in pixels of the original image that is the top left pixel of the cropped image
- WIDTH value (.width) This is the width in pixels of the cropped image
- By default, this is the height of the original image
- With src_rect, this can be set directly, but with Icon Sprite, it cannot
- HEIGHT value (.height) This is the height in pixels of the cropped image
- By default, this is the height of the original image
- With src_rect, this can be set directly, but with Icon Sprite, it cannot

NOTE: You can set the src_rect as a whole by doing:
Code:
Expand Collapse Copy
src_rect.set(X, Y, WIDTH, HEIGHT)
This saves lines and is optional, though all four arguments must be defined

In our example, we set only the width of the sec_rect, as it was not necessary to change the X, Y or HEIGHT property
It is also demonstrated how the src_rect width can be both defined and called like a variable
NOTE: src_rect can be used to change the appearance of an Icon Sprite without changing the image itself. It is used frequently in Essentials!

5.9. Positioning an Icon Sprite
If you do not set the position of an Icon Sprite when creating it, you can set it later with (.x =) and (.y = )
NOTE: You can gather the height and width of an Icon Sprite with .width and .height, but cannot set it like that!
Remember, the position is set relative to the position of the viewport you linked it to when creating it, though in our example those are (0, 0), so it will be as it appears on the screen

We have also defined the Z of the sprites, as discussed in Section 4.3.

5.10. Setting the index
In some interfaces, some sort of selection is usually made
The interface needs to know what is currently being selected
@index allows you to do this
- It can be any class variable
- Sometimes more than one index is used
- It usually takes the form of an Integer starting at 0, though does not have to be

To avoid preferential treatment, in our interface, we will be defining the default index as a random one of the starters that can be chosen
Usually, the default index is 0 to avoid crashing
NOTE: You can get the game to "remember" the default index of an interface by storing it as a global variable when closing the interface!

5.11. Disposing a sprite
In our interface, we have added a new method to draw the big sprite. This is because it is done more than once and we want to save writing the lines of code out again
As it is done more than once, the sprite is created again with the same key
When doing this, you MUST dispose the original sprite first!
This is very important!
If you see a sprite not move, or linger after closing the interface, this is because it has not been disposed properly
Every time you re-use a sprite in the same variable, ensure it is disposed just before creating it
However, to ensure no crashing, you have to ensure that the sprite exists first by using the right hand part of the line!
Anyway, if you put in the line in the example as written, you should be fine!

For the rest of the method, we draw the large sprite as normal. Everything here has been covered earlier

Chapter 6: Adding Text
Figure 6:
Code:
Expand Collapse Copy
class Starter_Selection
  def add_title_text
    do_text_pos = true
    # Section 6.1
    title = _INTL("Choose your starter!")
    overlay = @sprites["title"].bitmap
    title_x = Graphics.width / 2
    title_y = 8
    @baseColor = Color.new(224, 224, 224)
    @shadowColor = Color.new(48, 48, 48)
    if do_text_pos
      # Section 6.2
      text_pos = [
        [title, title_x, title_y, :center, @baseColor, @shadowColor]
      ]
      # Section 6.3
      pbDrawTextPositions(overlay, text_pos)
    else
      # Section 6.4
      drawTextEx(overlay, 0, title_y, Graphics.width, 1, title, @baseColor, @shadowColor)
    end
    change_pkmn_text
  end
 
  def change_pkmn_text
    species = @species[@index]
    data = GameData::Species.get(species)
    text = data.name
    type = data.types[0]
    text_type = GameData::Type.get(type).name
    text_type = _INTL("{1} type", text_type)
    overlay = @sprites["overlay"].bitmap
    # Section 6.5
    overlay.clear
    t_x = Graphics.width / 2
    t_y = @sprites["poke"].y + @sprites["poke"].height - 32
    text_pos = [
      [text, t_x, t_y, :center, @baseColor, @shadowColor],
      [text_type, t_x, t_y + 32, :center, @baseColor, @shadowColor]
    ]
    pbDrawTextPositions(overlay, text_pos)
  end
end

6.1. Defining the text
There are two main ways of adding text to an interface in Essentials
But before we cover either of those, you should prepare:
- What text is going to be drawn
- The base and shadow colour. Both of these are Color classes, as covered in Section 3.4.
- NOTE: In our example, we have chosen some light text, but these can be set to whatever you want. It is recommened that they contrast somewhat with each other and the background
- By Storing the bitmap property of the overlay in a local variable. This saves you from putting the full line every time you refer to it

6.2. Setting pbDrawTextPositions
This is a way of adding text
It is recommened where:
- You DO NOT text that is likely to be multiple lines long
- You want to right or centre align the text
- You want to be more specific with the text positions
- You have lots of little text eg. Summary

The text positions are a large array, consisting of multiple smaller arrays
The elements are as follows:
- The first element is the text, always a String
- The second element is the X position of the text within the overlay
- The third element is the Y position of the text within the overlay
- The fourth element is the alignment of the text
- Left align is written as :left or 0
- Right align is written as :right or 1
- Centre align is written as :center or 2
- The fifth element is the base colour of the text, as explained in Section 6.1
- The sixth element is the shadow colour of the text, as explained in Section 6.1
- The seventh element is optional
- :outline sets the text as outline text, with the shadow colour as the outline colour
- :none sets the text as plain text, with no shadow colour

6.3. Drawing pbDrawTextPositions
If you have assigned the text positions correctly, and referred to the overlay properly, the line as shown in the example is the onlu line required to add the text as dictated in the text positions array to the overlay

6.4. drawTextEx
We will cover this in the tutorial, but in our example interface, by default, the title text will be drawn in the other format
This is a way of adding text
It is recommened where:
- You DO have text that is likely to be multiple lines long
- The text is left aligned
- The text has to fit within a certain space
- You DO NOT want the text to be outlined by default
- You have a long string of text eg. Pokedex Description

The arguments of drawTextEx are as follows:
- The first argument is the overlay that the text is linked to
- The second argument is the X position of the text within the overlay
- The third argument is the Y position of the text within the overlay
- The fourth argument is the maximum width, in pixels, of the space where the text will be
- The fifth argument is the maximum amount of lines that the block of text will be
- Each line is about 32 pixels high by default
- The sixth argument is the text, always a String
- The seventh argument is the base colour of the text, as explained in Section 6.1
- The eighth argument is the shadow colour of the text, as explained in Section 6.1

6.5. Clearing the Overlay
When redrawing text or images on the same overlay, it is important to clear the overlay, in order to ensure that the previous text is not also drawn on the overlay as well as the new one

In our interface, we define a new method for drawing the Pokemon name, as we will end up reusing it every time a different starter is hovered over
In this method, we draw the starter name just under the picture of the Pokémon

Chapter 7: Adding Other Sprites
Figure 7:
Code:
Expand Collapse Copy
class Starter_Selection
  def add_other_sprites
    # Section 7.1
    @bg_colours = [
      Color.new(61, 102, 61), # Grass
      Color.new(127, 69, 63), # Fire
      Color.new(63, 92, 127)  # Water
    ]
    # Section 7.2
    @sprites["arrow"] = AnimatedSprite.create("Graphics/UI/pause_arrow", 4, 3, @viewport)
    @sprites["arrow"].start
    @sprites["arrow"].z = @sprites["bg"].z + 3
  end
end

7.1. Defining for later
In our interface, we are planning on changing the background depending on the type of the Pokemon picked (provided they were all defined earlier, otherwise it will be wrong!)
As the colours are one of three constants, we are assigning all the colours up front here, so that it is easier to read the more complex text later
Working out what an interface will look like before writing the script saves a lot of time. Both a drawn design and an idea for what it will look like mentally are very helpful

7.2. Adding An AnimatedSprite
The AnimatedSprite class is an Essentials sprite useful for smaller animated sprites like arrows, which move using a single image
The difference between this sprite and most other sprites is that it uses (.create) rather than the usual (.new)

The arguments of an AnimatedSprite are as follows:
- The first argument is the Graphics path of where the image is from
- The image contains at least one frame, which is a smaller image
- The frames must be done from left to right, in a single row
- All frames must be the same width
- NOTE: The arrows in the main UI Graphics section are a good example of how to do the image of an AnimatedSprite
- The second argument is the FRAMECOUNT, which is the amount of frames, written as an integer
- This is basically the graphic width in pixels, divided by a frame width in pixels
- The third argument is the FRAMESKIP, and is written as an integer bigger than one
- It is in 1/20ths of a second
- It dictates how often the frames change
- If it is lower than 1, it is treated as 1
- The fourth argument is the viewport that the sprite is linked to. We defined the one in the example in Chapter 2

An AnimatedSprite has the following methods:
  • (.start) or (.play) plays the animation
  • (.stop) stops the animation
  • (.visible = TRUE_OR_FALSE) hides or unhides the sprite
  • (.frame = Integer) assigns the frame the animation is playing

NOTE: The AnimatedSprite uses a simplified version of the src_rect method we covered in Section 5.8.

Chapter 8: The Loop
Figure 8:
Code:
Expand Collapse Copy
class Starter_Selection
  def main_loop
    refresh
    # Section 8.1
    pbFadeInAndShow(@sprites) { pbUpdate }
    loop do
      # Section 8.2
      Graphics.update
      Input.update
      pbUpdate
      # Section 8.3
      old_index = @index
      # Section 8.4
      if Input.trigger?(Input::USE)
        name = GameData::Species.get(@species[@index]).name
        if pbConfirmMessageSerious(_INTL("Are you sure that you wish to pick {1}?", name))
          # Section 8.5
          pbPlayDecisionSE
          # Section 8.6
          break
        else
          pbPlayCancelSE
        end
      # Section 8.7
      elsif Input.trigger?(Input::BACK)
        pbPlayBuzzerSE
      # Section 8.8
      elsif Input.trigger?(Input::LEFT)
        @index -= 1 unless @index % @icons_on_row == 0
      elsif Input.trigger?(Input::RIGHT)
        @index += 1 unless @index % @icons_on_row == @icons_on_row - 1
      elsif Input.trigger?(Input::UP)
        @index -= @icons_on_row unless @index < @icons_on_row
      elsif Input.trigger?(Input::DOWN)
        last_row = ((@species.length - 1) / @icons_on_row).floor * @icons_on_row
        @index += @icons_on_row if @index < last_row
      end
      # Section 8.9
      @index = @index.clamp(0, @species.length - 1)
      # Section 8.3
      if old_index != @index
        pbPlayCursorSE
        # Section 8.10
        refresh
      end
    end
    # Section 8.13
    pbFadeOutAndHide(@sprites)
    # Section 8.14
    pbDisposeSpriteHash(@sprites)
    @viewport.dispose
  end
 
  def pbUpdate
    # Section 8.2
    pbUpdateSpriteHash(@sprites)
  end
 
  def refresh
    update_main_icon
    change_pkmn_text
    # Moves Arrow
    # Section 8.11
    @sprites["arrow"].x = @sprites["icon_#{@index}"].x
    @sprites["arrow"].x += @sprites["icon_#{@index}"].width / 2
    @sprites["arrow"].x -= @sprites["arrow"].width / 2
    @sprites["arrow"].y = @sprites["icon_#{@index}"].y
    # Changes Background
    # Section 8.12
    c = @bg_colours[@index % @bg_colours.length]
    @sprites["bg"].set_plane_color(c)
  end
end
8.1. Fading the Sprites in
Though optional, the method pbFadeInAndShow, allows the interface to look smooth
The block between the {} is optional, but the @sprites argument is not
This method fades all the sprites in the sprite hash in to the interface and is the first thing the user sees

8.2. Updating the Loop and the Sprite Hash
One way to think of sprites in an interface is as machines that need constant energy going into them in order to keep staying active on the interface
In order to "fuel" them, you need to keep asking the game to update them by updating a number of things every time the loop runs
- Input.update tells the game that the user has or has not typed something on the keyboard or used the mouse
- Graphics.update tells the game to make the graphics look as they are supposed to look
- You can mess about with the graphics as much as want between the game calling these. The game will not show it visually until the next time it is called
- pbUpdate is used in most Essentials interfaces, and involves updating all the active sprites in the interface
- For most interfaces, this only tends to involve pbUpdateSpriteHash
- pbUpdateSpriteHash(@sprites) automatically updates every single sprite that we spent all that time defining in the @sprites
- This is probably the most important line in the interface. Without it running, it would appear to collapse instantly!

8.3. Setting the old index
When the user makes an input which changes the interface, it needs to be known that a change was made
This is done by getting the interface to "remember" what the index was before any chances the interface has to change the index
Then, later on, after all the chances are done, it checks the old index with the new index
If and only if the two variables are different, the script asks for the interface to change graphically
If this was not done, everything would update every frame, which especially in older versions of Essentials, and in more intense interfaces, would make everything run slower

8.4. Checking the Input Trigger
This line basically checks to see if the user has done something to the keyboard or the mouse, and then runs the code within if they have
The argument is an integer, though almost always in the form of a constant within the Input module. Ensure you put the Input:: beforehand!
Common constants used are:
- Input::USE
- The Enter or Space button by default
- Usually the A button in official games
- Input::BACK
- The Esc or X button by default
- Usually the B button in official games
- NOTE: This normally closes the interface, but in ours, as it is a starter selection, you cannot go back, so this is not the case this time
- Input::ACTION
- The Z button by default
- Usually the START button in official games
- Input::SPECIAL
- The F5 button by default
- Usually the SELECT button in official games
- Not used as often
- Input::LEFT, Input::RIGHT, Input::UP, Input::DOWN
- The standard direction buttons by default
- Input::JUMPUP, Input::JUMPDOWN
- PageUp and PageDown by default
- Usually the L and R buttons in official games
- Commonly used to move menus down by multiple rows at once

NOTE: You can add your own Input buttons, but we're not covering that here!

8.5. Adding Sound Effects
What really separates the wheat from the chaff when it comes to interfaces are sound effects. It can really make things seem that more professional
Essentials already has some methods for adding sound effects into interfaces
You just have to add the single line, and it should play
The main sound effect methods are:
- pbPlayCursorSE
- Plays the standard movement sound effect
- pbPlayDecisionSE
- Plays the standard sound effect when a decision has been made#
- pbPlayCancelSE
- Plays the standard sound effect when a decision has been cancelled
- pbPlayBuzzerSE
- Plays the standard sound effect when an input has been made that doesn't do anything or is wrong
- Used to help the user acknowledge that they cannot make that input there
- pbPlayCloseMenuSE
- Plays the standard closing menu sound effect

NOTE: I have added most of these to Figure 8 for a rough idea of where these typically go!
NOTE: You can add your own SE by using pbSEPlay. But you must make sure the sound effect is in the Audio/SE/ folder!


8.6. Breaking the Loop
It is important to break the loop when it is finished
Without breaking the loop, the interface will continue forever!
NOTE: You can use return to break the loop, but the interface would end messily and suddenly, so it is better to use break

8.7. Using elsif for Inputs
It is important to use elsif for inputs, as otherwise, if the user has entered two or more inputs, the interface will recognise both of them
Alternatively, you can have the interface ask if two or more inputs are being pressed, and then go from there!

8.8. Changing the Index
In any interface that is not just a still picture or simple animation, the index will be changed at some point
At this point, you only need to change the value of the index, and we will do the more substantial changes later on

On our interface, we have had it such that moving the arrow keys moves the selection up or down by one, or by the amount of icons on a row

8.9. The clamp Method for Integers
Again, this is not only used in interfaces, but is a really useful method for Integers
It is used commonly in Essentials as a failsafe

INTEGER.clamp(MINIMUM, MAXIMUM)
The first argument is the minumum amount that the integer can be
The second argument is the maximum amount that the integer can be

It is the equivalent of:
Code:
Expand Collapse Copy
[[INTEGER, MINIMUM].max, MAXIMUM].min
The clamp method has been used in our interface to ensure that the index is never out of the range of the amount
If we had not done this, there would have been a chance for the index to be too high or low and the interface would pull up an error when we try and change the screen later

8.10. Refreshing The Interface
You can change the sprites to do what they are supposed to do when interacted with
This usually consists of moving a cursor above a sprite, or changing text somewhere
In our example, we:
- Update the big Pokémon Icon, the method for which we covered in Chapter 5
- Update the text of the Pokémon Species Name, the method for which we covered in Chapter 6
- Move the cursor to on top of the icon sprite of the starter we are currently hovering over. We will elaborate on this in Section 8.11.
- Change the colour of the background if necessary. We will elaborate on this in Section 8.12.

NOTE: As a general rule, UPDATE is done to a sprite every frame, but REFRESH is done to a sprite or an interface every time it is necessary

8.11. Moving Sprites
Though we covered this already in Section 5.9, it is important to note that lots of sprites can be moved in the same way, including an AnimatedSprite like in our example
To avoid spending too much time correcting mistakes, it can be a good idea to move sprites relative to other sprites, like in our example. This saves having to do a load of Maths and remembering a lot of figures!
In our example, we align the midpoint of the cursor with the midpoint of the Pokémon icon

8.12. Changing ColoredPlane Backgrounds
You can change the colour of a ColoredPlane without having to dispose and recreate the sprite with set_plane_color
This saves time and script lines
When dealing with sprites, always try and find the easier methods of performing an action. A lot of these will be covered in Essentials already. Don't do any extra work you don't have to!
NOTE: You can do this with setBitmap for Icon Sprites as well!

8.13. Fading Out The Sprite Hash
pbFadeOutAndHide sets all the sprites in the sprite hash to black, and disposes them out graphically
Though technically optional, your interface will look very messy if you don't include this!
This is basically the opposite of pbFadeInAndShow, which we covered in Section 8.1.

8.14. Disposing Of The Sprite Hash
Upon closing down the interface, everything that can be disposed should be disposed. This usually is:
- The Sprite Hash (Done with pbDisposeSpriteHash)
- The Viewport (Done with a simple @viewport.dispose)
If you do not add this, many of the sprites will be left behind upon closing the interface
This should always be the last visual action you should put in your interface, as you cannot do anything to your sprites once you have done this

Chapter 9: Calling The Interface
Figure 9:
Code:
Expand Collapse Copy
class Starter_Selection
  def result
    # Section 9.1
    return @species[@index]
  end
end

# Section 9.2
def pbSelectStarter
  ret = Starter_Selection.new
  return ret.result
end
9.1. Setting A Result
In some interfaces that return a result, it is worth calling that method after finishing the interface
NOTE: This is also a good place to potentially save the result of the interface, or some of the variables somwhere for next time the interface is called

9.2. Opening The Interface
As the interface is just like any other class, it is called by using (.new) followed by any arguments you put in the initialize method
NOTE: You can call an interface from within an interface. Interfaception! Make sure that the Z of the second interface's viewport is higher than the Z of the first one though!

In our example, we make a method called pbSelectStarter, the result of which returns the Species Symbol of the starter the user picks

Chapter 10: The Example Interface In Full
Okay, so that more or less covers it!
Now, I will write the script of our interface in full so you have a better understanding of what an interface typically looks like
Feel free to use this in your project and mess about with it, but please credit me, Swdfm!
Thanks for reading, and hope this helps!
Figure 10
(External file - too big!)
Credits
Swdfm
Author
Swdfm
Views
1,238
First release
Last update

Ratings

0.00 star(s) 0 ratings

More resources from Swdfm

Back
Top